summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhk57.kim <hk57.kim@samsung.com>2015-06-03 15:16:56 +0900
committerhk57.kim <hk57.kim@samsung.com>2015-06-03 15:16:56 +0900
commit4078c98a5d481778482f52d3aaf7a1777ffe6088 (patch)
treec18ae9d21e2b29c349231c5b61d742e9fca9c1d9 /src
parentcce6a0e298fac08c588204b085e7a807fa75813d (diff)
downloadcairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.gz
cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.bz2
cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.zip
Cairo 1.12.14
Change-Id: Ibc39e63896ec42cab29fbbbf615a46f2d58319a8 Signed-off-by: hk57.kim <hk57.kim@samsung.com>
Diffstat (limited to 'src')
-rwxr-xr-xsrc/.gitignore33
-rwxr-xr-xsrc/Makefile.am119
-rwxr-xr-xsrc/Makefile.am.analysis35
-rwxr-xr-xsrc/Makefile.sources521
-rwxr-xr-xsrc/Makefile.win3229
-rwxr-xr-xsrc/Makefile.win32.features736
-rwxr-xr-xsrc/README69
-rwxr-xr-xsrc/cairo-analysis-surface-private.h74
-rwxr-xr-xsrc/cairo-analysis-surface.c938
-rwxr-xr-xsrc/cairo-arc-private.h61
-rwxr-xr-xsrc/cairo-arc.c308
-rwxr-xr-xsrc/cairo-array-private.h90
-rwxr-xr-xsrc/cairo-array.c528
-rwxr-xr-xsrc/cairo-atomic-private.h272
-rwxr-xr-xsrc/cairo-atomic.c106
-rwxr-xr-xsrc/cairo-backend-private.h219
-rwxr-xr-xsrc/cairo-base64-stream.c144
-rwxr-xr-xsrc/cairo-base85-stream.c131
-rwxr-xr-xsrc/cairo-bentley-ottmann-rectangular.c884
-rwxr-xr-xsrc/cairo-bentley-ottmann-rectilinear.c600
-rwxr-xr-xsrc/cairo-bentley-ottmann.c2133
-rwxr-xr-xsrc/cairo-beos-surface.cpp984
-rwxr-xr-xsrc/cairo-beos.h60
-rwxr-xr-xsrc/cairo-botor-scan-converter.c2164
-rwxr-xr-xsrc/cairo-box-inline.h121
-rwxr-xr-xsrc/cairo-boxes-intersect.c690
-rwxr-xr-xsrc/cairo-boxes-private.h123
-rwxr-xr-xsrc/cairo-boxes.c460
-rwxr-xr-xsrc/cairo-cache-private.h145
-rwxr-xr-xsrc/cairo-cache.c338
-rwxr-xr-xsrc/cairo-cff-subset.c3482
-rwxr-xr-xsrc/cairo-clip-boxes.c597
-rwxr-xr-xsrc/cairo-clip-inline.h83
-rwxr-xr-xsrc/cairo-clip-polygon.c156
-rwxr-xr-xsrc/cairo-clip-private.h201
-rwxr-xr-xsrc/cairo-clip-region.c123
-rwxr-xr-xsrc/cairo-clip-surface.c247
-rwxr-xr-xsrc/cairo-clip-tor-scan-converter.c1845
-rwxr-xr-xsrc/cairo-clip.c846
-rwxr-xr-xsrc/cairo-cogl-context-private.h52
-rwxr-xr-xsrc/cairo-cogl-context.c822
-rwxr-xr-xsrc/cairo-cogl-gradient-private.h89
-rwxr-xr-xsrc/cairo-cogl-gradient.c642
-rwxr-xr-xsrc/cairo-cogl-private.h164
-rwxr-xr-xsrc/cairo-cogl-surface.c2805
-rwxr-xr-xsrc/cairo-cogl-utils-private.h54
-rwxr-xr-xsrc/cairo-cogl-utils.c126
-rwxr-xr-xsrc/cairo-cogl.h69
-rwxr-xr-xsrc/cairo-color.c198
-rwxr-xr-xsrc/cairo-combsort-inline.h94
-rwxr-xr-xsrc/cairo-compiler-private.h246
-rwxr-xr-xsrc/cairo-composite-rectangles-private.h203
-rwxr-xr-xsrc/cairo-composite-rectangles.c731
-rwxr-xr-xsrc/cairo-compositor-private.h378
-rwxr-xr-xsrc/cairo-compositor.c391
-rwxr-xr-xsrc/cairo-contour-inline.h80
-rwxr-xr-xsrc/cairo-contour-private.h124
-rwxr-xr-xsrc/cairo-contour.c454
-rwxr-xr-xsrc/cairo-damage-private.h85
-rwxr-xr-xsrc/cairo-damage.c241
-rwxr-xr-xsrc/cairo-debug.c304
-rwxr-xr-xsrc/cairo-default-context-private.h68
-rwxr-xr-xsrc/cairo-default-context.c1552
-rwxr-xr-xsrc/cairo-deflate-stream.c156
-rwxr-xr-xsrc/cairo-deprecated.h123
-rwxr-xr-xsrc/cairo-device-private.h89
-rwxr-xr-xsrc/cairo-device.c557
-rwxr-xr-xsrc/cairo-directfb-surface.c544
-rwxr-xr-xsrc/cairo-directfb.h67
-rwxr-xr-xsrc/cairo-drm.h120
-rwxr-xr-xsrc/cairo-egl-context.c421
-rwxr-xr-xsrc/cairo-error-inline.h52
-rwxr-xr-xsrc/cairo-error-private.h123
-rwxr-xr-xsrc/cairo-error.c73
-rwxr-xr-xsrc/cairo-evas-gl-context.c438
-rwxr-xr-xsrc/cairo-evas-gl.h253
-rwxr-xr-xsrc/cairo-fallback-compositor.c185
-rwxr-xr-xsrc/cairo-features-uninstalled.pc.in7
-rwxr-xr-xsrc/cairo-features.pc.in12
-rwxr-xr-xsrc/cairo-filters-private.h57
-rwxr-xr-xsrc/cairo-filters.c85
-rwxr-xr-xsrc/cairo-fixed-private.h359
-rwxr-xr-xsrc/cairo-fixed-type-private.h75
-rwxr-xr-xsrc/cairo-fixed.c39
-rwxr-xr-xsrc/cairo-font-face-twin-data.c1072
-rwxr-xr-xsrc/cairo-font-face-twin.c758
-rwxr-xr-xsrc/cairo-font-face.c318
-rwxr-xr-xsrc/cairo-font-options.c560
-rwxr-xr-xsrc/cairo-fontconfig-private.h78
-rwxr-xr-xsrc/cairo-freed-pool-private.h139
-rwxr-xr-xsrc/cairo-freed-pool.c93
-rwxr-xr-xsrc/cairo-freelist-private.h139
-rwxr-xr-xsrc/cairo-freelist-type-private.h54
-rwxr-xr-xsrc/cairo-freelist.c191
-rwxr-xr-xsrc/cairo-ft-font.c3558
-rwxr-xr-xsrc/cairo-ft-private.h61
-rwxr-xr-xsrc/cairo-ft.h118
-rwxr-xr-xsrc/cairo-gl-composite.c1605
-rwxr-xr-xsrc/cairo-gl-device.c1092
-rwxr-xr-xsrc/cairo-gl-dispatch-private.h192
-rwxr-xr-xsrc/cairo-gl-dispatch.c296
-rwxr-xr-xsrc/cairo-gl-ext-def-private.h143
-rwxr-xr-xsrc/cairo-gl-filters.c416
-rwxr-xr-xsrc/cairo-gl-glyphs.c628
-rwxr-xr-xsrc/cairo-gl-gradient-private.h102
-rwxr-xr-xsrc/cairo-gl-gradient.c340
-rwxr-xr-xsrc/cairo-gl-hairline-stroke.c412
-rwxr-xr-xsrc/cairo-gl-info.c98
-rwxr-xr-xsrc/cairo-gl-msaa-compositor.c1284
-rwxr-xr-xsrc/cairo-gl-operand.c1500
-rwxr-xr-xsrc/cairo-gl-private.h1237
-rwxr-xr-xsrc/cairo-gl-shaders.c1453
-rwxr-xr-xsrc/cairo-gl-source.c111
-rwxr-xr-xsrc/cairo-gl-spans-compositor.c553
-rwxr-xr-xsrc/cairo-gl-surface-legacy.c603
-rwxr-xr-xsrc/cairo-gl-surface.c2142
-rwxr-xr-xsrc/cairo-gl-traps-compositor.c664
-rwxr-xr-xsrc/cairo-gl.h158
-rwxr-xr-xsrc/cairo-glx-context.c372
-rwxr-xr-xsrc/cairo-gstate-private.h415
-rwxr-xr-xsrc/cairo-gstate.c2501
-rwxr-xr-xsrc/cairo-hash-private.h87
-rwxr-xr-xsrc/cairo-hash.c578
-rwxr-xr-xsrc/cairo-hull.c235
-rwxr-xr-xsrc/cairo-image-compositor.c3277
-rwxr-xr-xsrc/cairo-image-filters-private.h56
-rwxr-xr-xsrc/cairo-image-filters.c315
-rwxr-xr-xsrc/cairo-image-info-private.h63
-rwxr-xr-xsrc/cairo-image-info.c292
-rwxr-xr-xsrc/cairo-image-mask-compositor.c408
-rwxr-xr-xsrc/cairo-image-source.c1331
-rwxr-xr-xsrc/cairo-image-surface-inline.h96
-rwxr-xr-xsrc/cairo-image-surface-private.h238
-rwxr-xr-xsrc/cairo-image-surface.c1671
-rwxr-xr-xsrc/cairo-list-inline.h209
-rwxr-xr-xsrc/cairo-list-private.h48
-rwxr-xr-xsrc/cairo-lzw.c404
-rwxr-xr-xsrc/cairo-malloc-private.h149
-rwxr-xr-xsrc/cairo-mask-compositor.c1498
-rwxr-xr-xsrc/cairo-matrix.c1203
-rwxr-xr-xsrc/cairo-mempool-private.h85
-rwxr-xr-xsrc/cairo-mempool.c368
-rwxr-xr-xsrc/cairo-mesh-pattern-rasterizer.c944
-rwxr-xr-xsrc/cairo-misc.c935
-rwxr-xr-xsrc/cairo-mono-scan-converter.c612
-rwxr-xr-xsrc/cairo-mutex-impl-private.h278
-rwxr-xr-xsrc/cairo-mutex-list-private.h83
-rwxr-xr-xsrc/cairo-mutex-private.h69
-rwxr-xr-xsrc/cairo-mutex-type-private.h194
-rwxr-xr-xsrc/cairo-mutex.c89
-rwxr-xr-xsrc/cairo-no-compositor.c107
-rwxr-xr-xsrc/cairo-observer.c52
-rwxr-xr-xsrc/cairo-os2-private.h67
-rwxr-xr-xsrc/cairo-os2-surface.c1415
-rwxr-xr-xsrc/cairo-os2.h110
-rwxr-xr-xsrc/cairo-output-stream-private.h196
-rwxr-xr-xsrc/cairo-output-stream.c774
-rwxr-xr-xsrc/cairo-paginated-private.h168
-rwxr-xr-xsrc/cairo-paginated-surface-private.h62
-rwxr-xr-xsrc/cairo-paginated-surface.c719
-rwxr-xr-xsrc/cairo-path-bounds.c295
-rwxr-xr-xsrc/cairo-path-fill.c342
-rwxr-xr-xsrc/cairo-path-fixed-private.h206
-rwxr-xr-xsrc/cairo-path-fixed.c1588
-rwxr-xr-xsrc/cairo-path-in-fill.c290
-rwxr-xr-xsrc/cairo-path-private.h57
-rwxr-xr-xsrc/cairo-path-stroke-boxes.c711
-rwxr-xr-xsrc/cairo-path-stroke-polygon.c1377
-rwxr-xr-xsrc/cairo-path-stroke-traps.c1127
-rwxr-xr-xsrc/cairo-path-stroke-tristrip.c1091
-rwxr-xr-xsrc/cairo-path-stroke.c1517
-rwxr-xr-xsrc/cairo-path.c479
-rwxr-xr-xsrc/cairo-pattern-inline.h65
-rwxr-xr-xsrc/cairo-pattern-private.h397
-rwxr-xr-xsrc/cairo-pattern.c5076
-rwxr-xr-xsrc/cairo-pdf-operators-private.h173
-rwxr-xr-xsrc/cairo-pdf-operators.c1557
-rwxr-xr-xsrc/cairo-pdf-shading-private.h100
-rwxr-xr-xsrc/cairo-pdf-shading.c284
-rwxr-xr-xsrc/cairo-pdf-surface-private.h206
-rwxr-xr-xsrc/cairo-pdf-surface.c7367
-rwxr-xr-xsrc/cairo-pdf.h94
-rwxr-xr-xsrc/cairo-pen.c475
-rwxr-xr-xsrc/cairo-png.c819
-rwxr-xr-xsrc/cairo-polygon-intersect.c1532
-rwxr-xr-xsrc/cairo-polygon-reduce.c1438
-rwxr-xr-xsrc/cairo-polygon.c608
-rwxr-xr-xsrc/cairo-private.h64
-rwxr-xr-xsrc/cairo-ps-surface-private.h104
-rwxr-xr-xsrc/cairo-ps-surface.c4660
-rwxr-xr-xsrc/cairo-ps.h116
-rwxr-xr-xsrc/cairo-qt-surface.cpp1713
-rwxr-xr-xsrc/cairo-qt.h85
-rwxr-xr-xsrc/cairo-quartz-filters.c450
-rwxr-xr-xsrc/cairo-quartz-font.c860
-rwxr-xr-xsrc/cairo-quartz-image-surface.c385
-rwxr-xr-xsrc/cairo-quartz-image.h59
-rwxr-xr-xsrc/cairo-quartz-private.h111
-rwxr-xr-xsrc/cairo-quartz-surface.c2895
-rwxr-xr-xsrc/cairo-quartz.h82
-rwxr-xr-xsrc/cairo-raster-source-pattern.c432
-rwxr-xr-xsrc/cairo-recording-surface-inline.h68
-rwxr-xr-xsrc/cairo-recording-surface-private.h187
-rwxr-xr-xsrc/cairo-recording-surface.c2085
-rwxr-xr-xsrc/cairo-rectangle.c330
-rwxr-xr-xsrc/cairo-rectangular-scan-converter.c791
-rwxr-xr-xsrc/cairo-reference-count-private.h62
-rwxr-xr-xsrc/cairo-region-private.h77
-rwxr-xr-xsrc/cairo-region.c950
-rwxr-xr-xsrc/cairo-rtree-private.h142
-rwxr-xr-xsrc/cairo-rtree.c388
-rwxr-xr-xsrc/cairo-scaled-font-private.h183
-rwxr-xr-xsrc/cairo-scaled-font-subsets-private.h720
-rwxr-xr-xsrc/cairo-scaled-font-subsets.c1259
-rwxr-xr-xsrc/cairo-scaled-font.c3187
-rwxr-xr-xsrc/cairo-script-private.h59
-rwxr-xr-xsrc/cairo-script-surface.c4024
-rwxr-xr-xsrc/cairo-script.h98
-rwxr-xr-xsrc/cairo-shape-mask-compositor.c337
-rwxr-xr-xsrc/cairo-skia-surface.cpp1175
-rwxr-xr-xsrc/cairo-skia.h66
-rwxr-xr-xsrc/cairo-slope-private.h72
-rwxr-xr-xsrc/cairo-slope.c99
-rwxr-xr-xsrc/cairo-spans-compositor-private.h111
-rwxr-xr-xsrc/cairo-spans-compositor.c1187
-rwxr-xr-xsrc/cairo-spans-private.h206
-rwxr-xr-xsrc/cairo-spans.c248
-rwxr-xr-xsrc/cairo-spline.c430
-rwxr-xr-xsrc/cairo-stroke-dash-private.h70
-rwxr-xr-xsrc/cairo-stroke-dash.c96
-rwxr-xr-xsrc/cairo-stroke-style.c354
-rwxr-xr-xsrc/cairo-surface-backend-private.h262
-rwxr-xr-xsrc/cairo-surface-clipper-private.h71
-rwxr-xr-xsrc/cairo-surface-clipper.c196
-rwxr-xr-xsrc/cairo-surface-fallback-private.h95
-rwxr-xr-xsrc/cairo-surface-fallback.c115
-rwxr-xr-xsrc/cairo-surface-inline.h60
-rwxr-xr-xsrc/cairo-surface-observer-inline.h59
-rwxr-xr-xsrc/cairo-surface-observer-private.h208
-rwxr-xr-xsrc/cairo-surface-observer.c2104
-rwxr-xr-xsrc/cairo-surface-offset-private.h95
-rwxr-xr-xsrc/cairo-surface-offset.c308
-rwxr-xr-xsrc/cairo-surface-private.h121
-rwxr-xr-xsrc/cairo-surface-scale-translate-private.h163
-rwxr-xr-xsrc/cairo-surface-scale-translate.c689
-rwxr-xr-xsrc/cairo-surface-shadow-private.h98
-rwxr-xr-xsrc/cairo-surface-shadow.c2286
-rwxr-xr-xsrc/cairo-surface-snapshot-inline.h67
-rwxr-xr-xsrc/cairo-surface-snapshot-private.h51
-rwxr-xr-xsrc/cairo-surface-snapshot.c289
-rwxr-xr-xsrc/cairo-surface-subsurface-inline.h72
-rwxr-xr-xsrc/cairo-surface-subsurface-private.h55
-rwxr-xr-xsrc/cairo-surface-subsurface.c574
-rwxr-xr-xsrc/cairo-surface-wrapper-private.h193
-rwxr-xr-xsrc/cairo-surface-wrapper.c686
-rwxr-xr-xsrc/cairo-surface.c2644
-rwxr-xr-xsrc/cairo-svg-surface-private.h74
-rwxr-xr-xsrc/cairo-svg-surface.c2876
-rwxr-xr-xsrc/cairo-svg.h84
-rwxr-xr-xsrc/cairo-tee-surface-private.h47
-rwxr-xr-xsrc/cairo-tee-surface.c602
-rwxr-xr-xsrc/cairo-tee.h66
-rwxr-xr-xsrc/cairo-tg-allocator-private.h134
-rwxr-xr-xsrc/cairo-tg-composite-extents-private.h121
-rwxr-xr-xsrc/cairo-tg-journal-private.h229
-rwxr-xr-xsrc/cairo-tg-journal.c520
-rwxr-xr-xsrc/cairo-tg-private.h57
-rwxr-xr-xsrc/cairo-tg-surface.c1372
-rwxr-xr-xsrc/cairo-tg.h70
-rwxr-xr-xsrc/cairo-thread-local-private.h161
-rwxr-xr-xsrc/cairo-time-private.h94
-rwxr-xr-xsrc/cairo-time.c225
-rwxr-xr-xsrc/cairo-tor-scan-converter.c1854
-rwxr-xr-xsrc/cairo-tor22-scan-converter.c1709
-rwxr-xr-xsrc/cairo-toy-font-face.c523
-rwxr-xr-xsrc/cairo-traps-compositor.c2403
-rwxr-xr-xsrc/cairo-traps-private.h141
-rwxr-xr-xsrc/cairo-traps.c1067
-rwxr-xr-xsrc/cairo-tristrip-private.h94
-rwxr-xr-xsrc/cairo-tristrip.c185
-rwxr-xr-xsrc/cairo-truetype-subset-private.h212
-rwxr-xr-xsrc/cairo-truetype-subset.c1668
-rwxr-xr-xsrc/cairo-type1-fallback.c907
-rwxr-xr-xsrc/cairo-type1-glyph-names.c410
-rwxr-xr-xsrc/cairo-type1-private.h51
-rwxr-xr-xsrc/cairo-type1-subset.c1825
-rwxr-xr-xsrc/cairo-type3-glyph-surface-private.h87
-rwxr-xr-xsrc/cairo-type3-glyph-surface.c573
-rwxr-xr-xsrc/cairo-types-private.h453
-rwxr-xr-xsrc/cairo-unicode.c422
-rwxr-xr-xsrc/cairo-uninstalled.pc.in8
-rwxr-xr-xsrc/cairo-user-font-private.h46
-rwxr-xr-xsrc/cairo-user-font.c831
-rwxr-xr-xsrc/cairo-version.c260
-rwxr-xr-xsrc/cairo-version.h14
-rwxr-xr-xsrc/cairo-vg-surface.c1853
-rwxr-xr-xsrc/cairo-vg.h103
-rwxr-xr-xsrc/cairo-wgl-context.c268
-rwxr-xr-xsrc/cairo-wideint-private.h338
-rwxr-xr-xsrc/cairo-wideint-type-private.h158
-rwxr-xr-xsrc/cairo-wideint.c852
-rwxr-xr-xsrc/cairo-win32.h112
-rwxr-xr-xsrc/cairo-xcb-connection-core.c314
-rwxr-xr-xsrc/cairo-xcb-connection-render.c301
-rwxr-xr-xsrc/cairo-xcb-connection-shm.c117
-rwxr-xr-xsrc/cairo-xcb-connection.c985
-rwxr-xr-xsrc/cairo-xcb-private.h770
-rwxr-xr-xsrc/cairo-xcb-screen.c364
-rwxr-xr-xsrc/cairo-xcb-shm.c337
-rwxr-xr-xsrc/cairo-xcb-surface-core.c642
-rwxr-xr-xsrc/cairo-xcb-surface-render.c5253
-rwxr-xr-xsrc/cairo-xcb-surface.c1702
-rwxr-xr-xsrc/cairo-xcb.h116
-rwxr-xr-xsrc/cairo-xlib-core-compositor.c652
-rwxr-xr-xsrc/cairo-xlib-display.c663
-rwxr-xr-xsrc/cairo-xlib-fallback-compositor.c248
-rwxr-xr-xsrc/cairo-xlib-private.h468
-rwxr-xr-xsrc/cairo-xlib-render-compositor.c2019
-rwxr-xr-xsrc/cairo-xlib-screen.c472
-rwxr-xr-xsrc/cairo-xlib-source.c1157
-rwxr-xr-xsrc/cairo-xlib-surface-private.h44
-rwxr-xr-xsrc/cairo-xlib-surface-shm.c1459
-rwxr-xr-xsrc/cairo-xlib-surface.c2382
-rwxr-xr-xsrc/cairo-xlib-visual.c191
-rwxr-xr-xsrc/cairo-xlib-xcb-surface.c844
-rwxr-xr-xsrc/cairo-xlib-xrender-private.h1169
-rwxr-xr-xsrc/cairo-xlib-xrender.h66
-rwxr-xr-xsrc/cairo-xlib.h118
-rwxr-xr-xsrc/cairo-xml-surface.c1143
-rwxr-xr-xsrc/cairo-xml.h67
-rwxr-xr-xsrc/cairo.c4127
-rwxr-xr-xsrc/cairo.h3184
-rwxr-xr-xsrc/cairo.pc.in13
-rwxr-xr-xsrc/cairoint.h2105
-rwxr-xr-xsrc/check-def.sh48
-rwxr-xr-xsrc/check-doc-syntax.awk108
-rwxr-xr-xsrc/check-doc-syntax.sh82
-rwxr-xr-xsrc/check-has-hidden-symbols.c3
-rwxr-xr-xsrc/check-headers.sh27
-rwxr-xr-xsrc/check-link.c24
-rwxr-xr-xsrc/check-plt.sh28
-rwxr-xr-xsrc/check-preprocessor-syntax.sh59
-rwxr-xr-xsrc/drm/cairo-drm-bo.c130
-rwxr-xr-xsrc/drm/cairo-drm-gallium-surface.c826
-rwxr-xr-xsrc/drm/cairo-drm-i915-glyphs.c563
-rwxr-xr-xsrc/drm/cairo-drm-i915-private.h1270
-rwxr-xr-xsrc/drm/cairo-drm-i915-shader.c2893
-rwxr-xr-xsrc/drm/cairo-drm-i915-spans.c799
-rwxr-xr-xsrc/drm/cairo-drm-i915-surface.c2942
-rwxr-xr-xsrc/drm/cairo-drm-i965-glyphs.c504
-rwxr-xr-xsrc/drm/cairo-drm-i965-private.h737
-rwxr-xr-xsrc/drm/cairo-drm-i965-shader.c2825
-rwxr-xr-xsrc/drm/cairo-drm-i965-spans.c407
-rwxr-xr-xsrc/drm/cairo-drm-i965-surface.c1927
-rwxr-xr-xsrc/drm/cairo-drm-intel-brw-defines.h824
-rwxr-xr-xsrc/drm/cairo-drm-intel-brw-eu-emit.c1089
-rwxr-xr-xsrc/drm/cairo-drm-intel-brw-eu-util.c121
-rwxr-xr-xsrc/drm/cairo-drm-intel-brw-eu.c250
-rwxr-xr-xsrc/drm/cairo-drm-intel-brw-eu.h1043
-rwxr-xr-xsrc/drm/cairo-drm-intel-brw-structs.h1328
-rwxr-xr-xsrc/drm/cairo-drm-intel-command-private.h909
-rwxr-xr-xsrc/drm/cairo-drm-intel-debug.c1209
-rwxr-xr-xsrc/drm/cairo-drm-intel-ioctl-private.h442
-rwxr-xr-xsrc/drm/cairo-drm-intel-private.h519
-rwxr-xr-xsrc/drm/cairo-drm-intel-surface.c451
-rwxr-xr-xsrc/drm/cairo-drm-intel.c1389
-rwxr-xr-xsrc/drm/cairo-drm-ioctl-private.h12
-rwxr-xr-xsrc/drm/cairo-drm-private.h238
-rwxr-xr-xsrc/drm/cairo-drm-radeon-private.h107
-rwxr-xr-xsrc/drm/cairo-drm-radeon-surface.c448
-rwxr-xr-xsrc/drm/cairo-drm-radeon.c451
-rwxr-xr-xsrc/drm/cairo-drm-surface.c369
-rwxr-xr-xsrc/drm/cairo-drm.c387
-rwxr-xr-xsrc/skia/cairo-skia-context.cpp1750
-rwxr-xr-xsrc/skia/cairo-skia-private.h110
-rwxr-xr-xsrc/skia/cairo-skia-surface.cpp324
-rwxr-xr-xsrc/test-base-compositor-surface.c824
-rwxr-xr-xsrc/test-compositor-surface-private.h56
-rwxr-xr-xsrc/test-compositor-surface.c260
-rwxr-xr-xsrc/test-compositor-surface.h71
-rwxr-xr-xsrc/test-null-compositor-surface.c482
-rwxr-xr-xsrc/test-null-compositor-surface.h60
-rwxr-xr-xsrc/test-paginated-surface.c286
-rwxr-xr-xsrc/test-paginated-surface.h48
-rwxr-xr-xsrc/win32/cairo-win32-debug.c87
-rwxr-xr-xsrc/win32/cairo-win32-device.c189
-rwxr-xr-xsrc/win32/cairo-win32-display-surface.c1071
-rwxr-xr-xsrc/win32/cairo-win32-font.c2313
-rwxr-xr-xsrc/win32/cairo-win32-gdi-compositor.c649
-rwxr-xr-xsrc/win32/cairo-win32-printing-surface.c1917
-rwxr-xr-xsrc/win32/cairo-win32-private.h226
-rwxr-xr-xsrc/win32/cairo-win32-surface.c321
-rwxr-xr-xsrc/win32/cairo-win32-system.c89
393 files changed, 249924 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100755
index 000000000..fd53c8649
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,33 @@
+.deps
+.libs
+Makefile
+Makefile.in
+Makefile.am.features
+#Makefile.win32.features
+*.gcda
+*.gcno
+*.la
+*.lo
+*.loT
+*.pc
+cairo-features.h
+cairo-supported-features.h
+cairo.def
+*.i
+*.s
+*.o
+*.obj
+*.pdb
+*.dll
+*.manifest
+*.ilk
+*.exp
+*.lib
+*~
+.*.sw?
+TAGS
+tags
+check-has-hidden-symbols.i
+check-link
+check-skiplist
+headers-standalone
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100755
index 000000000..acf0a8281
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,119 @@
+# Note: All source files are listed in Makefile.sources.
+
+include $(top_srcdir)/build/Makefile.am.common
+include $(srcdir)/Makefile.am.features
+
+EXTRA_DIST += Makefile.win32 Makefile.win32.features
+#MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features
+
+AM_CPPFLAGS = -I$(srcdir) $(CAIRO_CFLAGS)
+AM_LDFLAGS = $(CAIRO_LDFLAGS)
+
+if OS_WIN32
+export_symbols = -export-symbols cairo.def
+cairo_def_dependency = cairo.def
+endif
+
+$(top_builddir)/config.h: $(top_srcdir)/config.h.in
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) config.h
+
+cairoincludedir = $(includedir)/cairo
+cairoinclude_HEADERS = $(enabled_cairo_headers)
+
+lib_LTLIBRARIES = libcairo.la
+
+if BUILD_CXX
+cairo_cxx_lib = libcairo_cxx.la
+else
+cairo_cxx_lib =
+endif
+
+noinst_LTLIBRARIES = $(cairo_cxx_lib)
+libcairo_cxx_la_SOURCES = \
+ $(enabled_cairo_headers) \
+ $(enabled_cairo_private) \
+ $(enabled_cairo_cxx_sources) \
+ $(NULL)
+libcairo_cxx_la_LDFLAGS = $(AM_LDFLAGS) $(export_symbols)
+libcairo_cxx_la_LIBADD = $(CAIRO_LIBS)
+libcairo_cxx_la_DEPENDENCIES = $(cairo_def_dependency)
+
+
+libcairo_la_SOURCES = \
+ $(enabled_cairo_headers) \
+ $(enabled_cairo_private) \
+ $(enabled_cairo_sources) \
+ $(NULL)
+libcairo_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols)
+libcairo_la_LIBADD = $(CAIRO_LIBS) \
+ $(cairo_cxx_lib)
+libcairo_la_DEPENDENCIES = $(cairo_def_dependency) $(cairo_cxx_lib)
+
+# Special headers
+cairoinclude_HEADERS += $(top_srcdir)/cairo-version.h
+libcairo_la_SOURCES += cairo-version.h
+nodist_cairoinclude_HEADERS = cairo-features.h
+nodist_libcairo_la_SOURCES = cairo-features.h
+BUILT_SOURCES += cairo-features.h cairo-supported-features.h
+DISTCLEANFILES += cairo-features.h cairo-supported-features.h
+cairo-features.h cairo-supported-features.h:
+ cd $(top_builddir) && ./config.status src/$@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = $(enabled_cairo_pkgconf)
+
+CLEANFILES += cairo.def
+cairo.def: cairo-features.h $(enabled_cairo_headers)
+ @echo Generating $@
+ @(echo EXPORTS; \
+ (cd $(srcdir); cat $(enabled_cairo_headers) || echo 'cairo_ERROR ()' ) | \
+ $(EGREP) -v '^# *include' | \
+ ( cat cairo-features.h - | $(CPP) -D__cplusplus - || echo 'cairo_ERROR ()' ) | \
+ $(EGREP) '^cairo_.* \(' | \
+ sed -e 's/[ ].*//' | \
+ sort; \
+ echo LIBRARY libcairo-$(CAIRO_VERSION_SONUM).dll; \
+ ) >$@
+ @ ! grep -q cairo_ERROR $@ || ($(RM) $@; false)
+
+TESTS_ENVIRONMENT = \
+ srcdir="$(srcdir)" \
+ MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
+ all_cairo_files="$(all_cairo_files)" \
+ all_cairo_headers="$(all_cairo_headers)" \
+ all_cairo_private="$(all_cairo_private)" \
+ all_cairo_sources="$(all_cairo_sources)" \
+ enabled_cairo_headers="$(enabled_cairo_headers)" \
+ enabled_cairo_private="$(enabled_cairo_private)" \
+ enabled_cairo_sources="$(enabled_cairo_sources)" \
+ $(NULL)
+TESTS_SH = \
+ check-def.sh \
+ check-doc-syntax.sh \
+ check-headers.sh \
+ check-plt.sh \
+ check-preprocessor-syntax.sh \
+ $(NULL)
+TESTS += $(TESTS_SH)
+if CROSS_COMPILING
+else
+TESTS += check-link$(EXEEXT)
+endif
+
+EXTRA_DIST += $(TESTS_SH) check-has-hidden-symbols.c check-doc-syntax.awk
+check_PROGRAMS += check-link
+check_link_LDADD = libcairo.la
+
+check: headers-standalone
+
+PREPROCESS_ARGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS)
+COMPILE_ARGS = $(PREPROCESS_ARGS) $(AM_CFLAGS) $(CFLAGS)
+
+# The pre-processed result is used by check-{def,plt}.sh to determine whether
+# cairo has been compiled with symbol hiding.
+.c.i: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h
+ $(CPP) $(PREPROCESS_ARGS) $< -o $@
+.c.s: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h
+ $(CC) $(COMPILE_ARGS) $< -S -o $@
+
+include $(srcdir)/Makefile.am.analysis
diff --git a/src/Makefile.am.analysis b/src/Makefile.am.analysis
new file mode 100755
index 000000000..fab4cf7a5
--- /dev/null
+++ b/src/Makefile.am.analysis
@@ -0,0 +1,35 @@
+
+SPARSE = sparse
+sparse:
+ @echo Checking enabled sources with sparse checker
+ @status=true; for f in $(enabled_cairo_sources) $(enabled_cairo_cxx_sources); do \
+ echo $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f; \
+ $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \
+ done; $$status
+
+SPLINT = splint -badflag
+splint:
+ @echo Checking enabled sources with splint checker
+ @status=true; for f in $(enabled_cairo_sources) $(enabled_cairo_cxx_sources); do \
+ echo $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f; \
+ $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \
+ done; $$status
+
+UNO = uno
+uno:
+ @echo Checking enabled sources with uno checker
+ cd $(srcdir); $(UNO) $(PREPROCESS_ARGS) -DHAVE_CONFIG_H -U__GNUC__ $(enabled_cairo_sources)
+
+headers-standalone: $(enabled_cairo_headers) $(enabled_cairo_private)
+ @echo Checking that enabled public/private headers can be compiled standalone
+ @status=true; for f in $(enabled_cairo_headers) $(enabled_cairo_private); do \
+ echo " CHECK $$f"; \
+ echo "#include \"$(srcdir)/$$f\"" > headers-standalone-tmp.c; \
+ echo "int main(int argc, char * argv[]) { return 0; }" >> headers-standalone-tmp.c; \
+ $(COMPILE) -o headers-standalone-tmp headers-standalone-tmp.c || status=false; \
+ $(RM) headers-standalone-tmp headers-standalone-tmp.c; \
+ done; $$status
+ @touch $@
+CLEANFILES += headers-standalone
+
+analysis: all headers-standalone sparse splint uno
diff --git a/src/Makefile.sources b/src/Makefile.sources
new file mode 100755
index 000000000..0c9bfbe54
--- /dev/null
+++ b/src/Makefile.sources
@@ -0,0 +1,521 @@
+# Makefile.sources
+#
+# This file is the canonical location listing all the source files used
+# to build the cairo library. Every source file is categorized as one of:
+#
+# * public header file
+# * private header file (must end in -private.h except for cairoint.h)
+# * source code file
+#
+# Every source file should be specified exactly once, grouped with the
+# feature that uses the source file. If more than one feature use the
+# file (like pdf_operators or font_subset files), the files should be
+# appended to to the base cairo files, and the code inside them
+# enabled/disabled using C preprocessor macros defined in cairoint.h.
+# See how pdf_operators or font_subset are handled.
+#
+# The sources are picked up according to the configured features
+# by the generated file Makefile.am.features or Makefile.win32.features.
+#
+# These are a few special source files. Those are not included in this
+# file to not confuse build systems. Each build system must handle them
+# separately. These files include:
+#
+# * cairo-features.h:
+# This file is generated by configure and includes macros signifying
+# which features are enabled. This file should be installed like
+# other public headers, but should NOT be distributed in the cairo
+# distribution.
+#
+# * cairo-version.h:
+# This is a dummy header file used during the build, but it should
+# NOT be installed. Its sole purpose is to make sure changes in the
+# cairo version do not trigger a full rebuild of the library, but
+# just the functions actually using the version information.
+#
+# * $(top_srcdir)/cairo-version.h:
+# This is the real file holding the cairo version number. This file
+# should be installed like other public headers. This is used during
+# the build by cairo-version.c only.
+#
+# * cairo-supported-features.h:
+# This file is generated by configure and includes macros signifying
+# all supported features. This is used by gtk-doc to generate
+# documentation for all those macros, enabled or not.
+# This file is NOT used during the build of the library and should
+# NOT be installed or distributed.
+#
+# Please follow the strict syntax of this file, including keeping file
+# lists sorted.
+#
+
+cairo_headers = cairo.h cairo-deprecated.h
+cairo_private = \
+ cairoint.h \
+ cairo-analysis-surface-private.h \
+ cairo-arc-private.h \
+ cairo-array-private.h \
+ cairo-atomic-private.h \
+ cairo-backend-private.h \
+ cairo-box-inline.h \
+ cairo-boxes-private.h \
+ cairo-cache-private.h \
+ cairo-clip-inline.h \
+ cairo-clip-private.h \
+ cairo-combsort-inline.h \
+ cairo-compiler-private.h \
+ cairo-compositor-private.h \
+ cairo-contour-inline.h \
+ cairo-contour-private.h \
+ cairo-composite-rectangles-private.h \
+ cairo-damage-private.h \
+ cairo-default-context-private.h \
+ cairo-device-private.h \
+ cairo-error-inline.h \
+ cairo-error-private.h \
+ cairo-filters-private.h \
+ cairo-fixed-private.h \
+ cairo-fixed-type-private.h \
+ cairo-freelist-private.h \
+ cairo-freelist-type-private.h \
+ cairo-freed-pool-private.h \
+ cairo-fontconfig-private.h \
+ cairo-gstate-private.h \
+ cairo-hash-private.h \
+ cairo-image-filters-private.h \
+ cairo-image-info-private.h \
+ cairo-image-surface-inline.h \
+ cairo-image-surface-private.h \
+ cairo-list-inline.h \
+ cairo-list-private.h \
+ cairo-malloc-private.h \
+ cairo-mempool-private.h \
+ cairo-mutex-impl-private.h \
+ cairo-mutex-list-private.h \
+ cairo-mutex-private.h \
+ cairo-mutex-type-private.h \
+ cairo-output-stream-private.h \
+ cairo-paginated-private.h \
+ cairo-paginated-surface-private.h \
+ cairo-path-fixed-private.h \
+ cairo-path-private.h \
+ cairo-pattern-inline.h \
+ cairo-pattern-private.h \
+ cairo-private.h \
+ cairo-recording-surface-inline.h \
+ cairo-recording-surface-private.h \
+ cairo-reference-count-private.h \
+ cairo-region-private.h \
+ cairo-rtree-private.h \
+ cairo-scaled-font-private.h \
+ cairo-slope-private.h \
+ cairo-spans-private.h \
+ cairo-spans-compositor-private.h \
+ cairo-stroke-dash-private.h \
+ cairo-surface-inline.h \
+ cairo-surface-private.h \
+ cairo-surface-backend-private.h \
+ cairo-surface-clipper-private.h \
+ cairo-surface-fallback-private.h \
+ cairo-surface-observer-inline.h \
+ cairo-surface-observer-private.h \
+ cairo-surface-offset-private.h \
+ cairo-surface-scale-translate-private.h \
+ cairo-surace-shadow-private.h \
+ cairo-surface-subsurface-inline.h \
+ cairo-surface-subsurface-private.h \
+ cairo-surface-snapshot-inline.h \
+ cairo-surface-snapshot-private.h \
+ cairo-surface-wrapper-private.h \
+ cairo-time-private.h \
+ cairo-types-private.h \
+ cairo-traps-private.h \
+ cairo-tristrip-private.h \
+ cairo-user-font-private.h \
+ cairo-wideint-private.h \
+ cairo-wideint-type-private.h \
+ cairo-thread-local-private.h \
+ $(NULL)
+cairo_sources = \
+ cairo-analysis-surface.c \
+ cairo-arc.c \
+ cairo-array.c \
+ cairo-atomic.c \
+ cairo-base64-stream.c \
+ cairo-base85-stream.c \
+ cairo-bentley-ottmann.c \
+ cairo-bentley-ottmann-rectangular.c \
+ cairo-bentley-ottmann-rectilinear.c \
+ cairo-botor-scan-converter.c \
+ cairo-boxes.c \
+ cairo-boxes-intersect.c \
+ cairo.c \
+ cairo-cache.c \
+ cairo-clip.c \
+ cairo-clip-boxes.c \
+ cairo-clip-polygon.c \
+ cairo-clip-region.c \
+ cairo-clip-surface.c \
+ cairo-color.c \
+ cairo-composite-rectangles.c \
+ cairo-compositor.c \
+ cairo-contour.c \
+ cairo-damage.c \
+ cairo-debug.c \
+ cairo-default-context.c \
+ cairo-device.c \
+ cairo-error.c \
+ cairo-fallback-compositor.c \
+ cairo-filters.c \
+ cairo-fixed.c \
+ cairo-font-face.c \
+ cairo-font-face-twin.c \
+ cairo-font-face-twin-data.c \
+ cairo-font-options.c \
+ cairo-freelist.c \
+ cairo-freed-pool.c \
+ cairo-gstate.c \
+ cairo-hash.c \
+ cairo-hull.c \
+ cairo-image-compositor.c \
+ cairo-image-filters.c \
+ cairo-image-info.c \
+ cairo-image-source.c \
+ cairo-image-surface.c \
+ cairo-lzw.c \
+ cairo-matrix.c \
+ cairo-mask-compositor.c \
+ cairo-mesh-pattern-rasterizer.c \
+ cairo-mempool.c \
+ cairo-misc.c \
+ cairo-mono-scan-converter.c \
+ cairo-mutex.c \
+ cairo-no-compositor.c \
+ cairo-observer.c \
+ cairo-output-stream.c \
+ cairo-paginated-surface.c \
+ cairo-path-bounds.c \
+ cairo-path.c \
+ cairo-path-fill.c \
+ cairo-path-fixed.c \
+ cairo-path-in-fill.c \
+ cairo-path-stroke.c \
+ cairo-path-stroke-boxes.c \
+ cairo-path-stroke-polygon.c \
+ cairo-path-stroke-traps.c \
+ cairo-path-stroke-tristrip.c \
+ cairo-pattern.c \
+ cairo-pen.c \
+ cairo-polygon.c \
+ cairo-polygon-intersect.c \
+ cairo-polygon-reduce.c \
+ cairo-raster-source-pattern.c \
+ cairo-recording-surface.c \
+ cairo-rectangle.c \
+ cairo-rectangular-scan-converter.c \
+ cairo-region.c \
+ cairo-rtree.c \
+ cairo-scaled-font.c \
+ cairo-shape-mask-compositor.c \
+ cairo-slope.c \
+ cairo-spans.c \
+ cairo-spans-compositor.c \
+ cairo-spline.c \
+ cairo-stroke-dash.c \
+ cairo-stroke-style.c \
+ cairo-surface.c \
+ cairo-surface-clipper.c \
+ cairo-surface-fallback.c \
+ cairo-surface-observer.c \
+ cairo-surface-offset.c \
+ cairo-surface-scale-translate.c \
+ cairo-surface-shadow.c \
+ cairo-surface-snapshot.c \
+ cairo-surface-subsurface.c \
+ cairo-surface-wrapper.c \
+ cairo-time.c \
+ cairo-tor-scan-converter.c \
+ cairo-tor22-scan-converter.c \
+ cairo-clip-tor-scan-converter.c \
+ cairo-toy-font-face.c \
+ cairo-traps.c \
+ cairo-tristrip.c \
+ cairo-traps-compositor.c \
+ cairo-unicode.c \
+ cairo-user-font.c \
+ cairo-version.c \
+ cairo-wideint.c \
+ $(NULL)
+
+_cairo_font_subset_private = \
+ cairo-scaled-font-subsets-private.h \
+ cairo-truetype-subset-private.h \
+ cairo-type1-private.h \
+ cairo-type3-glyph-surface-private.h \
+ $(NULL)
+_cairo_font_subset_sources = \
+ cairo-cff-subset.c \
+ cairo-scaled-font-subsets.c \
+ cairo-truetype-subset.c \
+ cairo-type1-fallback.c \
+ cairo-type1-glyph-names.c \
+ cairo-type1-subset.c \
+ cairo-type3-glyph-surface.c \
+ $(NULL)
+cairo_private += $(_cairo_font_subset_private)
+cairo_sources += $(_cairo_font_subset_sources)
+
+cairo_egl_sources =
+cairo_glx_sources =
+cairo_wgl_sources =
+
+_cairo_pdf_operators_private = cairo-pdf-operators-private.h cairo-pdf-shading-private.h
+_cairo_pdf_operators_sources = cairo-pdf-operators.c cairo-pdf-shading.c
+cairo_private += $(_cairo_pdf_operators_private)
+cairo_sources += $(_cairo_pdf_operators_sources)
+
+cairo_png_sources = cairo-png.c
+
+cairo_ps_headers = cairo-ps.h
+cairo_ps_private = cairo-ps-surface-private.h
+cairo_ps_sources = cairo-ps-surface.c
+
+_cairo_deflate_stream_sources = cairo-deflate-stream.c
+cairo_sources += $(_cairo_deflate_stream_sources)
+
+cairo_pdf_headers = cairo-pdf.h
+cairo_pdf_private = cairo-pdf-surface-private.h
+cairo_pdf_sources = cairo-pdf-surface.c
+
+cairo_svg_headers = cairo-svg.h
+cairo_svg_private = cairo-svg-surface-private.h
+cairo_svg_sources = cairo-svg-surface.c
+
+cairo_ft_headers = cairo-ft.h
+cairo_ft_private = cairo-ft-private.h
+cairo_ft_sources = cairo-ft-font.c
+
+# These are private, even though they look like public headers
+cairo_test_surfaces_private = \
+ test-compositor-surface.h \
+ test-compositor-surface-private.h \
+ test-null-compositor-surface.h \
+ test-paginated-surface.h \
+ $(NULL)
+cairo_test_surfaces_sources = \
+ test-compositor-surface.c \
+ test-null-compositor-surface.c \
+ test-base-compositor-surface.c \
+ test-paginated-surface.c \
+ $(NULL)
+
+cairo_xlib_headers = cairo-xlib.h
+cairo_xlib_private = \
+ cairo-xlib-private.h \
+ cairo-xlib-surface-private.h \
+ cairo-xlib-xrender-private.h \
+ $(NULL)
+cairo_xlib_sources = \
+ cairo-xlib-display.c \
+ cairo-xlib-core-compositor.c \
+ cairo-xlib-fallback-compositor.c \
+ cairo-xlib-render-compositor.c \
+ cairo-xlib-screen.c \
+ cairo-xlib-source.c \
+ cairo-xlib-surface.c \
+ cairo-xlib-surface-shm.c \
+ cairo-xlib-visual.c \
+ cairo-xlib-xcb-surface.c \
+ $(NULL)
+
+cairo_xlib_xrender_headers = cairo-xlib-xrender.h
+
+cairo_xcb_headers = cairo-xcb.h
+cairo_xcb_private = cairo-xcb-private.h
+cairo_xcb_sources = \
+ cairo-xcb-connection.c \
+ cairo-xcb-connection-core.c \
+ cairo-xcb-connection-render.c \
+ cairo-xcb-connection-shm.c \
+ cairo-xcb-screen.c \
+ cairo-xcb-shm.c \
+ cairo-xcb-surface.c \
+ cairo-xcb-surface-core.c \
+ cairo-xcb-surface-render.c \
+ $(NULL)
+
+cairo_qt_headers = cairo-qt.h
+cairo_qt_cxx_sources = cairo-qt-surface.cpp
+
+cairo_quartz_headers = cairo-quartz.h
+cairo_quartz_private = cairo-quartz-private.h
+cairo_quartz_sources = cairo-quartz-surface.c \
+ cairo-quartz-filters.c
+
+cairo_quartz_image_headers = cairo-quartz-image.h
+cairo_quartz_image_sources = cairo-quartz-image-surface.c
+
+cairo_quartz_font_sources = cairo-quartz-font.c
+
+cairo_win32_headers = cairo-win32.h
+cairo_win32_private = win32/cairo-win32-private.h
+cairo_win32_sources = \
+ win32/cairo-win32-debug.c \
+ win32/cairo-win32-device.c \
+ win32/cairo-win32-gdi-compositor.c \
+ win32/cairo-win32-system.c \
+ win32/cairo-win32-surface.c \
+ win32/cairo-win32-display-surface.c \
+ win32/cairo-win32-printing-surface.c \
+ $(NULL)
+cairo_win32_font_sources = \
+ win32/cairo-win32-font.c \
+ $(NULL)
+
+cairo_skia_headers = cairo-skia.h
+cairo_skia_private = skia/cairo-skia-private.h
+cairo_skia_cxx_sources = \
+ skia/cairo-skia-context.cpp \
+ skia/cairo-skia-surface.cpp \
+ $(NULL)
+
+cairo_os2_headers = cairo-os2.h
+cairo_os2_private = cairo-os2-private.h
+cairo_os2_sources = cairo-os2-surface.c
+
+# automake is stupid enough to always use c++ linker if we enable the
+# following lines, even if beos surface is not enabled. Disable it for now.
+cairo_beos_headers = cairo-beos.h
+cairo_beos_cxx_sources = cairo-beos-surface.cpp
+
+cairo_gl_headers = cairo-gl.h
+cairo_gl_private = cairo-gl-private.h \
+ cairo-gl-dispatch-private.h \
+ cairo-gl-ext-def-private.h \
+ cairo-gl-gradient-private.h
+
+cairo_gl_sources = cairo-gl-composite.c \
+ cairo-gl-device.c \
+ cairo-gl-dispatch.c \
+ cairo-gl-glyphs.c \
+ cairo-gl-gradient.c \
+ cairo-gl-info.c \
+ cairo-gl-operand.c \
+ cairo-gl-shaders.c \
+ cairo-gl-filters.c \
+ cairo-gl-hairline-stroke.c \
+ cairo-gl-msaa-compositor.c \
+ cairo-gl-spans-compositor.c \
+ cairo-gl-traps-compositor.c \
+ cairo-gl-source.c \
+ cairo-gl-surface.c
+
+cairo_glesv2_headers = $(cairo_gl_headers)
+cairo_glesv2_private = $(cairo_gl_private)
+cairo_glesv2_sources = $(cairo_gl_sources)
+
+cairo_glesv3_headers = $(cairo_gl_headers)
+cairo_glesv3_private = $(cairo_gl_private)
+cairo_glesv3_sources = $(cairo_gl_sources)
+
+if CAIRO_HAS_EVASGL_SURFACE
+if CAIRO_HAS_GL_SURFACE
+cairo_evasgl_headers =
+cairo_evasgl_private =
+cairo_evasgl_sources =
+else
+if CAIRO_HAS_GLESV2_SURFACE
+cairo_evasgl_headers =
+cairo_evasgl_private =
+cairo_evasgl_sources =
+else
+if CAIRO_HAS_GLESV3_SURFACE
+cairo_evasgl_headers =
+cairo_evasgl_private =
+cairo_evasgl_sources =
+else
+cairo_evasgl_headers = $(cairo_gl_headers)
+cairo_evasgl_private = $(cairo_gl_private)
+cairo_evasgl_sources = $(cairo_gl_sources)
+endif
+endif
+endif
+else
+cairo_evasgl_headers =
+cairo_evasgl_private =
+cairo_evasgl_sources =
+endif
+
+cairo_egl_sources += cairo-egl-context.c
+cairo_glx_sources += cairo-glx-context.c
+cairo_wgl_sources += cairo-wgl-context.c
+cairo_evasgl_headers += cairo-evas-gl.h
+cairo_evasgl_sources += cairo-evas-gl-context.c
+
+cairo_directfb_headers = cairo-directfb.h
+cairo_directfb_sources = cairo-directfb-surface.c
+
+cairo_drm_headers = cairo-drm.h
+cairo_drm_private = drm/cairo-drm-private.h \
+ drm/cairo-drm-ioctl-private.h \
+ drm/cairo-drm-intel-private.h \
+ drm/cairo-drm-intel-brw-defines.h \
+ drm/cairo-drm-intel-brw-structs.h \
+ drm/cairo-drm-intel-brw-eu.h \
+ drm/cairo-drm-intel-command-private.h \
+ drm/cairo-drm-intel-ioctl-private.h \
+ drm/cairo-drm-i915-private.h \
+ drm/cairo-drm-i965-private.h \
+ drm/cairo-drm-radeon-private.h
+cairo_drm_sources = drm/cairo-drm.c \
+ drm/cairo-drm-bo.c \
+ drm/cairo-drm-surface.c \
+ drm/cairo-drm-intel.c \
+ drm/cairo-drm-intel-debug.c \
+ drm/cairo-drm-intel-surface.c \
+ drm/cairo-drm-i915-surface.c \
+ drm/cairo-drm-i915-glyphs.c \
+ drm/cairo-drm-i915-shader.c \
+ drm/cairo-drm-i915-spans.c \
+ drm/cairo-drm-i965-surface.c \
+ drm/cairo-drm-i965-glyphs.c \
+ drm/cairo-drm-i965-shader.c \
+ drm/cairo-drm-i965-spans.c \
+ drm/cairo-drm-intel-brw-eu.c \
+ drm/cairo-drm-intel-brw-eu-emit.c \
+ drm/cairo-drm-intel-brw-eu-util.c \
+ drm/cairo-drm-radeon.c \
+ drm/cairo-drm-radeon-surface.c
+cairo_gallium_sources = drm/cairo-drm-gallium-surface.c
+
+cairo_script_headers = cairo-script.h
+cairo_script_private = cairo-script-private.h
+cairo_script_sources = cairo-script-surface.c
+
+cairo_tee_headers = cairo-tee.h
+cairo_tee_private = cairo-tee-surface-private.h
+cairo_tee_sources = cairo-tee-surface.c
+
+cairo_xml_headers = cairo-xml.h
+cairo_xml_sources = cairo-xml-surface.c
+
+cairo_vg_headers = cairo-vg.h
+cairo_vg_sources = cairo-vg-surface.c
+
+cairo_cogl_headers = cairo-cogl.h
+cairo_cogl_private = cairo-cogl-private.h \
+ cairo-cogl-gradient-private.h \
+ cairo-cogl-context-private.h \
+ cairo-cogl-utils-private.h
+cairo_cogl_sources = cairo-cogl-surface.c \
+ cairo-cogl-gradient.c \
+ cairo-cogl-context.c \
+ cairo-cogl-utils.c
+
+cairo_tg_headers = cairo-tg.h
+cairo_tg_private = cairo-tg-private.h \
+ cairo-tg-allocator-private.h \
+ cairo-tg-journal-private.h \
+ cairo-tg-composite-extents-private.h
+cairo_tg_sources = cairo-tg-surface.c \
+ cairo-tg-journal.c
diff --git a/src/Makefile.win32 b/src/Makefile.win32
new file mode 100755
index 000000000..864791f37
--- /dev/null
+++ b/src/Makefile.win32
@@ -0,0 +1,29 @@
+top_srcdir = ..
+include $(top_srcdir)/build/Makefile.win32.common
+include Makefile.win32.features
+
+SOURCES = $(enabled_cairo_sources)
+
+STATIC_SOURCES = cairo-system.c
+
+OBJECTS = $(patsubst %.c, $(CFG)/%.obj, $(SOURCES))
+OBJECTS_STATIC = $(patsubst %cairo-system.obj, %cairo-system-static.obj, $(OBJECTS))
+
+static: inform $(CFG)/cairo-static.lib
+dynamic: inform $(CFG)/cairo.dll
+
+$(CFG)/cairo.dll: $(OBJECTS)
+ @$(LD) $(CAIRO_LDFLAGS) -DLL -OUT:$@ $(CAIRO_LIBS) $(PIXMAN_LIBS) $(OBJECTS)
+
+$(CFG)/cairo-static.lib: $(OBJECTS_STATIC)
+ @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(PIXMAN_LIBS) $(OBJECTS_STATIC)
+
+all: inform $(CFG)/cairo.dll $(CFG)/cairo-static.lib
+ @echo "Built successfully!"
+ @echo "You should copy the following files to a proper place now:"
+ @echo ""
+ @echo " cairo-version.h (NOTE: toplevel, not the src/cairo-version.h one!)"
+ @echo " src/cairo-features.h"
+ @for x in $(enabled_cairo_headers); do echo " src/$$x"; done
+ @echo " src/$(CFG)/cairo.dll"
+ @echo " src/$(CFG)/cairo-static.lib"
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
new file mode 100755
index 000000000..268117094
--- /dev/null
+++ b/src/Makefile.win32.features
@@ -0,0 +1,736 @@
+# Generated by configure. Do not edit.
+
+ifeq ($(top_srcdir),)
+include Makefile.sources
+else
+include $(top_srcdir)/src/Makefile.sources
+endif
+
+supported_cairo_headers = $(cairo_headers)
+unsupported_cairo_headers =
+all_cairo_headers = $(cairo_headers)
+all_cairo_private = $(cairo_private)
+all_cairo_cxx_sources = $(cairo_cxx_sources)
+all_cairo_sources = $(cairo_sources)
+
+enabled_cairo_headers = $(cairo_headers)
+enabled_cairo_private = $(cairo_private)
+enabled_cairo_cxx_sources = $(cairo_cxx_sources)
+enabled_cairo_sources = $(cairo_sources)
+
+all_cairo_pkgconf = cairo.pc
+enabled_cairo_pkgconf = cairo.pc
+
+all_cairo_private += $(cairo_tls_private) $(cairo_tls_headers)
+all_cairo_cxx_sources += $(cairo_tls_cxx_sources)
+all_cairo_sources += $(cairo_tls_sources)
+ifeq ($(CAIRO_HAS_TLS),1)
+enabled_cairo_private += $(cairo_tls_private) $(cairo_tls_headers)
+enabled_cairo_cxx_sources += $(cairo_tls_cxx_sources)
+enabled_cairo_sources += $(cairo_tls_sources)
+endif
+
+all_cairo_private += $(cairo_pthread_setspecific_private) $(cairo_pthread_setspecific_headers)
+all_cairo_cxx_sources += $(cairo_pthread_setspecific_cxx_sources)
+all_cairo_sources += $(cairo_pthread_setspecific_sources)
+ifeq ($(CAIRO_HAS_PTHREAD_SETSPECIFIC),1)
+enabled_cairo_private += $(cairo_pthread_setspecific_private) $(cairo_pthread_setspecific_headers)
+enabled_cairo_cxx_sources += $(cairo_pthread_setspecific_cxx_sources)
+enabled_cairo_sources += $(cairo_pthread_setspecific_sources)
+endif
+
+supported_cairo_headers += $(cairo_xlib_headers)
+all_cairo_headers += $(cairo_xlib_headers)
+all_cairo_private += $(cairo_xlib_private)
+all_cairo_cxx_sources += $(cairo_xlib_cxx_sources)
+all_cairo_sources += $(cairo_xlib_sources)
+ifeq ($(CAIRO_HAS_XLIB_SURFACE),1)
+enabled_cairo_headers += $(cairo_xlib_headers)
+enabled_cairo_private += $(cairo_xlib_private)
+enabled_cairo_cxx_sources += $(cairo_xlib_cxx_sources)
+enabled_cairo_sources += $(cairo_xlib_sources)
+endif
+all_cairo_pkgconf += cairo-xlib.pc
+ifeq ($(CAIRO_HAS_XLIB_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xlib.pc
+endif
+
+supported_cairo_headers += $(cairo_xlib_xrender_headers)
+all_cairo_headers += $(cairo_xlib_xrender_headers)
+all_cairo_private += $(cairo_xlib_xrender_private)
+all_cairo_cxx_sources += $(cairo_xlib_xrender_cxx_sources)
+all_cairo_sources += $(cairo_xlib_xrender_sources)
+ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1)
+enabled_cairo_headers += $(cairo_xlib_xrender_headers)
+enabled_cairo_private += $(cairo_xlib_xrender_private)
+enabled_cairo_cxx_sources += $(cairo_xlib_xrender_cxx_sources)
+enabled_cairo_sources += $(cairo_xlib_xrender_sources)
+endif
+all_cairo_pkgconf += cairo-xlib-xrender.pc
+ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xlib-xrender.pc
+endif
+
+supported_cairo_headers += $(cairo_xcb_headers)
+all_cairo_headers += $(cairo_xcb_headers)
+all_cairo_private += $(cairo_xcb_private)
+all_cairo_cxx_sources += $(cairo_xcb_cxx_sources)
+all_cairo_sources += $(cairo_xcb_sources)
+ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
+enabled_cairo_headers += $(cairo_xcb_headers)
+enabled_cairo_private += $(cairo_xcb_private)
+enabled_cairo_cxx_sources += $(cairo_xcb_cxx_sources)
+enabled_cairo_sources += $(cairo_xcb_sources)
+endif
+all_cairo_pkgconf += cairo-xcb.pc
+ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xcb.pc
+endif
+
+unsupported_cairo_headers += $(cairo_xlib_xcb_headers)
+all_cairo_headers += $(cairo_xlib_xcb_headers)
+all_cairo_private += $(cairo_xlib_xcb_private)
+all_cairo_cxx_sources += $(cairo_xlib_xcb_cxx_sources)
+all_cairo_sources += $(cairo_xlib_xcb_sources)
+ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_xlib_xcb_headers)
+enabled_cairo_private += $(cairo_xlib_xcb_private)
+enabled_cairo_cxx_sources += $(cairo_xlib_xcb_cxx_sources)
+enabled_cairo_sources += $(cairo_xlib_xcb_sources)
+endif
+all_cairo_pkgconf += cairo-xlib-xcb.pc
+ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-xlib-xcb.pc
+endif
+
+supported_cairo_headers += $(cairo_xcb_shm_headers)
+all_cairo_headers += $(cairo_xcb_shm_headers)
+all_cairo_private += $(cairo_xcb_shm_private)
+all_cairo_cxx_sources += $(cairo_xcb_shm_cxx_sources)
+all_cairo_sources += $(cairo_xcb_shm_sources)
+ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_xcb_shm_headers)
+enabled_cairo_private += $(cairo_xcb_shm_private)
+enabled_cairo_cxx_sources += $(cairo_xcb_shm_cxx_sources)
+enabled_cairo_sources += $(cairo_xcb_shm_sources)
+endif
+all_cairo_pkgconf += cairo-xcb-shm.pc
+ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-xcb-shm.pc
+endif
+
+unsupported_cairo_headers += $(cairo_qt_headers)
+all_cairo_headers += $(cairo_qt_headers)
+all_cairo_private += $(cairo_qt_private)
+all_cairo_cxx_sources += $(cairo_qt_cxx_sources)
+all_cairo_sources += $(cairo_qt_sources)
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_headers += $(cairo_qt_headers)
+enabled_cairo_private += $(cairo_qt_private)
+enabled_cairo_cxx_sources += $(cairo_qt_cxx_sources)
+enabled_cairo_sources += $(cairo_qt_sources)
+endif
+all_cairo_pkgconf += cairo-qt.pc
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_pkgconf += cairo-qt.pc
+endif
+
+supported_cairo_headers += $(cairo_quartz_headers)
+all_cairo_headers += $(cairo_quartz_headers)
+all_cairo_private += $(cairo_quartz_private)
+all_cairo_cxx_sources += $(cairo_quartz_cxx_sources)
+all_cairo_sources += $(cairo_quartz_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1)
+enabled_cairo_headers += $(cairo_quartz_headers)
+enabled_cairo_private += $(cairo_quartz_private)
+enabled_cairo_cxx_sources += $(cairo_quartz_cxx_sources)
+enabled_cairo_sources += $(cairo_quartz_sources)
+endif
+all_cairo_pkgconf += cairo-quartz.pc
+ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1)
+enabled_cairo_pkgconf += cairo-quartz.pc
+endif
+
+supported_cairo_headers += $(cairo_quartz_font_headers)
+all_cairo_headers += $(cairo_quartz_font_headers)
+all_cairo_private += $(cairo_quartz_font_private)
+all_cairo_cxx_sources += $(cairo_quartz_font_cxx_sources)
+all_cairo_sources += $(cairo_quartz_font_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_FONT),1)
+enabled_cairo_headers += $(cairo_quartz_font_headers)
+enabled_cairo_private += $(cairo_quartz_font_private)
+enabled_cairo_cxx_sources += $(cairo_quartz_font_cxx_sources)
+enabled_cairo_sources += $(cairo_quartz_font_sources)
+endif
+all_cairo_pkgconf += cairo-quartz-font.pc
+ifeq ($(CAIRO_HAS_QUARTZ_FONT),1)
+enabled_cairo_pkgconf += cairo-quartz-font.pc
+endif
+
+unsupported_cairo_headers += $(cairo_quartz_image_headers)
+all_cairo_headers += $(cairo_quartz_image_headers)
+all_cairo_private += $(cairo_quartz_image_private)
+all_cairo_cxx_sources += $(cairo_quartz_image_cxx_sources)
+all_cairo_sources += $(cairo_quartz_image_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1)
+enabled_cairo_headers += $(cairo_quartz_image_headers)
+enabled_cairo_private += $(cairo_quartz_image_private)
+enabled_cairo_cxx_sources += $(cairo_quartz_image_cxx_sources)
+enabled_cairo_sources += $(cairo_quartz_image_sources)
+endif
+all_cairo_pkgconf += cairo-quartz-image.pc
+ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1)
+enabled_cairo_pkgconf += cairo-quartz-image.pc
+endif
+
+supported_cairo_headers += $(cairo_win32_headers)
+all_cairo_headers += $(cairo_win32_headers)
+all_cairo_private += $(cairo_win32_private)
+all_cairo_cxx_sources += $(cairo_win32_cxx_sources)
+all_cairo_sources += $(cairo_win32_sources)
+ifeq ($(CAIRO_HAS_WIN32_SURFACE),1)
+enabled_cairo_headers += $(cairo_win32_headers)
+enabled_cairo_private += $(cairo_win32_private)
+enabled_cairo_cxx_sources += $(cairo_win32_cxx_sources)
+enabled_cairo_sources += $(cairo_win32_sources)
+endif
+all_cairo_pkgconf += cairo-win32.pc
+ifeq ($(CAIRO_HAS_WIN32_SURFACE),1)
+enabled_cairo_pkgconf += cairo-win32.pc
+endif
+
+supported_cairo_headers += $(cairo_win32_font_headers)
+all_cairo_headers += $(cairo_win32_font_headers)
+all_cairo_private += $(cairo_win32_font_private)
+all_cairo_cxx_sources += $(cairo_win32_font_cxx_sources)
+all_cairo_sources += $(cairo_win32_font_sources)
+ifeq ($(CAIRO_HAS_WIN32_FONT),1)
+enabled_cairo_headers += $(cairo_win32_font_headers)
+enabled_cairo_private += $(cairo_win32_font_private)
+enabled_cairo_cxx_sources += $(cairo_win32_font_cxx_sources)
+enabled_cairo_sources += $(cairo_win32_font_sources)
+endif
+all_cairo_pkgconf += cairo-win32-font.pc
+ifeq ($(CAIRO_HAS_WIN32_FONT),1)
+enabled_cairo_pkgconf += cairo-win32-font.pc
+endif
+
+unsupported_cairo_headers += $(cairo_skia_headers)
+all_cairo_headers += $(cairo_skia_headers)
+all_cairo_private += $(cairo_skia_private)
+all_cairo_cxx_sources += $(cairo_skia_cxx_sources)
+all_cairo_sources += $(cairo_skia_sources)
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+enabled_cairo_headers += $(cairo_skia_headers)
+enabled_cairo_private += $(cairo_skia_private)
+enabled_cairo_cxx_sources += $(cairo_skia_cxx_sources)
+enabled_cairo_sources += $(cairo_skia_sources)
+endif
+all_cairo_pkgconf += cairo-skia.pc
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+enabled_cairo_pkgconf += cairo-skia.pc
+endif
+
+unsupported_cairo_headers += $(cairo_os2_headers)
+all_cairo_headers += $(cairo_os2_headers)
+all_cairo_private += $(cairo_os2_private)
+all_cairo_cxx_sources += $(cairo_os2_cxx_sources)
+all_cairo_sources += $(cairo_os2_sources)
+ifeq ($(CAIRO_HAS_OS2_SURFACE),1)
+enabled_cairo_headers += $(cairo_os2_headers)
+enabled_cairo_private += $(cairo_os2_private)
+enabled_cairo_cxx_sources += $(cairo_os2_cxx_sources)
+enabled_cairo_sources += $(cairo_os2_sources)
+endif
+all_cairo_pkgconf += cairo-os2.pc
+ifeq ($(CAIRO_HAS_OS2_SURFACE),1)
+enabled_cairo_pkgconf += cairo-os2.pc
+endif
+
+unsupported_cairo_headers += $(cairo_beos_headers)
+all_cairo_headers += $(cairo_beos_headers)
+all_cairo_private += $(cairo_beos_private)
+all_cairo_cxx_sources += $(cairo_beos_cxx_sources)
+all_cairo_sources += $(cairo_beos_sources)
+ifeq ($(CAIRO_HAS_BEOS_SURFACE),1)
+enabled_cairo_headers += $(cairo_beos_headers)
+enabled_cairo_private += $(cairo_beos_private)
+enabled_cairo_cxx_sources += $(cairo_beos_cxx_sources)
+enabled_cairo_sources += $(cairo_beos_sources)
+endif
+all_cairo_pkgconf += cairo-beos.pc
+ifeq ($(CAIRO_HAS_BEOS_SURFACE),1)
+enabled_cairo_pkgconf += cairo-beos.pc
+endif
+
+unsupported_cairo_headers += $(cairo_drm_headers)
+all_cairo_headers += $(cairo_drm_headers)
+all_cairo_private += $(cairo_drm_private)
+all_cairo_cxx_sources += $(cairo_drm_cxx_sources)
+all_cairo_sources += $(cairo_drm_sources)
+ifeq ($(CAIRO_HAS_DRM_SURFACE),1)
+enabled_cairo_headers += $(cairo_drm_headers)
+enabled_cairo_private += $(cairo_drm_private)
+enabled_cairo_cxx_sources += $(cairo_drm_cxx_sources)
+enabled_cairo_sources += $(cairo_drm_sources)
+endif
+all_cairo_pkgconf += cairo-drm.pc
+ifeq ($(CAIRO_HAS_DRM_SURFACE),1)
+enabled_cairo_pkgconf += cairo-drm.pc
+endif
+
+unsupported_cairo_headers += $(cairo_gallium_headers)
+all_cairo_headers += $(cairo_gallium_headers)
+all_cairo_private += $(cairo_gallium_private)
+all_cairo_cxx_sources += $(cairo_gallium_cxx_sources)
+all_cairo_sources += $(cairo_gallium_sources)
+ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1)
+enabled_cairo_headers += $(cairo_gallium_headers)
+enabled_cairo_private += $(cairo_gallium_private)
+enabled_cairo_cxx_sources += $(cairo_gallium_cxx_sources)
+enabled_cairo_sources += $(cairo_gallium_sources)
+endif
+all_cairo_pkgconf += cairo-gallium.pc
+ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1)
+enabled_cairo_pkgconf += cairo-gallium.pc
+endif
+
+supported_cairo_headers += $(cairo_png_headers)
+all_cairo_headers += $(cairo_png_headers)
+all_cairo_private += $(cairo_png_private)
+all_cairo_cxx_sources += $(cairo_png_cxx_sources)
+all_cairo_sources += $(cairo_png_sources)
+ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_png_headers)
+enabled_cairo_private += $(cairo_png_private)
+enabled_cairo_cxx_sources += $(cairo_png_cxx_sources)
+enabled_cairo_sources += $(cairo_png_sources)
+endif
+all_cairo_pkgconf += cairo-png.pc
+ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-png.pc
+endif
+
+unsupported_cairo_headers += $(cairo_gl_headers)
+all_cairo_headers += $(cairo_gl_headers)
+all_cairo_private += $(cairo_gl_private)
+all_cairo_cxx_sources += $(cairo_gl_cxx_sources)
+all_cairo_sources += $(cairo_gl_sources)
+ifeq ($(CAIRO_HAS_GL_SURFACE),1)
+enabled_cairo_headers += $(cairo_gl_headers)
+enabled_cairo_private += $(cairo_gl_private)
+enabled_cairo_cxx_sources += $(cairo_gl_cxx_sources)
+enabled_cairo_sources += $(cairo_gl_sources)
+endif
+all_cairo_pkgconf += cairo-gl.pc
+ifeq ($(CAIRO_HAS_GL_SURFACE),1)
+enabled_cairo_pkgconf += cairo-gl.pc
+endif
+
+unsupported_cairo_headers += $(cairo_evasgl_headers)
+all_cairo_headers += $(cairo_evasgl_headers)
+all_cairo_private += $(cairo_evasgl_private)
+all_cairo_cxx_sources += $(cairo_evasgl_cxx_sources)
+all_cairo_sources += $(cairo_evasgl_sources)
+ifeq ($(CAIRO_HAS_EVASGL_SURFACE),1)
+enabled_cairo_headers += $(cairo_evasgl_headers)
+enabled_cairo_private += $(cairo_evasgl_private)
+enabled_cairo_cxx_sources += $(cairo_evasgl_cxx_sources)
+enabled_cairo_sources += $(cairo_evasgl_sources)
+endif
+all_cairo_pkgconf += cairo-evasgl.pc
+ifeq ($(CAIRO_HAS_EVASGL_SURFACE),1)
+enabled_cairo_pkgconf += cairo-evasgl.pc
+endif
+
+unsupported_cairo_headers += $(cairo_glesv2_headers)
+all_cairo_headers += $(cairo_glesv2_headers)
+all_cairo_private += $(cairo_glesv2_private)
+all_cairo_cxx_sources += $(cairo_glesv2_cxx_sources)
+all_cairo_sources += $(cairo_glesv2_sources)
+ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
+enabled_cairo_headers += $(cairo_glesv2_headers)
+enabled_cairo_private += $(cairo_glesv2_private)
+enabled_cairo_cxx_sources += $(cairo_glesv2_cxx_sources)
+enabled_cairo_sources += $(cairo_glesv2_sources)
+endif
+all_cairo_pkgconf += cairo-glesv2.pc
+ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
+enabled_cairo_pkgconf += cairo-glesv2.pc
+endif
+
+unsupported_cairo_headers += $(cairo_glesv3_headers)
+all_cairo_headers += $(cairo_glesv3_headers)
+all_cairo_private += $(cairo_glesv3_private)
+all_cairo_cxx_sources += $(cairo_glesv3_cxx_sources)
+all_cairo_sources += $(cairo_glesv3_sources)
+ifeq ($(CAIRO_HAS_GLESV3_SURFACE),1)
+enabled_cairo_headers += $(cairo_glesv3_headers)
+enabled_cairo_private += $(cairo_glesv3_private)
+enabled_cairo_cxx_sources += $(cairo_glesv3_cxx_sources)
+enabled_cairo_sources += $(cairo_glesv3_sources)
+endif
+all_cairo_pkgconf += cairo-glesv3.pc
+ifeq ($(CAIRO_HAS_GLESV3_SURFACE),1)
+enabled_cairo_pkgconf += cairo-glesv3.pc
+endif
+
+unsupported_cairo_headers += $(cairo_cogl_headers)
+all_cairo_headers += $(cairo_cogl_headers)
+all_cairo_private += $(cairo_cogl_private)
+all_cairo_cxx_sources += $(cairo_cogl_cxx_sources)
+all_cairo_sources += $(cairo_cogl_sources)
+ifeq ($(CAIRO_HAS_COGL_SURFACE),1)
+enabled_cairo_headers += $(cairo_cogl_headers)
+enabled_cairo_private += $(cairo_cogl_private)
+enabled_cairo_cxx_sources += $(cairo_cogl_cxx_sources)
+enabled_cairo_sources += $(cairo_cogl_sources)
+endif
+all_cairo_pkgconf += cairo-cogl.pc
+ifeq ($(CAIRO_HAS_COGL_SURFACE),1)
+enabled_cairo_pkgconf += cairo-cogl.pc
+endif
+
+unsupported_cairo_headers += $(cairo_directfb_headers)
+all_cairo_headers += $(cairo_directfb_headers)
+all_cairo_private += $(cairo_directfb_private)
+all_cairo_cxx_sources += $(cairo_directfb_cxx_sources)
+all_cairo_sources += $(cairo_directfb_sources)
+ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
+enabled_cairo_headers += $(cairo_directfb_headers)
+enabled_cairo_private += $(cairo_directfb_private)
+enabled_cairo_cxx_sources += $(cairo_directfb_cxx_sources)
+enabled_cairo_sources += $(cairo_directfb_sources)
+endif
+all_cairo_pkgconf += cairo-directfb.pc
+ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
+enabled_cairo_pkgconf += cairo-directfb.pc
+endif
+
+unsupported_cairo_headers += $(cairo_tg_headers)
+all_cairo_headers += $(cairo_tg_headers)
+all_cairo_private += $(cairo_tg_private)
+all_cairo_cxx_sources += $(cairo_tg_cxx_sources)
+all_cairo_sources += $(cairo_tg_sources)
+ifeq ($(CAIRO_HAS_TG_SURFACE),1)
+enabled_cairo_headers += $(cairo_tg_headers)
+enabled_cairo_private += $(cairo_tg_private)
+enabled_cairo_cxx_sources += $(cairo_tg_cxx_sources)
+enabled_cairo_sources += $(cairo_tg_sources)
+endif
+all_cairo_pkgconf += cairo-tg.pc
+ifeq ($(CAIRO_HAS_TG_SURFACE),1)
+enabled_cairo_pkgconf += cairo-tg.pc
+endif
+
+unsupported_cairo_headers += $(cairo_vg_headers)
+all_cairo_headers += $(cairo_vg_headers)
+all_cairo_private += $(cairo_vg_private)
+all_cairo_cxx_sources += $(cairo_vg_cxx_sources)
+all_cairo_sources += $(cairo_vg_sources)
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_headers += $(cairo_vg_headers)
+enabled_cairo_private += $(cairo_vg_private)
+enabled_cairo_cxx_sources += $(cairo_vg_cxx_sources)
+enabled_cairo_sources += $(cairo_vg_sources)
+endif
+all_cairo_pkgconf += cairo-vg.pc
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_pkgconf += cairo-vg.pc
+endif
+
+supported_cairo_headers += $(cairo_egl_headers)
+all_cairo_headers += $(cairo_egl_headers)
+all_cairo_private += $(cairo_egl_private)
+all_cairo_cxx_sources += $(cairo_egl_cxx_sources)
+all_cairo_sources += $(cairo_egl_sources)
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_egl_headers)
+enabled_cairo_private += $(cairo_egl_private)
+enabled_cairo_cxx_sources += $(cairo_egl_cxx_sources)
+enabled_cairo_sources += $(cairo_egl_sources)
+endif
+all_cairo_pkgconf += cairo-egl.pc
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-egl.pc
+endif
+
+supported_cairo_headers += $(cairo_glx_headers)
+all_cairo_headers += $(cairo_glx_headers)
+all_cairo_private += $(cairo_glx_private)
+all_cairo_cxx_sources += $(cairo_glx_cxx_sources)
+all_cairo_sources += $(cairo_glx_sources)
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_glx_headers)
+enabled_cairo_private += $(cairo_glx_private)
+enabled_cairo_cxx_sources += $(cairo_glx_cxx_sources)
+enabled_cairo_sources += $(cairo_glx_sources)
+endif
+all_cairo_pkgconf += cairo-glx.pc
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-glx.pc
+endif
+
+supported_cairo_headers += $(cairo_wgl_headers)
+all_cairo_headers += $(cairo_wgl_headers)
+all_cairo_private += $(cairo_wgl_private)
+all_cairo_cxx_sources += $(cairo_wgl_cxx_sources)
+all_cairo_sources += $(cairo_wgl_sources)
+ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_wgl_headers)
+enabled_cairo_private += $(cairo_wgl_private)
+enabled_cairo_cxx_sources += $(cairo_wgl_cxx_sources)
+enabled_cairo_sources += $(cairo_wgl_sources)
+endif
+all_cairo_pkgconf += cairo-wgl.pc
+ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-wgl.pc
+endif
+
+supported_cairo_headers += $(cairo_script_headers)
+all_cairo_headers += $(cairo_script_headers)
+all_cairo_private += $(cairo_script_private)
+all_cairo_cxx_sources += $(cairo_script_cxx_sources)
+all_cairo_sources += $(cairo_script_sources)
+ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1)
+enabled_cairo_headers += $(cairo_script_headers)
+enabled_cairo_private += $(cairo_script_private)
+enabled_cairo_cxx_sources += $(cairo_script_cxx_sources)
+enabled_cairo_sources += $(cairo_script_sources)
+endif
+all_cairo_pkgconf += cairo-script.pc
+ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1)
+enabled_cairo_pkgconf += cairo-script.pc
+endif
+
+supported_cairo_headers += $(cairo_ft_headers)
+all_cairo_headers += $(cairo_ft_headers)
+all_cairo_private += $(cairo_ft_private)
+all_cairo_cxx_sources += $(cairo_ft_cxx_sources)
+all_cairo_sources += $(cairo_ft_sources)
+ifeq ($(CAIRO_HAS_FT_FONT),1)
+enabled_cairo_headers += $(cairo_ft_headers)
+enabled_cairo_private += $(cairo_ft_private)
+enabled_cairo_cxx_sources += $(cairo_ft_cxx_sources)
+enabled_cairo_sources += $(cairo_ft_sources)
+endif
+all_cairo_pkgconf += cairo-ft.pc
+ifeq ($(CAIRO_HAS_FT_FONT),1)
+enabled_cairo_pkgconf += cairo-ft.pc
+endif
+
+supported_cairo_headers += $(cairo_fc_headers)
+all_cairo_headers += $(cairo_fc_headers)
+all_cairo_private += $(cairo_fc_private)
+all_cairo_cxx_sources += $(cairo_fc_cxx_sources)
+all_cairo_sources += $(cairo_fc_sources)
+ifeq ($(CAIRO_HAS_FC_FONT),1)
+enabled_cairo_headers += $(cairo_fc_headers)
+enabled_cairo_private += $(cairo_fc_private)
+enabled_cairo_cxx_sources += $(cairo_fc_cxx_sources)
+enabled_cairo_sources += $(cairo_fc_sources)
+endif
+all_cairo_pkgconf += cairo-fc.pc
+ifeq ($(CAIRO_HAS_FC_FONT),1)
+enabled_cairo_pkgconf += cairo-fc.pc
+endif
+
+supported_cairo_headers += $(cairo_ps_headers)
+all_cairo_headers += $(cairo_ps_headers)
+all_cairo_private += $(cairo_ps_private)
+all_cairo_cxx_sources += $(cairo_ps_cxx_sources)
+all_cairo_sources += $(cairo_ps_sources)
+ifeq ($(CAIRO_HAS_PS_SURFACE),1)
+enabled_cairo_headers += $(cairo_ps_headers)
+enabled_cairo_private += $(cairo_ps_private)
+enabled_cairo_cxx_sources += $(cairo_ps_cxx_sources)
+enabled_cairo_sources += $(cairo_ps_sources)
+endif
+all_cairo_pkgconf += cairo-ps.pc
+ifeq ($(CAIRO_HAS_PS_SURFACE),1)
+enabled_cairo_pkgconf += cairo-ps.pc
+endif
+
+supported_cairo_headers += $(cairo_pdf_headers)
+all_cairo_headers += $(cairo_pdf_headers)
+all_cairo_private += $(cairo_pdf_private)
+all_cairo_cxx_sources += $(cairo_pdf_cxx_sources)
+all_cairo_sources += $(cairo_pdf_sources)
+ifeq ($(CAIRO_HAS_PDF_SURFACE),1)
+enabled_cairo_headers += $(cairo_pdf_headers)
+enabled_cairo_private += $(cairo_pdf_private)
+enabled_cairo_cxx_sources += $(cairo_pdf_cxx_sources)
+enabled_cairo_sources += $(cairo_pdf_sources)
+endif
+all_cairo_pkgconf += cairo-pdf.pc
+ifeq ($(CAIRO_HAS_PDF_SURFACE),1)
+enabled_cairo_pkgconf += cairo-pdf.pc
+endif
+
+supported_cairo_headers += $(cairo_svg_headers)
+all_cairo_headers += $(cairo_svg_headers)
+all_cairo_private += $(cairo_svg_private)
+all_cairo_cxx_sources += $(cairo_svg_cxx_sources)
+all_cairo_sources += $(cairo_svg_sources)
+ifeq ($(CAIRO_HAS_SVG_SURFACE),1)
+enabled_cairo_headers += $(cairo_svg_headers)
+enabled_cairo_private += $(cairo_svg_private)
+enabled_cairo_cxx_sources += $(cairo_svg_cxx_sources)
+enabled_cairo_sources += $(cairo_svg_sources)
+endif
+all_cairo_pkgconf += cairo-svg.pc
+ifeq ($(CAIRO_HAS_SVG_SURFACE),1)
+enabled_cairo_pkgconf += cairo-svg.pc
+endif
+
+all_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers)
+all_cairo_cxx_sources += $(cairo_test_surfaces_cxx_sources)
+all_cairo_sources += $(cairo_test_surfaces_sources)
+ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
+enabled_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers)
+enabled_cairo_cxx_sources += $(cairo_test_surfaces_cxx_sources)
+enabled_cairo_sources += $(cairo_test_surfaces_sources)
+endif
+
+supported_cairo_headers += $(cairo_image_headers)
+all_cairo_headers += $(cairo_image_headers)
+all_cairo_private += $(cairo_image_private)
+all_cairo_cxx_sources += $(cairo_image_cxx_sources)
+all_cairo_sources += $(cairo_image_sources)
+enabled_cairo_headers += $(cairo_image_headers)
+enabled_cairo_private += $(cairo_image_private)
+enabled_cairo_cxx_sources += $(cairo_image_cxx_sources)
+enabled_cairo_sources += $(cairo_image_sources)
+
+supported_cairo_headers += $(cairo_mime_headers)
+all_cairo_headers += $(cairo_mime_headers)
+all_cairo_private += $(cairo_mime_private)
+all_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+all_cairo_sources += $(cairo_mime_sources)
+enabled_cairo_headers += $(cairo_mime_headers)
+enabled_cairo_private += $(cairo_mime_private)
+enabled_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+enabled_cairo_sources += $(cairo_mime_sources)
+
+supported_cairo_headers += $(cairo_recording_headers)
+all_cairo_headers += $(cairo_recording_headers)
+all_cairo_private += $(cairo_recording_private)
+all_cairo_cxx_sources += $(cairo_recording_cxx_sources)
+all_cairo_sources += $(cairo_recording_sources)
+enabled_cairo_headers += $(cairo_recording_headers)
+enabled_cairo_private += $(cairo_recording_private)
+enabled_cairo_cxx_sources += $(cairo_recording_cxx_sources)
+enabled_cairo_sources += $(cairo_recording_sources)
+
+supported_cairo_headers += $(cairo_observer_headers)
+all_cairo_headers += $(cairo_observer_headers)
+all_cairo_private += $(cairo_observer_private)
+all_cairo_cxx_sources += $(cairo_observer_cxx_sources)
+all_cairo_sources += $(cairo_observer_sources)
+enabled_cairo_headers += $(cairo_observer_headers)
+enabled_cairo_private += $(cairo_observer_private)
+enabled_cairo_cxx_sources += $(cairo_observer_cxx_sources)
+enabled_cairo_sources += $(cairo_observer_sources)
+
+unsupported_cairo_headers += $(cairo_tee_headers)
+all_cairo_headers += $(cairo_tee_headers)
+all_cairo_private += $(cairo_tee_private)
+all_cairo_cxx_sources += $(cairo_tee_cxx_sources)
+all_cairo_sources += $(cairo_tee_sources)
+ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
+enabled_cairo_headers += $(cairo_tee_headers)
+enabled_cairo_private += $(cairo_tee_private)
+enabled_cairo_cxx_sources += $(cairo_tee_cxx_sources)
+enabled_cairo_sources += $(cairo_tee_sources)
+endif
+all_cairo_pkgconf += cairo-tee.pc
+ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
+enabled_cairo_pkgconf += cairo-tee.pc
+endif
+
+unsupported_cairo_headers += $(cairo_xml_headers)
+all_cairo_headers += $(cairo_xml_headers)
+all_cairo_private += $(cairo_xml_private)
+all_cairo_cxx_sources += $(cairo_xml_cxx_sources)
+all_cairo_sources += $(cairo_xml_sources)
+ifeq ($(CAIRO_HAS_XML_SURFACE),1)
+enabled_cairo_headers += $(cairo_xml_headers)
+enabled_cairo_private += $(cairo_xml_private)
+enabled_cairo_cxx_sources += $(cairo_xml_cxx_sources)
+enabled_cairo_sources += $(cairo_xml_sources)
+endif
+all_cairo_pkgconf += cairo-xml.pc
+ifeq ($(CAIRO_HAS_XML_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xml.pc
+endif
+
+supported_cairo_headers += $(cairo_user_headers)
+all_cairo_headers += $(cairo_user_headers)
+all_cairo_private += $(cairo_user_private)
+all_cairo_cxx_sources += $(cairo_user_cxx_sources)
+all_cairo_sources += $(cairo_user_sources)
+enabled_cairo_headers += $(cairo_user_headers)
+enabled_cairo_private += $(cairo_user_private)
+enabled_cairo_cxx_sources += $(cairo_user_cxx_sources)
+enabled_cairo_sources += $(cairo_user_sources)
+
+all_cairo_private += $(cairo_openmp_private) $(cairo_openmp_headers)
+all_cairo_cxx_sources += $(cairo_openmp_cxx_sources)
+all_cairo_sources += $(cairo_openmp_sources)
+ifeq ($(CAIRO_HAS_OPENMP),1)
+enabled_cairo_private += $(cairo_openmp_private) $(cairo_openmp_headers)
+enabled_cairo_cxx_sources += $(cairo_openmp_cxx_sources)
+enabled_cairo_sources += $(cairo_openmp_sources)
+endif
+
+all_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers)
+all_cairo_cxx_sources += $(cairo_pthread_cxx_sources)
+all_cairo_sources += $(cairo_pthread_sources)
+ifeq ($(CAIRO_HAS_PTHREAD),1)
+enabled_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers)
+enabled_cairo_cxx_sources += $(cairo_pthread_cxx_sources)
+enabled_cairo_sources += $(cairo_pthread_sources)
+endif
+
+supported_cairo_headers += $(cairo_gobject_headers)
+all_cairo_headers += $(cairo_gobject_headers)
+all_cairo_private += $(cairo_gobject_private)
+all_cairo_cxx_sources += $(cairo_gobject_cxx_sources)
+all_cairo_sources += $(cairo_gobject_sources)
+ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_gobject_headers)
+enabled_cairo_private += $(cairo_gobject_private)
+enabled_cairo_cxx_sources += $(cairo_gobject_cxx_sources)
+enabled_cairo_sources += $(cairo_gobject_sources)
+endif
+all_cairo_pkgconf += cairo-gobject.pc
+ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-gobject.pc
+endif
+
+all_cairo_private += $(cairo_trace_private) $(cairo_trace_headers)
+all_cairo_cxx_sources += $(cairo_trace_cxx_sources)
+all_cairo_sources += $(cairo_trace_sources)
+ifeq ($(CAIRO_HAS_TRACE),1)
+enabled_cairo_private += $(cairo_trace_private) $(cairo_trace_headers)
+enabled_cairo_cxx_sources += $(cairo_trace_cxx_sources)
+enabled_cairo_sources += $(cairo_trace_sources)
+endif
+
+all_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers)
+all_cairo_cxx_sources += $(cairo_interpreter_cxx_sources)
+all_cairo_sources += $(cairo_interpreter_sources)
+ifeq ($(CAIRO_HAS_INTERPRETER),1)
+enabled_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers)
+enabled_cairo_cxx_sources += $(cairo_interpreter_cxx_sources)
+enabled_cairo_sources += $(cairo_interpreter_sources)
+endif
+
+all_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers)
+all_cairo_cxx_sources += $(cairo_symbol_lookup_cxx_sources)
+all_cairo_sources += $(cairo_symbol_lookup_sources)
+ifeq ($(CAIRO_HAS_SYMBOL_LOOKUP),1)
+enabled_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers)
+enabled_cairo_cxx_sources += $(cairo_symbol_lookup_cxx_sources)
+enabled_cairo_sources += $(cairo_symbol_lookup_sources)
+endif
diff --git a/src/README b/src/README
new file mode 100755
index 000000000..03e4455ea
--- /dev/null
+++ b/src/README
@@ -0,0 +1,69 @@
+Cairo Library Source Code
+=========================
+
+This directory contains the source code of the cairo library.
+
+
+Source Code Listing
+-------------------
+
+The canonical list of source files is the file Makefile.sources. See that
+file for how it works.
+
+
+New Backends
+------------
+
+The rule of the thumb for adding new backends is to see how other
+backends are integrated. Pick one of the simpler, unsupported, backends
+and search the entire tree for it, and go from there.
+
+To add new backends you need to basically:
+
+ * Modify $(top_srcdir)/configure.in to add checks for your backend.
+
+ * Modify Makefile.sources to add source files for your backend,
+
+ * Modify $(top_srcdir)/boilerplate/ to add boilerplate code for
+ testing your new backend.
+
+
+New API
+-------
+
+After adding new API, run "make check" in this directory and fix any
+reported issues. Also add new API to the right location in
+$(top_srcdir)/doc/public/cairo-sections.txt and run "make check"
+in $(top_builddir)/doc/public to make sure that any newly added
+documentation is correctly hooked up.
+
+Do not forget to add tests for the new API. See next section.
+
+
+Tests
+-----
+
+There are some tests in this directory that check the source code and
+the build for various issues. The tests are very quick to run, and
+particularly should be run after any documentation or API changes. It
+does not hurt to run them after any source modification either. Run
+them simply by calling:
+
+ make check
+
+There are also extensive regression tests in $(top_srcdir)/test. It is
+a good idea to run that test suite for any changes made to the source
+code. Moreover, for any new feature, API, or bug fix, new tests should
+be added to the regression test suite to test the new code.
+
+
+Bibliography
+------------
+
+A detailed list of academic publications used in cairo code is available
+in the file $(top_srcdir)/BIBLIOGRAPHY. Feel free to update as you
+implement more papers.
+
+For more technical publications (eg. Adobe technical reports) just
+point them out in a comment in the header of the file implementing them.
+
diff --git a/src/cairo-analysis-surface-private.h b/src/cairo-analysis-surface-private.h
new file mode 100755
index 000000000..1e054c209
--- /dev/null
+++ b/src/cairo-analysis-surface-private.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2005 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#ifndef CAIRO_ANALYSIS_SURFACE_H
+#define CAIRO_ANALYSIS_SURFACE_H
+
+#include "cairoint.h"
+
+cairo_private cairo_surface_t *
+_cairo_analysis_surface_create (cairo_surface_t *target);
+
+cairo_private void
+_cairo_analysis_surface_set_ctm (cairo_surface_t *surface,
+ const cairo_matrix_t *ctm);
+
+cairo_private void
+_cairo_analysis_surface_get_ctm (cairo_surface_t *surface,
+ cairo_matrix_t *ctm);
+
+cairo_private cairo_region_t *
+_cairo_analysis_surface_get_supported (cairo_surface_t *surface);
+
+cairo_private cairo_region_t *
+_cairo_analysis_surface_get_unsupported (cairo_surface_t *surface);
+
+cairo_private cairo_bool_t
+_cairo_analysis_surface_has_supported (cairo_surface_t *surface);
+
+cairo_private cairo_bool_t
+_cairo_analysis_surface_has_unsupported (cairo_surface_t *surface);
+
+cairo_private void
+_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface,
+ cairo_box_t *bbox);
+
+cairo_private cairo_int_status_t
+_cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
+ cairo_int_status_t status_b);
+
+cairo_private cairo_surface_t *
+_cairo_null_surface_create (cairo_content_t content);
+
+#endif /* CAIRO_ANALYSIS_SURFACE_H */
diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
new file mode 100755
index 000000000..6717f7369
--- /dev/null
+++ b/src/cairo-analysis-surface.c
@@ -0,0 +1,938 @@
+/*
+ * 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-box-inline.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-inline.h"
+#include "cairo-region-private.h"
+
+typedef struct {
+ cairo_surface_t base;
+
+ cairo_surface_t *target;
+
+ cairo_bool_t first_op;
+ cairo_bool_t has_supported;
+ cairo_bool_t has_unsupported;
+
+ cairo_region_t supported_region;
+ cairo_region_t fallback_region;
+ cairo_box_t page_bbox;
+
+ cairo_bool_t has_ctm;
+ cairo_matrix_t ctm;
+
+} cairo_analysis_surface_t;
+
+cairo_int_status_t
+_cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
+ cairo_int_status_t status_b)
+{
+ /* fatal errors should be checked and propagated at source */
+ assert (! _cairo_int_status_is_error (status_a));
+ assert (! _cairo_int_status_is_error (status_b));
+
+ /* return the most important status */
+ if (status_a == CAIRO_INT_STATUS_UNSUPPORTED ||
+ status_b == CAIRO_INT_STATUS_UNSUPPORTED)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK ||
+ status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK)
+ return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+
+ if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN ||
+ status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+ return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+
+ if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
+ status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
+ return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+
+ /* at this point we have checked all the valid internal codes, so... */
+ assert (status_a == CAIRO_INT_STATUS_SUCCESS &&
+ status_b == CAIRO_INT_STATUS_SUCCESS);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+struct proxy {
+ cairo_surface_t base;
+ cairo_surface_t *target;
+};
+
+static cairo_status_t
+proxy_finish (void *abstract_surface)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t proxy_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+ proxy_finish,
+};
+
+static cairo_surface_t *
+attach_proxy (cairo_surface_t *source,
+ cairo_surface_t *target)
+{
+ struct proxy *proxy;
+
+ proxy = malloc (sizeof (*proxy));
+ if (unlikely (proxy == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&proxy->base, &proxy_backend, NULL, target->content);
+
+ proxy->target = target;
+ _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
+
+ return &proxy->base;
+}
+
+static void
+detach_proxy (cairo_surface_t *proxy)
+{
+ cairo_surface_finish (proxy);
+ cairo_surface_destroy (proxy);
+}
+
+static cairo_int_status_t
+_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ const cairo_surface_pattern_t *surface_pattern;
+ cairo_analysis_surface_t *tmp;
+ cairo_surface_t *source, *proxy;
+ cairo_matrix_t p2d;
+ cairo_status_t status, analysis_status;
+
+ assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
+ surface_pattern = (const cairo_surface_pattern_t *) pattern;
+ assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
+ source = surface_pattern->surface;
+
+ proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
+ if (proxy != NULL) {
+ /* nothing untoward found so far */
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ tmp = (cairo_analysis_surface_t *)
+ _cairo_analysis_surface_create (surface->target);
+ if (unlikely (tmp->base.status)) {
+ status = tmp->base.status;
+ cairo_surface_destroy (&tmp->base);
+ return status;
+ }
+
+ proxy = attach_proxy (source, &tmp->base);
+
+ p2d = pattern->matrix;
+ status = cairo_matrix_invert (&p2d);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm);
+ tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm);
+
+ source = _cairo_surface_get_source (source, NULL);
+ status = _cairo_recording_surface_replay_and_create_regions (source,
+ &tmp->base);
+ analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
+ detach_proxy (proxy);
+ cairo_surface_destroy (&tmp->base);
+
+ if (unlikely (status))
+ return status;
+
+ return analysis_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_INT_STATUS_SUCCESS ||
+ backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
+ backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+ else
+ {
+ return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+ }
+ }
+
+ _cairo_box_from_rectangle (&bbox, rect);
+
+ if (surface->has_ctm) {
+ int tx, ty;
+
+ if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) {
+ rect->x += tx;
+ rect->y += ty;
+
+ tx = _cairo_fixed_from_int (tx);
+ bbox.p1.x += tx;
+ bbox.p2.x += tx;
+
+ ty = _cairo_fixed_from_int (ty);
+ bbox.p1.y += ty;
+ bbox.p2.y += ty;
+ } else {
+ _cairo_matrix_transform_bounding_box_fixed (&surface->ctm,
+ &bbox, NULL);
+
+ if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) {
+ /* Even though the operation is not visible we must be
+ * careful to not allow unsupported operations to be
+ * replayed to the backend during
+ * CAIRO_PAGINATED_MODE_RENDER */
+ if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
+ backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
+ backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+ else
+ {
+ return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+ }
+ }
+
+ _cairo_box_round_to_rectangle (&bbox, rect);
+ }
+ }
+
+ if (surface->first_op) {
+ surface->first_op = FALSE;
+ surface->page_bbox = bbox;
+ } else
+ _cairo_box_add_box(&surface->page_bbox, &bbox);
+
+ /* If the operation is completely enclosed within the fallback
+ * region there is no benefit in emitting a native operation as
+ * the fallback image will be painted on top.
+ */
+ if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN)
+ return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+
+ if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) {
+ /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates
+ * that the backend only supports this operation if the
+ * transparency removed. If the extents of this operation does
+ * not intersect any other native operation, the operation is
+ * natively supported and the backend will blend the
+ * transparency into the white background.
+ */
+ if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT)
+ backend_status = CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (backend_status == CAIRO_INT_STATUS_SUCCESS) {
+ /* Add the operation to the supported region. Operations in
+ * this region will be emitted as native operations.
+ */
+ surface->has_supported = TRUE;
+ return cairo_region_union_rectangle (&surface->supported_region, rect);
+ }
+
+ /* Add the operation to the unsupported region. This region will
+ * be painted as an image after all native operations have been
+ * emitted.
+ */
+ surface->has_unsupported = TRUE;
+ status = cairo_region_union_rectangle (&surface->fallback_region, rect);
+
+ /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate
+ * unsupported operations to the recording surface as using
+ * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to
+ * invoke the cairo-surface-fallback path then return
+ * CAIRO_STATUS_SUCCESS.
+ */
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+ else
+ return status;
+}
+
+static cairo_status_t
+_cairo_analysis_surface_finish (void *abstract_surface)
+{
+ cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+ _cairo_region_fini (&surface->supported_region);
+ _cairo_region_fini (&surface->fallback_region);
+
+ cairo_surface_destroy (surface->target);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_analysis_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_get_extents (surface->target, rectangle);
+}
+
+static void
+_rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip)
+{
+ if (clip != NULL)
+ _cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip));
+}
+
+static void
+_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t is_empty;
+
+ is_empty = _cairo_surface_get_extents (&surface->base, extents);
+
+ if (_cairo_operator_bounded_by_source (op)) {
+ cairo_rectangle_int_t source_extents;
+
+ _cairo_pattern_get_extents (source, &source_extents);
+ _cairo_rectangle_intersect (extents, &source_extents);
+ }
+
+ _rectangle_intersect_clip (extents, clip);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_int_status_t backend_status;
+ cairo_rectangle_int_t extents;
+
+ if (surface->target->backend->paint == NULL) {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ backend_status =
+ surface->target->backend->paint (surface->target,
+ op, source, clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ 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,
+ const cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_int_status_t backend_status;
+ cairo_rectangle_int_t extents;
+
+ if (surface->target->backend->mask == NULL) {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ backend_status =
+ surface->target->backend->mask (surface->target,
+ op, source, mask, clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ 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) {
+ cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface;
+ src_surface = _cairo_surface_get_source (src_surface, NULL);
+ if (_cairo_surface_is_recording (src_surface)) {
+ backend_source_status =
+ _analyze_recording_surface_pattern (surface, source);
+ if (_cairo_int_status_is_error (backend_source_status))
+ return backend_source_status;
+ }
+ }
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->surface;
+ mask_surface = _cairo_surface_get_source (mask_surface, NULL);
+ if (_cairo_surface_is_recording (mask_surface)) {
+ backend_mask_status =
+ _analyze_recording_surface_pattern (surface, mask);
+ if (_cairo_int_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);
+ _cairo_rectangle_intersect (&extents, &mask_extents);
+ }
+
+ return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_int_status_t backend_status;
+ cairo_rectangle_int_t extents;
+
+ if (surface->target->backend->stroke == NULL) {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ backend_status =
+ surface->target->backend->stroke (surface->target, op,
+ source, path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ 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;
+ cairo_int_status_t status;
+
+ status = _cairo_path_fixed_stroke_extents (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &mask_extents);
+ if (unlikely (status))
+ return status;
+
+ _cairo_rectangle_intersect (&extents, &mask_extents);
+ }
+
+ return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_int_status_t backend_status;
+ cairo_rectangle_int_t extents;
+
+ if (surface->target->backend->fill == NULL) {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ backend_status =
+ surface->target->backend->fill (surface->target, op,
+ source, path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ 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;
+
+ _cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
+ &mask_extents);
+
+ _cairo_rectangle_intersect (&extents, &mask_extents);
+ }
+
+ return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_int_status_t status, backend_status;
+ cairo_rectangle_int_t extents, glyph_extents;
+
+ /* Adapted from _cairo_surface_show_glyphs */
+ if (surface->target->backend->show_glyphs != NULL) {
+ backend_status =
+ surface->target->backend->show_glyphs (surface->target, op,
+ source,
+ glyphs, num_glyphs,
+ scaled_font,
+ clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+ else if (surface->target->backend->show_text_glyphs != NULL)
+ {
+ backend_status =
+ surface->target->backend->show_text_glyphs (surface->target, op,
+ source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0,
+ FALSE,
+ scaled_font,
+ clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+ else
+ {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ 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;
+
+ _cairo_rectangle_intersect (&extents, &glyph_extents);
+ }
+
+ return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_bool_t
+_cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+
+ return cairo_surface_has_show_text_glyphs (surface->target);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_int_status_t status, backend_status;
+ cairo_rectangle_int_t extents, glyph_extents;
+
+ /* Adapted from _cairo_surface_show_glyphs */
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (surface->target->backend->show_text_glyphs != NULL) {
+ backend_status =
+ surface->target->backend->show_text_glyphs (surface->target, op,
+ source,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+ if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED &&
+ surface->target->backend->show_glyphs != NULL)
+ {
+ backend_status =
+ surface->target->backend->show_glyphs (surface->target, op,
+ source,
+ glyphs, num_glyphs,
+ scaled_font,
+ clip);
+ if (_cairo_int_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ 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;
+
+ _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,
+
+ _cairo_analysis_surface_finish,
+ NULL,
+
+ NULL, /* create_similar */
+ NULL, /* create_similar_image */
+ NULL, /* map_to_image */
+ NULL, /* unmap */
+
+ NULL, /* source */
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_analysis_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_analysis_surface_paint,
+ _cairo_analysis_surface_mask,
+ _cairo_analysis_surface_stroke,
+ _cairo_analysis_surface_fill,
+ NULL, /* fill_stroke */
+ _cairo_analysis_surface_show_glyphs,
+ _cairo_analysis_surface_has_show_text_glyphs,
+ _cairo_analysis_surface_show_text_glyphs
+};
+
+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,
+ const 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,
+ const cairo_clip_t *clip);
+
+typedef cairo_int_status_t
+(*_stroke_func) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+typedef cairo_int_status_t
+(*_fill_func) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+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,
+ const cairo_clip_t *clip);
+
+static const cairo_surface_backend_t cairo_null_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+ NULL, /* finish */
+
+ NULL, /* only accessed through the surface functions */
+
+ NULL, /* create_similar */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image*/
+
+ NULL, /* source */
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ NULL, /* get_extents */
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ (_paint_func) _return_success, /* paint */
+ (_mask_func) _return_success, /* mask */
+ (_stroke_func) _return_success, /* stroke */
+ (_fill_func) _return_success, /* fill */
+ NULL, /* fill_stroke */
+ (_show_glyphs_func) _return_success, /* show_glyphs */
+ NULL, /* has_show_text_glyphs */
+ NULL /* show_text_glyphs */
+};
+
+cairo_surface_t *
+_cairo_null_surface_create (cairo_content_t content)
+{
+ cairo_surface_t *surface;
+
+ surface = 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/src/cairo-arc-private.h b/src/cairo-arc-private.h
new file mode 100755
index 000000000..a22c01ac9
--- /dev/null
+++ b/src/cairo-arc-private.h
@@ -0,0 +1,61 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_ARC_PRIVATE_H
+#define CAIRO_ARC_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private void
+_cairo_arc_path (cairo_t *cr,
+ double xc,
+ double yc,
+ double radius,
+ double angle1,
+ double angle2);
+
+cairo_private void
+_cairo_arc_path_negative (cairo_t *cr,
+ double xc,
+ double yc,
+ double radius,
+ double angle1,
+ double angle2);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_ARC_PRIVATE_H */
diff --git a/src/cairo-arc.c b/src/cairo-arc.c
new file mode 100755
index 000000000..5cbd11238
--- /dev/null
+++ b/src/cairo-arc.c
@@ -0,0 +1,308 @@
+/* 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"
+
+#define MAX_FULL_CIRCLES 65536
+
+/* Spline deviation from the circle in radius would be given by:
+
+ error = sqrt (x**2 + y**2) - 1
+
+ A simpler error function to work with is:
+
+ e = x**2 + y**2 - 1
+
+ From "Good approximation of circles by curvature-continuous Bezier
+ curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
+ Design 8 (1990) 22-41, we learn:
+
+ abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
+
+ and
+ abs (error) =~ 1/2 * e
+
+ Of course, this error value applies only for the particular spline
+ approximation that is used in _cairo_gstate_arc_segment.
+*/
+static double
+_arc_error_normalized (double angle)
+{
+ return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
+}
+
+static double
+_arc_max_angle_for_tolerance_normalized (double tolerance)
+{
+ double angle, error;
+ int i;
+
+ /* Use table lookup to reduce search time in most cases. */
+ struct {
+ double angle;
+ double error;
+ } table[] = {
+ { M_PI / 1.0, 0.0185185185185185036127 },
+ { M_PI / 2.0, 0.000272567143730179811158 },
+ { M_PI / 3.0, 2.38647043651461047433e-05 },
+ { M_PI / 4.0, 4.2455377443222443279e-06 },
+ { M_PI / 5.0, 1.11281001494389081528e-06 },
+ { M_PI / 6.0, 3.72662000942734705475e-07 },
+ { M_PI / 7.0, 1.47783685574284411325e-07 },
+ { M_PI / 8.0, 6.63240432022601149057e-08 },
+ { M_PI / 9.0, 3.2715520137536980553e-08 },
+ { M_PI / 10.0, 1.73863223499021216974e-08 },
+ { M_PI / 11.0, 9.81410988043554039085e-09 },
+ };
+ int table_size = ARRAY_LENGTH (table);
+
+ for (i = 0; i < table_size; i++)
+ if (table[i].error < tolerance)
+ return table[i].angle;
+
+ ++i;
+ do {
+ angle = M_PI / i++;
+ error = _arc_error_normalized (angle);
+ } while (error > tolerance);
+
+ return angle;
+}
+
+static int
+_arc_segments_needed (double angle,
+ double radius,
+ cairo_matrix_t *ctm,
+ double tolerance)
+{
+ double major_axis, max_angle;
+
+ /* the error is amplified by at most the length of the
+ * major axis of the circle; see cairo-pen.c for a more detailed analysis
+ * of this. */
+ major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius);
+ max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis);
+
+ return ceil (fabs (angle) / max_angle);
+}
+
+/* We want to draw a single spline approximating a circular arc radius
+ R from angle A to angle B. Since we want a symmetric spline that
+ matches the endpoints of the arc in position and slope, we know
+ that the spline control points must be:
+
+ (R * cos(A), R * sin(A))
+ (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
+ (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
+ (R * cos(B), R * sin(B))
+
+ for some value of h.
+
+ "Approximation of circular arcs by cubic polynomials", Michael
+ Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
+ various values of h along with error analysis for each.
+
+ From that paper, a very practical value of h is:
+
+ h = 4/3 * R * tan(angle/4)
+
+ This value does not give the spline with minimal error, but it does
+ provide a very good approximation, (6th-order convergence), and the
+ error expression is quite simple, (see the comment for
+ _arc_error_normalized).
+*/
+static void
+_cairo_arc_segment (cairo_t *cr,
+ double xc,
+ double yc,
+ double radius,
+ double angle_A,
+ double angle_B)
+{
+ double r_sin_A, r_cos_A;
+ double r_sin_B, r_cos_B;
+ double h;
+
+ r_sin_A = radius * sin (angle_A);
+ r_cos_A = radius * cos (angle_A);
+ r_sin_B = radius * sin (angle_B);
+ r_cos_B = radius * cos (angle_B);
+
+ h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
+
+ cairo_curve_to (cr,
+ xc + r_cos_A - h * r_sin_A,
+ yc + r_sin_A + h * r_cos_A,
+ xc + r_cos_B + h * r_sin_B,
+ yc + r_sin_B - h * r_cos_B,
+ xc + r_cos_B,
+ yc + r_sin_B);
+}
+
+static void
+_cairo_arc_in_direction (cairo_t *cr,
+ double xc,
+ double yc,
+ double radius,
+ double angle_min,
+ double angle_max,
+ cairo_direction_t dir)
+{
+ if (cairo_status (cr))
+ return;
+
+ assert (angle_max >= angle_min);
+
+ if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) {
+ angle_max = fmod (angle_max - angle_min, 2 * M_PI);
+ angle_min = fmod (angle_min, 2 * M_PI);
+ angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES;
+ }
+
+ /* Recurse if drawing arc larger than pi */
+ if (angle_max - angle_min > M_PI) {
+ double angle_mid = angle_min + (angle_max - angle_min) / 2.0;
+ if (dir == CAIRO_DIRECTION_FORWARD) {
+ _cairo_arc_in_direction (cr, xc, yc, radius,
+ angle_min, angle_mid,
+ dir);
+
+ _cairo_arc_in_direction (cr, xc, yc, radius,
+ angle_mid, angle_max,
+ dir);
+ } else {
+ _cairo_arc_in_direction (cr, xc, yc, radius,
+ angle_mid, angle_max,
+ dir);
+
+ _cairo_arc_in_direction (cr, xc, yc, radius,
+ angle_min, angle_mid,
+ dir);
+ }
+ } else if (angle_max != angle_min) {
+ cairo_matrix_t ctm;
+ int i, segments;
+ double step;
+
+ cairo_get_matrix (cr, &ctm);
+ segments = _arc_segments_needed (angle_max - angle_min,
+ radius, &ctm,
+ cairo_get_tolerance (cr));
+ step = (angle_max - angle_min) / segments;
+ segments -= 1;
+
+ if (dir == CAIRO_DIRECTION_REVERSE) {
+ double t;
+
+ t = angle_min;
+ angle_min = angle_max;
+ angle_max = t;
+
+ step = -step;
+ }
+
+ for (i = 0; i < segments; i++, angle_min += step) {
+ _cairo_arc_segment (cr, xc, yc, radius,
+ angle_min, angle_min + step);
+ }
+
+ _cairo_arc_segment (cr, xc, yc, radius,
+ angle_min, angle_max);
+ } else {
+ cairo_line_to (cr,
+ xc + radius * cos (angle_min),
+ yc + radius * sin (angle_min));
+ }
+}
+
+/**
+ * _cairo_arc_path:
+ * @cr: a cairo context
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ *
+ * Compute a path for the given arc and append it onto the current
+ * path within @cr. The arc will be accurate within the current
+ * tolerance and given the current transformation.
+ **/
+void
+_cairo_arc_path (cairo_t *cr,
+ double xc,
+ double yc,
+ double radius,
+ double angle1,
+ double angle2)
+{
+ _cairo_arc_in_direction (cr, xc, yc,
+ radius,
+ angle1, angle2,
+ CAIRO_DIRECTION_FORWARD);
+}
+
+/**
+ * _cairo_arc_path_negative:
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ * @ctm: the current transformation matrix
+ * @tolerance: the current tolerance value
+ * @path: the path onto which the arc will be appended
+ *
+ * Compute a path for the given arc (defined in the negative
+ * direction) and append it onto the current path within @cr. The arc
+ * will be accurate within the current tolerance and given the current
+ * transformation.
+ **/
+void
+_cairo_arc_path_negative (cairo_t *cr,
+ double xc,
+ double yc,
+ double radius,
+ double angle1,
+ double angle2)
+{
+ _cairo_arc_in_direction (cr, xc, yc,
+ radius,
+ angle2, angle1,
+ CAIRO_DIRECTION_REVERSE);
+}
diff --git a/src/cairo-array-private.h b/src/cairo-array-private.h
new file mode 100755
index 000000000..35b29e5fc
--- /dev/null
+++ b/src/cairo-array-private.h
@@ -0,0 +1,90 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_ARRAY_PRIVATE_H
+#define CAIRO_ARRAY_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+/* cairo-array.c structures and functions */
+
+cairo_private void
+_cairo_array_init (cairo_array_t *array, unsigned int element_size);
+
+cairo_private void
+_cairo_array_fini (cairo_array_t *array);
+
+cairo_private cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, unsigned int additional);
+
+cairo_private void
+_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements);
+
+cairo_private cairo_status_t
+_cairo_array_append (cairo_array_t *array, const void *element);
+
+cairo_private cairo_status_t
+_cairo_array_append_multiple (cairo_array_t *array,
+ const void *elements,
+ unsigned int num_elements);
+
+cairo_private cairo_status_t
+_cairo_array_allocate (cairo_array_t *array,
+ unsigned int num_elements,
+ void **elements);
+
+cairo_private void *
+_cairo_array_index (cairo_array_t *array, unsigned int index);
+
+cairo_private const void *
+_cairo_array_index_const (const cairo_array_t *array, unsigned int index);
+
+cairo_private void
+_cairo_array_copy_element (const cairo_array_t *array, unsigned int index, void *dst);
+
+cairo_private unsigned int
+_cairo_array_num_elements (const cairo_array_t *array);
+
+cairo_private unsigned int
+_cairo_array_size (const cairo_array_t *array);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_ARRAY_PRIVATE_H */
diff --git a/src/cairo-array.c b/src/cairo-array.c
new file mode 100755
index 000000000..e37bb9980
--- /dev/null
+++ b/src/cairo-array.c
@@ -0,0 +1,528 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+/**
+ * _cairo_array_init:
+ *
+ * Initialize a new #cairo_array_t object to store objects each of size
+ * @element_size.
+ *
+ * The #cairo_array_t object provides grow-by-doubling storage. It
+ * never interprets the data passed to it, nor does it provide any
+ * sort of callback mechanism for freeing resources held onto by
+ * stored objects.
+ *
+ * When finished using the array, _cairo_array_fini() should be
+ * called to free resources allocated during use of the array.
+ **/
+void
+_cairo_array_init (cairo_array_t *array, unsigned int element_size)
+{
+ array->size = 0;
+ array->num_elements = 0;
+ array->element_size = element_size;
+ array->elements = NULL;
+}
+
+/**
+ * _cairo_array_fini:
+ * @array: A #cairo_array_t
+ *
+ * Free all resources associated with @array. After this call, @array
+ * should not be used again without a subsequent call to
+ * _cairo_array_init() again first.
+ **/
+void
+_cairo_array_fini (cairo_array_t *array)
+{
+ free (array->elements);
+}
+
+/**
+ * _cairo_array_grow_by:
+ * @array: a #cairo_array_t
+ *
+ * Increase the size of @array (if needed) so that there are at least
+ * @additional free spaces in the array. The actual size of the array
+ * is always increased by doubling as many times as necessary.
+ **/
+cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, unsigned int additional)
+{
+ char *new_elements;
+ unsigned int old_size = array->size;
+ unsigned int required_size = array->num_elements + additional;
+ unsigned int new_size;
+
+ /* check for integer overflow */
+ if (required_size > INT_MAX || required_size < array->num_elements)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (required_size <= old_size)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (old_size == 0)
+ new_size = 1;
+ else
+ new_size = old_size * 2;
+
+ while (new_size < required_size)
+ new_size = new_size * 2;
+
+ array->size = new_size;
+ new_elements = _cairo_realloc_ab (array->elements,
+ array->size, array->element_size);
+
+ if (unlikely (new_elements == NULL)) {
+ array->size = old_size;
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ array->elements = new_elements;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_array_truncate:
+ * @array: a #cairo_array_t
+ *
+ * Truncate size of the array to @num_elements if less than the
+ * current size. No memory is actually freed. The stored objects
+ * beyond @num_elements are simply "forgotten".
+ **/
+void
+_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements)
+{
+ if (num_elements < array->num_elements)
+ array->num_elements = num_elements;
+}
+
+/**
+ * _cairo_array_index:
+ * @array: a #cairo_array_t
+ * Returns: A pointer to the object stored at @index.
+ *
+ * If the resulting value is assigned to a pointer to an object of the same
+ * element_size as initially passed to _cairo_array_init() then that
+ * pointer may be used for further direct indexing with []. For
+ * example:
+ *
+ * <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, 0);
+ * for (i=0; i < num_elements; i++) {
+ * ... use elements[i] here ...
+ * }
+ *
+ * which in the num_elements==0 case gets the NULL pointer here,
+ * but never dereferences it.
+ */
+ if (index == 0 && array->num_elements == 0)
+ return NULL;
+
+ assert (index < array->num_elements);
+
+ return array->elements + index * array->element_size;
+}
+
+/**
+ * _cairo_array_index_const:
+ * @array: a #cairo_array_t
+ * Returns: A pointer to the object stored at @index.
+ *
+ * If the resulting value is assigned to a pointer to an object of the same
+ * element_size as initially passed to _cairo_array_init() then that
+ * pointer may be used for further direct indexing with []. For
+ * example:
+ *
+ * <informalexample><programlisting>
+ * cairo_array_t array;
+ * const double *values;
+ *
+ * _cairo_array_init (&array, sizeof(double));
+ * ... calls to _cairo_array_append() here ...
+ *
+ * values = _cairo_array_index_const (&array, 0);
+ * for (i = 0; i < _cairo_array_num_elements (&array); i++)
+ * ... read values[i] here ...
+ * </programlisting></informalexample>
+ **/
+const void *
+_cairo_array_index_const (const cairo_array_t *array, unsigned int index)
+{
+ /* We allow an index of 0 for the no-elements case.
+ * This makes for cleaner calling code which will often look like:
+ *
+ * elements = _cairo_array_index_const (array, 0);
+ * for (i=0; i < num_elements; i++) {
+ * ... read elements[i] here ...
+ * }
+ *
+ * which in the num_elements==0 case gets the NULL pointer here,
+ * but never dereferences it.
+ */
+ if (index == 0 && array->num_elements == 0)
+ return NULL;
+
+ assert (index < array->num_elements);
+
+ return array->elements + index * array->element_size;
+}
+
+/**
+ * _cairo_array_copy_element:
+ * @array: a #cairo_array_t
+ *
+ * Copy a single element out of the array from index @index into the
+ * location pointed to by @dst.
+ **/
+void
+_cairo_array_copy_element (const cairo_array_t *array,
+ unsigned int index,
+ void *dst)
+{
+ void *src = _cairo_array_index_const (array, index);
+ if(src != NULL)
+ memcpy (dst, src, array->element_size);
+}
+
+/**
+ * _cairo_array_append:
+ * @array: a #cairo_array_t
+ *
+ * Append a single item onto the array by growing the array by at
+ * least one element, then copying element_size bytes from @element
+ * into the array. The address of the resulting object within the
+ * array can be determined with:
+ *
+ * _cairo_array_index (array, _cairo_array_num_elements (array) - 1);
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
+ * operation.
+ **/
+cairo_status_t
+_cairo_array_append (cairo_array_t *array,
+ const void *element)
+{
+ return _cairo_array_append_multiple (array, element, 1);
+}
+
+/**
+ * _cairo_array_append_multiple:
+ * @array: a #cairo_array_t
+ *
+ * Append one or more items onto the array by growing the array by
+ * @num_elements, then copying @num_elements * element_size bytes from
+ * @elements into the array.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
+ * operation.
+ **/
+cairo_status_t
+_cairo_array_append_multiple (cairo_array_t *array,
+ const void *elements,
+ unsigned int num_elements)
+{
+ cairo_status_t status;
+ void *dest;
+
+ status = _cairo_array_allocate (array, num_elements, &dest);
+ if (unlikely (status))
+ return status;
+
+ memcpy (dest, elements, num_elements * array->element_size);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_array_allocate:
+ * @array: a #cairo_array_t
+ *
+ * Allocate space at the end of the array for @num_elements additional
+ * elements, providing the address of the new memory chunk in
+ * @elements. This memory will be 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;
+
+ status = _cairo_array_grow_by (array, num_elements);
+ if (unlikely (status))
+ return status;
+
+ assert (array->num_elements + num_elements <= array->size);
+
+ *elements = array->elements + array->num_elements * array->element_size;
+
+ array->num_elements += num_elements;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_array_num_elements:
+ * @array: a #cairo_array_t
+ * Returns: The number of elements stored in @array.
+ *
+ * This space was left intentionally blank, but gtk-doc filled it.
+ **/
+unsigned int
+_cairo_array_num_elements (const cairo_array_t *array)
+{
+ return array->num_elements;
+}
+
+/**
+ * _cairo_array_size:
+ * @array: a #cairo_array_t
+ * Returns: The number of elements for which there is currently space
+ * allocated in @array.
+ *
+ * This space was left intentionally blank, but gtk-doc filled it.
+ **/
+unsigned int
+_cairo_array_size (const cairo_array_t *array)
+{
+ return array->size;
+}
+
+/**
+ * _cairo_user_data_array_init:
+ * @array: a #cairo_user_data_array_t
+ *
+ * Initializes a #cairo_user_data_array_t structure for future
+ * use. After initialization, the array has no keys. Call
+ * _cairo_user_data_array_fini() to free any allocated memory
+ * when done using the array.
+ **/
+void
+_cairo_user_data_array_init (cairo_user_data_array_t *array)
+{
+ _cairo_array_init (array, sizeof (cairo_user_data_slot_t));
+}
+
+/**
+ * _cairo_user_data_array_fini:
+ * @array: a #cairo_user_data_array_t
+ *
+ * Destroys all current keys in the user data array and deallocates
+ * any memory allocated for the array itself.
+ **/
+void
+_cairo_user_data_array_fini (cairo_user_data_array_t *array)
+{
+ unsigned int num_slots;
+
+ num_slots = array->num_elements;
+ if (num_slots) {
+ cairo_user_data_slot_t *slots;
+
+ slots = _cairo_array_index (array, 0);
+ while (num_slots--) {
+ cairo_user_data_slot_t *s = &slots[num_slots];
+ if (s->user_data != NULL && s->destroy != NULL)
+ s->destroy (s->user_data);
+ }
+ }
+
+ _cairo_array_fini (array);
+}
+
+/**
+ * _cairo_user_data_array_get_data:
+ * @array: a #cairo_user_data_array_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Returns user data previously attached using the specified
+ * key. If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ **/
+void *
+_cairo_user_data_array_get_data (cairo_user_data_array_t *array,
+ const cairo_user_data_key_t *key)
+{
+ int i, num_slots;
+ cairo_user_data_slot_t *slots;
+
+ /* We allow this to support degenerate objects such as cairo_surface_nil. */
+ if (array == NULL)
+ return NULL;
+
+ num_slots = array->num_elements;
+ slots = _cairo_array_index (array, 0);
+ for (i = 0; i < num_slots; i++) {
+ if (slots[i].key == key)
+ return slots[i].user_data;
+ }
+
+ return NULL;
+}
+
+/**
+ * _cairo_user_data_array_set_data:
+ * @array: a #cairo_user_data_array_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * user data array is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attaches user data to a user data array. To remove user data,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ **/
+cairo_status_t
+_cairo_user_data_array_set_data (cairo_user_data_array_t *array,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy)
+{
+ cairo_status_t status;
+ int i, num_slots;
+ cairo_user_data_slot_t *slots, *slot, new_slot;
+
+ if (user_data) {
+ new_slot.key = key;
+ new_slot.user_data = user_data;
+ new_slot.destroy = destroy;
+ } else {
+ new_slot.key = NULL;
+ new_slot.user_data = NULL;
+ new_slot.destroy = NULL;
+ }
+
+ slot = NULL;
+ num_slots = array->num_elements;
+ slots = _cairo_array_index (array, 0);
+ for (i = 0; i < num_slots; i++) {
+ if (slots[i].key == key) {
+ slot = &slots[i];
+ if (slot->destroy && slot->user_data)
+ slot->destroy (slot->user_data);
+ break;
+ }
+ if (user_data && slots[i].user_data == NULL) {
+ slot = &slots[i]; /* Have to keep searching for an exact match */
+ }
+ }
+
+ if (slot) {
+ *slot = new_slot;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_array_append (array, &new_slot);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_user_data_array_copy (cairo_user_data_array_t *dst,
+ const cairo_user_data_array_t *src)
+{
+ /* discard any existing user-data */
+ if (dst->num_elements != 0) {
+ _cairo_user_data_array_fini (dst);
+ _cairo_user_data_array_init (dst);
+ }
+
+ return _cairo_array_append_multiple (dst,
+ _cairo_array_index_const (src, 0),
+ src->num_elements);
+}
+
+void
+_cairo_user_data_array_foreach (cairo_user_data_array_t *array,
+ void (*func) (const void *key,
+ void *elt,
+ void *closure),
+ void *closure)
+{
+ cairo_user_data_slot_t *slots;
+ int i, num_slots;
+
+ num_slots = array->num_elements;
+ slots = _cairo_array_index (array, 0);
+ for (i = 0; i < num_slots; i++) {
+ if (slots[i].user_data != NULL)
+ func (slots[i].key, slots[i].user_data, closure);
+ }
+}
diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h
new file mode 100755
index 000000000..327fed1d9
--- /dev/null
+++ b/src/cairo-atomic-private.h
@@ -0,0 +1,272 @@
+/* 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
+
+/* 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
+
+#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 void *
+_cairo_atomic_ptr_get (void **x)
+{
+ __sync_synchronize ();
+ return *x;
+}
+#else
+# define _cairo_atomic_int_get(x) (*x)
+# define _cairo_atomic_ptr_get(x) (*x)
+#endif
+
+# define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1))
+# define _cairo_atomic_int_dec(x) ((void) __sync_fetch_and_add(x, -1))
+# define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1)
+# define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv)
+# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv)
+
+#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_inc(x) ((void) AO_fetch_and_add1_full(x))
+# define _cairo_atomic_int_dec(x) ((void) AO_fetch_and_sub1_full(x))
+# define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1)
+# define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv)
+
+#if SIZEOF_VOID_P==SIZEOF_INT
+typedef unsigned int cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG
+typedef unsigned long cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG
+typedef unsigned long long cairo_atomic_intptr_t;
+#else
+#error No matching integer pointer type
+#endif
+
+# define _cairo_atomic_ptr_get(x) _cairo_atomic_intptr_to_voidptr (AO_load_full (x))
+# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
+ _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv)
+
+#endif
+
+#if HAVE_OS_ATOMIC_OPS
+#include <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_inc(x) ((void) OSAtomicIncrement32Barrier (x))
+# define _cairo_atomic_int_dec(x) ((void) OSAtomicDecrement32Barrier (x))
+# define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0)
+# define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x)
+
+#if SIZEOF_VOID_P==4
+typedef int32_t cairo_atomic_intptr_t;
+# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
+ OSAtomicCompareAndSwap32Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x)
+
+#elif SIZEOF_VOID_P==8
+typedef int64_t cairo_atomic_intptr_t;
+# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
+ OSAtomicCompareAndSwap64Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x)
+
+#else
+#error No matching integer pointer type
+#endif
+
+# define _cairo_atomic_ptr_get(x) (OSMemoryBarrier(), *(x))
+
+#endif
+
+#ifndef HAS_ATOMIC_OPS
+
+#if SIZEOF_VOID_P==SIZEOF_INT
+typedef unsigned int cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG
+typedef unsigned long cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG
+typedef unsigned long long cairo_atomic_intptr_t;
+#else
+#error No matching integer pointer type
+#endif
+
+typedef cairo_atomic_intptr_t cairo_atomic_int_t;
+
+cairo_private void
+_cairo_atomic_int_inc (cairo_atomic_int_t *x);
+
+#define _cairo_atomic_int_dec(x) _cairo_atomic_int_dec_and_test(x)
+
+cairo_private cairo_bool_t
+_cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x);
+
+cairo_private cairo_atomic_int_t
+_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv);
+
+cairo_private void *
+_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv);
+
+#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_impl (x, oldv, newv)
+#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_impl (x, oldv, newv)
+
+#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER
+cairo_private cairo_atomic_int_t
+_cairo_atomic_int_get (cairo_atomic_int_t *x);
+# 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_ptr_get(x) (*x)
+#endif
+
+#else
+
+/* Workaround GCC complaining about casts */
+static cairo_always_inline void *
+_cairo_atomic_intptr_to_voidptr (cairo_atomic_intptr_t x)
+{
+ return (void *) x;
+}
+
+static cairo_always_inline cairo_atomic_int_t
+_cairo_atomic_int_cmpxchg_return_old_fallback(cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv)
+{
+ cairo_atomic_int_t curr;
+
+ do {
+ curr = _cairo_atomic_int_get (x);
+ } while (curr == oldv && !_cairo_atomic_int_cmpxchg (x, oldv, newv));
+
+ return curr;
+}
+
+static cairo_always_inline void *
+_cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv)
+{
+ void *curr;
+
+ do {
+ curr = _cairo_atomic_ptr_get (x);
+ } while (curr == oldv && !_cairo_atomic_ptr_cmpxchg (x, oldv, newv));
+
+ return curr;
+}
+#endif
+
+#ifndef _cairo_atomic_int_cmpxchg_return_old
+#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_fallback (x, oldv, newv)
+#endif
+
+#ifndef _cairo_atomic_ptr_cmpxchg_return_old
+#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_fallback (x, oldv, newv)
+#endif
+
+#ifndef _cairo_atomic_int_cmpxchg
+#define _cairo_atomic_int_cmpxchg(x, oldv, newv) (_cairo_atomic_int_cmpxchg_return_old (x, oldv, newv) == oldv)
+#endif
+
+#ifndef _cairo_atomic_ptr_cmpxchg
+#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (_cairo_atomic_ptr_cmpxchg_return_old (x, oldv, newv) == oldv)
+#endif
+
+#define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x)
+#define _cairo_atomic_uint_cmpxchg(x, oldv, newv) \
+ _cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv)
+
+#define _cairo_status_set_error(status, err) do { \
+ int ret__; \
+ assert (err < CAIRO_STATUS_LAST_STATUS); \
+ /* hide compiler warnings about cairo_status_t != int (gcc treats its as \
+ * an unsigned integer instead, and about ignoring the return value. */ \
+ ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \
+ (void) ret__; \
+} while (0)
+
+CAIRO_END_DECLS
+
+#endif
diff --git a/src/cairo-atomic.c b/src/cairo-atomic.c
new file mode 100755
index 000000000..909cfea49
--- /dev/null
+++ b/src/cairo-atomic.c
@@ -0,0 +1,106 @@
+/* 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;
+}
+#endif
+
+#endif
diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h
new file mode 100755
index 000000000..cc5d5f313
--- /dev/null
+++ b/src/cairo-backend-private.h
@@ -0,0 +1,219 @@
+/* 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>
+ */
+
+#ifndef CAIRO_BACKEND_PRIVATE_H
+#define CAIRO_BACKEND_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-private.h"
+
+typedef enum _cairo_backend_type {
+ CAIRO_TYPE_DEFAULT,
+ CAIRO_TYPE_SKIA,
+} cairo_backend_type_t;
+
+struct _cairo_backend {
+ cairo_backend_type_t type;
+ void (*destroy) (void *cr);
+
+ cairo_surface_t *(*get_original_target) (void *cr);
+ cairo_surface_t *(*get_current_target) (void *cr);
+
+ cairo_status_t (*save) (void *cr);
+ cairo_status_t (*restore) (void *cr);
+
+ cairo_status_t (*push_group) (void *cr, cairo_content_t content);
+ cairo_pattern_t *(*pop_group) (void *cr);
+
+ cairo_status_t (*set_source_rgba) (void *cr, double red, double green, double blue, double alpha);
+ cairo_status_t (*set_source_surface) (void *cr, cairo_surface_t *surface, double x, double y);
+ cairo_status_t (*set_source) (void *cr, cairo_pattern_t *source);
+ cairo_pattern_t *(*get_source) (void *cr);
+
+ cairo_status_t (*set_antialias) (void *cr, cairo_antialias_t antialias);
+ cairo_status_t (*set_dash) (void *cr, const double *dashes, int num_dashes, double offset);
+ cairo_status_t (*set_fill_rule) (void *cr, cairo_fill_rule_t fill_rule);
+ cairo_status_t (*set_line_cap) (void *cr, cairo_line_cap_t line_cap);
+ cairo_status_t (*set_line_join) (void *cr, cairo_line_join_t line_join);
+ cairo_status_t (*set_line_width) (void *cr, double line_width);
+ cairo_status_t (*set_miter_limit) (void *cr, double limit);
+ cairo_status_t (*set_opacity) (void *cr, double opacity);
+ cairo_status_t (*set_operator) (void *cr, cairo_operator_t op);
+ cairo_status_t (*set_tolerance) (void *cr, double tolerance);
+
+ cairo_antialias_t (*get_antialias) (void *cr);
+ void (*get_dash) (void *cr, double *dashes, int *num_dashes, double *offset);
+ cairo_fill_rule_t (*get_fill_rule) (void *cr);
+ cairo_line_cap_t (*get_line_cap) (void *cr);
+ cairo_line_join_t (*get_line_join) (void *cr);
+ double (*get_line_width) (void *cr);
+ double (*get_miter_limit) (void *cr);
+ double (*get_opacity) (void *cr);
+ cairo_operator_t (*get_operator) (void *cr);
+ double (*get_tolerance) (void *cr);
+
+ cairo_status_t (*translate) (void *cr, double tx, double ty);
+ cairo_status_t (*scale) (void *cr, double sx, double sy);
+ cairo_status_t (*rotate) (void *cr, double theta);
+ cairo_status_t (*transform) (void *cr, const cairo_matrix_t *matrix);
+ cairo_status_t (*set_matrix) (void *cr, const cairo_matrix_t *matrix);
+ cairo_status_t (*set_identity_matrix) (void *cr);
+ void (*get_matrix) (void *cr, cairo_matrix_t *matrix);
+
+ void (*user_to_device) (void *cr, double *x, double *y);
+ void (*user_to_device_distance) (void *cr, double *x, double *y);
+ void (*device_to_user) (void *cr, double *x, double *y);
+ void (*device_to_user_distance) (void *cr, double *x, double *y);
+
+ void (*user_to_backend) (void *cr, double *x, double *y);
+ void (*user_to_backend_distance) (void *cr, double *x, double *y);
+ void (*backend_to_user) (void *cr, double *x, double *y);
+ void (*backend_to_user_distance) (void *cr, double *x, double *y);
+
+ cairo_status_t (*new_path) (void *cr);
+ cairo_status_t (*new_sub_path) (void *cr);
+ cairo_status_t (*move_to) (void *cr, double x, double y);
+ cairo_status_t (*rel_move_to) (void *cr, double dx, double dy);
+ cairo_status_t (*line_to) (void *cr, double x, double y);
+ cairo_status_t (*rel_line_to) (void *cr, double dx, double dy);
+ cairo_status_t (*curve_to) (void *cr, double x1, double y1, double x2, double y2, double x3, double y3);
+ cairo_status_t (*rel_curve_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3);
+ cairo_status_t (*arc_to) (void *cr, double x1, double y1, double x2, double y2, double radius);
+ cairo_status_t (*rel_arc_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double radius);
+ cairo_status_t (*close_path) (void *cr);
+
+ cairo_status_t (*arc) (void *cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward);
+ cairo_status_t (*rectangle) (void *cr, double x, double y, double width, double height);
+
+ void (*path_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+ cairo_bool_t (*has_current_point) (void *cr);
+ cairo_bool_t (*get_current_point) (void *cr, double *x, double *y);
+
+ cairo_path_t *(*copy_path) (void *cr);
+ cairo_path_t *(*copy_path_flat) (void *cr);
+ cairo_status_t (*append_path) (void *cr, const cairo_path_t *path);
+
+ cairo_status_t (*stroke_to_path) (void *cr);
+
+ cairo_status_t (*clip) (void *cr);
+ cairo_status_t (*clip_preserve) (void *cr);
+ cairo_status_t (*in_clip) (void *cr, double x, double y, cairo_bool_t *inside);
+ cairo_status_t (*clip_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+ cairo_status_t (*reset_clip) (void *cr);
+ cairo_rectangle_list_t *(*clip_copy_rectangle_list) (void *cr);
+
+ cairo_status_t (*paint) (void *cr);
+ cairo_status_t (*paint_with_alpha) (void *cr, double opacity);
+ cairo_status_t (*mask) (void *cr, cairo_pattern_t *pattern);
+
+ cairo_status_t (*stroke) (void *cr);
+ cairo_status_t (*stroke_preserve) (void *cr);
+ cairo_status_t (*in_stroke) (void *cr, double x, double y, cairo_bool_t *inside);
+ cairo_status_t (*stroke_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+
+ cairo_status_t (*fill) (void *cr);
+ cairo_status_t (*fill_preserve) (void *cr);
+ cairo_status_t (*in_fill) (void *cr, double x, double y, cairo_bool_t *inside);
+ cairo_status_t (*fill_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+
+ cairo_status_t (*set_font_face) (void *cr, cairo_font_face_t *font_face);
+ cairo_font_face_t *(*get_font_face) (void *cr);
+ cairo_status_t (*set_font_size) (void *cr, double size);
+ cairo_status_t (*set_font_matrix) (void *cr, const cairo_matrix_t *matrix);
+ void (*get_font_matrix) (void *cr, cairo_matrix_t *matrix);
+ cairo_status_t (*set_font_options) (void *cr, const cairo_font_options_t *options);
+ void (*get_font_options) (void *cr, cairo_font_options_t *options);
+ cairo_status_t (*set_scaled_font) (void *cr, cairo_scaled_font_t *scaled_font);
+ cairo_scaled_font_t *(*get_scaled_font) (void *cr);
+ cairo_status_t (*font_extents) (void *cr, cairo_font_extents_t *extents);
+
+ cairo_status_t (*glyphs) (void *cr,
+ const cairo_glyph_t *glyphs, int num_glyphs,
+ cairo_glyph_text_info_t *info);
+ cairo_status_t (*glyph_path) (void *cr,
+ const cairo_glyph_t *glyphs, int num_glyphs);
+
+ cairo_status_t (*glyph_extents) (void *cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents);
+
+ cairo_status_t (*copy_page) (void *cr);
+ cairo_status_t (*show_page) (void *cr);
+
+ cairo_status_t (*set_shadow) (void *cr, cairo_shadow_type_t shadow);
+ cairo_status_t (*set_shadow_offset) (void *cr,
+ double x_offset,
+ double y_offset);
+ cairo_status_t (*set_shadow_rgba) (void *cr,
+ double red,
+ double green,
+ double blue,
+ double alpha);
+ cairo_status_t (*set_shadow_blur) (void *cr,
+ double x_blur,
+ double y_blur);
+ void (*set_draw_shadow_only) (void *cr,
+ cairo_bool_t draw_shadow_only);
+ void (*shadow_enable_cache) (void *cr, cairo_bool_t enable);
+ void (*set_path_is_inset_shadow_with_spread) (void *cr,
+ cairo_bool_t is_spread_path);
+};
+
+static inline void
+_cairo_backend_to_user (cairo_t *cr, double *x, double *y)
+{
+ cr->backend->backend_to_user (cr, x, y);
+}
+
+static inline void
+_cairo_backend_to_user_distance (cairo_t *cr, double *x, double *y)
+{
+ cr->backend->backend_to_user_distance (cr, x, y);
+}
+
+static inline void
+_cairo_user_to_backend (cairo_t *cr, double *x, double *y)
+{
+ cr->backend->user_to_backend (cr, x, y);
+}
+
+static inline void
+_cairo_user_to_backend_distance (cairo_t *cr, double *x, double *y)
+{
+ cr->backend->user_to_backend_distance (cr, x, y);
+}
+
+#endif /* CAIRO_BACKEND_PRIVATE_H */
diff --git a/src/cairo-base64-stream.c b/src/cairo-base64-stream.c
new file mode 100755
index 000000000..636431372
--- /dev/null
+++ b/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/src/cairo-base85-stream.c b/src/cairo-base85-stream.c
new file mode 100755
index 000000000..f81affb49
--- /dev/null
+++ b/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/src/cairo-bentley-ottmann-rectangular.c b/src/cairo-bentley-ottmann-rectangular.c
new file mode 100755
index 000000000..5541bdc3a
--- /dev/null
+++ b/src/cairo-bentley-ottmann-rectangular.c
@@ -0,0 +1,884 @@
+/*
+ * 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-inline.h"
+#include "cairo-list-private.h"
+#include "cairo-traps-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 _sweep_line {
+ rectangle_t **rectangles;
+ rectangle_t **stop;
+ edge_t head, tail, *insert, *cursor;
+ int32_t current_y;
+ int32_t last_y;
+ int stop_size;
+
+ int32_t insert_x;
+ cairo_fill_rule_t fill_rule;
+
+ cairo_bool_t do_traps;
+ void *container;
+
+ jmp_buf unwind;
+} sweep_line_t;
+
+#define DEBUG_TRAPS 0
+
+#if DEBUG_TRAPS
+static void
+dump_traps (cairo_traps_t *traps, const char *filename)
+{
+ FILE *file;
+ int n;
+
+ if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+ return;
+
+ file = fopen (filename, "a");
+ if (file != NULL) {
+ for (n = 0; n < traps->num_traps; n++) {
+ fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+ traps->traps[n].top,
+ traps->traps[n].bottom,
+ traps->traps[n].left.p1.x,
+ traps->traps[n].left.p1.y,
+ traps->traps[n].left.p2.x,
+ traps->traps[n].left.p2.y,
+ traps->traps[n].right.p1.x,
+ traps->traps[n].right.p1.y,
+ traps->traps[n].right.p2.x,
+ traps->traps[n].right.p2.y);
+ }
+ fprintf (file, "\n");
+ fclose (file);
+ }
+}
+#else
+#define dump_traps(traps, filename)
+#endif
+
+static inline int
+rectangle_compare_start (const rectangle_t *a,
+ const rectangle_t *b)
+{
+ return a->top - b->top;
+}
+
+static inline int
+rectangle_compare_stop (const rectangle_t *a,
+ const rectangle_t *b)
+{
+ return a->bottom - b->bottom;
+}
+
+static inline void
+pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+ rectangle_t **elements;
+ int i, parent;
+
+ elements = sweep->stop;
+ for (i = ++sweep->stop_size;
+ i != PQ_FIRST_ENTRY &&
+ rectangle_compare_stop (rectangle,
+ elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+ i = parent)
+ {
+ elements[i] = elements[parent];
+ }
+
+ elements[i] = rectangle;
+}
+
+static inline void
+rectangle_pop_stop (sweep_line_t *sweep)
+{
+ rectangle_t **elements = sweep->stop;
+ rectangle_t *tail;
+ int child, i;
+
+ tail = elements[sweep->stop_size--];
+ if (sweep->stop_size == 0) {
+ elements[PQ_FIRST_ENTRY] = NULL;
+ return;
+ }
+
+ for (i = PQ_FIRST_ENTRY;
+ (child = PQ_LEFT_CHILD_INDEX (i)) <= sweep->stop_size;
+ i = child)
+ {
+ if (child != sweep->stop_size &&
+ rectangle_compare_stop (elements[child+1],
+ elements[child]) < 0)
+ {
+ child++;
+ }
+
+ if (rectangle_compare_stop (elements[child], tail) >= 0)
+ break;
+
+ elements[i] = elements[child];
+ }
+ elements[i] = tail;
+}
+
+static inline rectangle_t *
+rectangle_pop_start (sweep_line_t *sweep_line)
+{
+ return *sweep_line->rectangles++;
+}
+
+static inline rectangle_t *
+rectangle_peek_stop (sweep_line_t *sweep_line)
+{
+ return sweep_line->stop[PQ_FIRST_ENTRY];
+}
+
+CAIRO_COMBSORT_DECLARE (_rectangle_sort,
+ rectangle_t *,
+ rectangle_compare_start)
+
+static void
+sweep_line_init (sweep_line_t *sweep_line,
+ rectangle_t **rectangles,
+ int num_rectangles,
+ cairo_fill_rule_t fill_rule,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ rectangles[-2] = NULL;
+ rectangles[-1] = NULL;
+ rectangles[num_rectangles] = NULL;
+ sweep_line->rectangles = rectangles;
+ sweep_line->stop = rectangles - 2;
+ sweep_line->stop_size = 0;
+
+ sweep_line->insert = NULL;
+ sweep_line->insert_x = INT_MAX;
+ sweep_line->cursor = &sweep_line->tail;
+
+ sweep_line->head.dir = 0;
+ sweep_line->head.x = INT32_MIN;
+ sweep_line->head.right = NULL;
+ sweep_line->head.prev = NULL;
+ sweep_line->head.next = &sweep_line->tail;
+ sweep_line->tail.prev = &sweep_line->head;
+ sweep_line->tail.next = NULL;
+ sweep_line->tail.right = NULL;
+ sweep_line->tail.x = INT32_MAX;
+ sweep_line->tail.dir = 0;
+
+ sweep_line->current_y = INT32_MIN;
+ sweep_line->last_y = INT32_MIN;
+
+ sweep_line->fill_rule = fill_rule;
+ sweep_line->container = container;
+ sweep_line->do_traps = do_traps;
+}
+
+static void
+edge_end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ /* Only emit (trivial) non-degenerate trapezoids with positive height. */
+ if (likely (left->top < bot)) {
+ if (sweep_line->do_traps) {
+ cairo_line_t _left = {
+ { left->x, left->top },
+ { left->x, bot },
+ }, _right = {
+ { left->right->x, left->top },
+ { left->right->x, bot },
+ };
+ _cairo_traps_add_trap (sweep_line->container, left->top, bot, &_left, &_right);
+ status = _cairo_traps_status ((cairo_traps_t *) sweep_line->container);
+ } else {
+ cairo_box_t box;
+
+ box.p1.x = left->x;
+ box.p1.y = left->top;
+ box.p2.x = left->right->x;
+ box.p2.y = bot;
+
+ status = _cairo_boxes_add (sweep_line->container,
+ CAIRO_ANTIALIAS_DEFAULT,
+ &box);
+ }
+ }
+ if (unlikely (status))
+ longjmp (sweep_line->unwind, status);
+
+ left->right = NULL;
+}
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline void
+edge_start_or_continue_box (sweep_line_t *sweep_line,
+ edge_t *left,
+ edge_t *right,
+ int top)
+{
+ if (left->right == right)
+ return;
+
+ if (left->right != NULL) {
+ if (left->right->x == right->x) {
+ /* continuation on right, so just swap edges */
+ left->right = right;
+ return;
+ }
+
+ edge_end_box (sweep_line, left, top);
+ }
+
+ if (left->x != right->x) {
+ left->top = top;
+ left->right = right;
+ }
+}
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ * - head_a: The head of the first list.
+ * - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static edge_t *
+merge_sorted_edges (edge_t *head_a, edge_t *head_b)
+{
+ edge_t *head, *prev;
+ int32_t x;
+
+ prev = head_a->prev;
+ if (head_a->x <= head_b->x) {
+ head = head_a;
+ } else {
+ head_b->prev = prev;
+ head = head_b;
+ goto start_with_b;
+ }
+
+ do {
+ x = head_b->x;
+ while (head_a != NULL && head_a->x <= x) {
+ prev = head_a;
+ head_a = head_a->next;
+ }
+
+ head_b->prev = prev;
+ prev->next = head_b;
+ if (head_a == NULL)
+ return head;
+
+start_with_b:
+ x = head_a->x;
+ while (head_b != NULL && head_b->x <= x) {
+ prev = head_b;
+ head_b = head_b->next;
+ }
+
+ head_a->prev = prev;
+ prev->next = head_a;
+ if (head_b == NULL)
+ return head;
+ } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ * - list: The list to be sorted; list cannot be NULL.
+ * - limit: Recursion limit.
+ * Output:
+ * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ * input list; if the input list has fewer elements, head_out be a sorted list
+ * containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static edge_t *
+sort_edges (edge_t *list,
+ unsigned int level,
+ edge_t **head_out)
+{
+ edge_t *head_other, *remaining;
+ unsigned int i;
+
+ head_other = list->next;
+
+ if (head_other == NULL) {
+ *head_out = list;
+ return NULL;
+ }
+
+ remaining = head_other->next;
+ if (list->x <= head_other->x) {
+ *head_out = list;
+ head_other->next = NULL;
+ } else {
+ *head_out = head_other;
+ head_other->prev = list->prev;
+ head_other->next = list;
+ list->prev = head_other;
+ list->next = NULL;
+ }
+
+ for (i = 0; i < level && remaining; i++) {
+ remaining = sort_edges (remaining, i, &head_other);
+ *head_out = merge_sorted_edges (*head_out, head_other);
+ }
+
+ return remaining;
+}
+
+static edge_t *
+merge_unsorted_edges (edge_t *head, edge_t *unsorted)
+{
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ return merge_sorted_edges (head, unsorted);
+}
+
+static void
+active_edges_insert (sweep_line_t *sweep)
+{
+ edge_t *prev;
+ int x;
+
+ x = sweep->insert_x;
+ prev = sweep->cursor;
+ if (prev->x > x) {
+ do {
+ prev = prev->prev;
+ } while (prev->x > x);
+ } else {
+ while (prev->next->x < x)
+ prev = prev->next;
+ }
+
+ prev->next = merge_unsorted_edges (prev->next, sweep->insert);
+ sweep->cursor = sweep->insert;
+ sweep->insert = NULL;
+ sweep->insert_x = INT_MAX;
+}
+
+static inline void
+active_edges_to_traps (sweep_line_t *sweep)
+{
+ int top = sweep->current_y;
+ edge_t *pos;
+
+ if (sweep->last_y == sweep->current_y)
+ return;
+
+ if (sweep->insert)
+ active_edges_insert (sweep);
+
+ pos = sweep->head.next;
+ if (pos == &sweep->tail)
+ return;
+
+ if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING) {
+ do {
+ edge_t *left, *right;
+ int winding;
+
+ left = pos;
+ winding = left->dir;
+
+ right = left->next;
+
+ /* Check if there is a co-linear edge with an existing trap */
+ while (right->x == left->x) {
+ if (right->right != NULL) {
+ assert (left->right == NULL);
+ /* continuation on left */
+ left->top = right->top;
+ left->right = right->right;
+ right->right = NULL;
+ }
+ winding += right->dir;
+ right = right->next;
+ }
+
+ if (winding == 0) {
+ if (left->right != NULL)
+ edge_end_box (sweep, left, top);
+ pos = right;
+ continue;
+ }
+
+ do {
+ /* End all subsumed traps */
+ if (unlikely (right->right != NULL))
+ edge_end_box (sweep, right, top);
+
+ /* Greedily search for the closing edge, so that we generate
+ * the * maximal span width with the minimal number of
+ * boxes.
+ */
+ winding += right->dir;
+ if (winding == 0 && right->x != right->next->x)
+ break;
+
+ right = right->next;
+ } while (TRUE);
+
+ edge_start_or_continue_box (sweep, left, right, top);
+
+ pos = right->next;
+ } while (pos != &sweep->tail);
+ } else {
+ do {
+ edge_t *right = pos->next;
+ int count = 0;
+
+ do {
+ /* End all subsumed traps */
+ if (unlikely (right->right != NULL))
+ edge_end_box (sweep, right, top);
+
+ /* skip co-linear edges */
+ if (++count & 1 && right->x != right->next->x)
+ break;
+
+ right = right->next;
+ } while (TRUE);
+
+ edge_start_or_continue_box (sweep, pos, right, top);
+
+ pos = right->next;
+ } while (pos != &sweep->tail);
+ }
+
+ sweep->last_y = sweep->current_y;
+}
+
+static inline void
+sweep_line_delete_edge (sweep_line_t *sweep, edge_t *edge)
+{
+ if (edge->right != NULL) {
+ edge_t *next = edge->next;
+ if (next->x == edge->x) {
+ next->top = edge->top;
+ next->right = edge->right;
+ } else
+ edge_end_box (sweep, edge, sweep->current_y);
+ }
+
+ if (sweep->cursor == edge)
+ sweep->cursor = edge->prev;
+
+ edge->prev->next = edge->next;
+ edge->next->prev = edge->prev;
+}
+
+static inline cairo_bool_t
+sweep_line_delete (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+ cairo_bool_t update;
+
+ update = TRUE;
+ if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING &&
+ rectangle->left.prev->dir == rectangle->left.dir)
+ {
+ update = rectangle->left.next != &rectangle->right;
+ }
+
+ sweep_line_delete_edge (sweep, &rectangle->left);
+ sweep_line_delete_edge (sweep, &rectangle->right);
+
+ rectangle_pop_stop (sweep);
+ return update;
+}
+
+static inline void
+sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+ if (sweep->insert)
+ sweep->insert->prev = &rectangle->right;
+ rectangle->right.next = sweep->insert;
+ rectangle->right.prev = &rectangle->left;
+ rectangle->left.next = &rectangle->right;
+ rectangle->left.prev = NULL;
+ sweep->insert = &rectangle->left;
+ if (rectangle->left.x < sweep->insert_x)
+ sweep->insert_x = rectangle->left.x;
+
+ pqueue_push (sweep, rectangle);
+}
+
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles,
+ int num_rectangles,
+ cairo_fill_rule_t fill_rule,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ sweep_line_t sweep_line;
+ rectangle_t *rectangle;
+ cairo_status_t status;
+ cairo_bool_t update = FALSE;
+
+ sweep_line_init (&sweep_line,
+ rectangles, num_rectangles,
+ fill_rule,
+ do_traps, container);
+ if ((status = setjmp (sweep_line.unwind)))
+ return status;
+
+ rectangle = rectangle_pop_start (&sweep_line);
+ do {
+ if (rectangle->top != sweep_line.current_y) {
+ rectangle_t *stop;
+
+ stop = rectangle_peek_stop (&sweep_line);
+ while (stop != NULL && stop->bottom < rectangle->top) {
+ if (stop->bottom != sweep_line.current_y) {
+ if (update) {
+ active_edges_to_traps (&sweep_line);
+ update = FALSE;
+ }
+
+ sweep_line.current_y = stop->bottom;
+ }
+
+ update |= sweep_line_delete (&sweep_line, stop);
+ stop = rectangle_peek_stop (&sweep_line);
+ }
+
+ if (update) {
+ active_edges_to_traps (&sweep_line);
+ update = FALSE;
+ }
+
+ sweep_line.current_y = rectangle->top;
+ }
+
+ do {
+ sweep_line_insert (&sweep_line, rectangle);
+ } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL &&
+ sweep_line.current_y == rectangle->top);
+ update = TRUE;
+ } while (rectangle);
+
+ while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) {
+ if (rectangle->bottom != sweep_line.current_y) {
+ if (update) {
+ active_edges_to_traps (&sweep_line);
+ update = FALSE;
+ }
+ sweep_line.current_y = rectangle->bottom;
+ }
+
+ update |= sweep_line_delete (&sweep_line, rectangle);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps,
+ cairo_fill_rule_t fill_rule)
+{
+ rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
+ rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3];
+ rectangle_t *rectangles, **rectangles_ptrs;
+ cairo_status_t status;
+ int i;
+
+ if (unlikely (traps->num_traps <= 1))
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (traps->is_rectangular);
+
+ dump_traps (traps, "bo-rects-traps-in.txt");
+
+ rectangles = stack_rectangles;
+ rectangles_ptrs = stack_rectangles_ptrs;
+ if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) {
+ rectangles = _cairo_malloc_ab_plus_c (traps->num_traps,
+ sizeof (rectangle_t) +
+ sizeof (rectangle_t *),
+ 3*sizeof (rectangle_t *));
+ if (unlikely (rectangles == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps);
+ }
+
+ for (i = 0; i < traps->num_traps; i++) {
+ if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) {
+ rectangles[i].left.x = traps->traps[i].left.p1.x;
+ rectangles[i].left.dir = 1;
+
+ rectangles[i].right.x = traps->traps[i].right.p1.x;
+ rectangles[i].right.dir = -1;
+ } else {
+ rectangles[i].right.x = traps->traps[i].left.p1.x;
+ rectangles[i].right.dir = 1;
+
+ rectangles[i].left.x = traps->traps[i].right.p1.x;
+ rectangles[i].left.dir = -1;
+ }
+
+ rectangles[i].left.right = NULL;
+ rectangles[i].right.right = NULL;
+
+ rectangles[i].top = traps->traps[i].top;
+ rectangles[i].bottom = traps->traps[i].bottom;
+
+ rectangles_ptrs[i+2] = &rectangles[i];
+ }
+ /* XXX incremental sort */
+ _rectangle_sort (rectangles_ptrs+2, i);
+
+ _cairo_traps_clear (traps);
+ status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i,
+ fill_rule,
+ TRUE, traps);
+ traps->is_rectilinear = TRUE;
+ traps->is_rectangular = TRUE;
+
+ if (rectangles != stack_rectangles)
+ free (rectangles);
+
+ dump_traps (traps, "bo-rects-traps-out.txt");
+
+ return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in,
+ cairo_fill_rule_t fill_rule,
+ cairo_boxes_t *out)
+{
+ rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
+ rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3];
+ rectangle_t *rectangles, **rectangles_ptrs;
+ rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ];
+ rectangle_t **rectangles_chain = NULL;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ int i, j, y_min, y_max;
+
+ if (unlikely (in->num_boxes == 0)) {
+ _cairo_boxes_clear (out);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (in->num_boxes == 1) {
+ if (in == out) {
+ cairo_box_t *box = &in->chunks.base[0];
+
+ if (box->p1.x > box->p2.x) {
+ cairo_fixed_t tmp = box->p1.x;
+ box->p1.x = box->p2.x;
+ box->p2.x = tmp;
+ }
+ } else {
+ cairo_box_t box = in->chunks.base[0];
+
+ if (box.p1.x > box.p2.x) {
+ cairo_fixed_t tmp = box.p1.x;
+ box.p1.x = box.p2.x;
+ box.p2.x = tmp;
+ }
+
+ _cairo_boxes_clear (out);
+ status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ y_min = INT_MAX; y_max = INT_MIN;
+ for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ if (box[i].p1.y < y_min)
+ y_min = box[i].p1.y;
+ if (box[i].p1.y > y_max)
+ y_max = box[i].p1.y;
+ }
+ }
+ y_min = _cairo_fixed_integer_floor (y_min);
+ y_max = _cairo_fixed_integer_floor (y_max) + 1;
+ y_max -= y_min;
+
+ if (y_max < in->num_boxes) {
+ rectangles_chain = stack_rectangles_chain;
+ if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) {
+ rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *));
+ if (unlikely (rectangles_chain == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*));
+ }
+
+ rectangles = stack_rectangles;
+ rectangles_ptrs = stack_rectangles_ptrs;
+ if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) {
+ rectangles = _cairo_malloc_ab_plus_c (in->num_boxes,
+ sizeof (rectangle_t) +
+ sizeof (rectangle_t *),
+ 3*sizeof (rectangle_t *));
+ if (unlikely (rectangles == NULL)) {
+ if (rectangles_chain != stack_rectangles_chain)
+ free (rectangles_chain);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes);
+ }
+
+ j = 0;
+ for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int h;
+
+ if (box[i].p1.x < box[i].p2.x) {
+ rectangles[j].left.x = box[i].p1.x;
+ rectangles[j].left.dir = 1;
+
+ rectangles[j].right.x = box[i].p2.x;
+ rectangles[j].right.dir = -1;
+ } else {
+ rectangles[j].right.x = box[i].p1.x;
+ rectangles[j].right.dir = 1;
+
+ rectangles[j].left.x = box[i].p2.x;
+ rectangles[j].left.dir = -1;
+ }
+
+ rectangles[j].left.right = NULL;
+ rectangles[j].right.right = NULL;
+
+ rectangles[j].top = box[i].p1.y;
+ rectangles[j].bottom = box[i].p2.y;
+
+ if (rectangles_chain) {
+ h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min;
+ rectangles[j].left.next = (edge_t *)rectangles_chain[h];
+ rectangles_chain[h] = &rectangles[j];
+ } else {
+ rectangles_ptrs[j+2] = &rectangles[j];
+ }
+ j++;
+ }
+ }
+
+ if (rectangles_chain) {
+ j = 2;
+ for (y_min = 0; y_min < y_max; y_min++) {
+ rectangle_t *r;
+ int start = j;
+ for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next)
+ rectangles_ptrs[j++] = r;
+ if (j > start + 1)
+ _rectangle_sort (rectangles_ptrs + start, j - start);
+ }
+
+ if (rectangles_chain != stack_rectangles_chain)
+ free (rectangles_chain);
+
+ j -= 2;
+ } else {
+ _rectangle_sort (rectangles_ptrs + 2, j);
+ }
+
+ _cairo_boxes_clear (out);
+ status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j,
+ fill_rule,
+ FALSE, out);
+ if (rectangles != stack_rectangles)
+ free (rectangles);
+
+ return status;
+}
diff --git a/src/cairo-bentley-ottmann-rectilinear.c b/src/cairo-bentley-ottmann-rectilinear.c
new file mode 100755
index 000000000..7c0be69b7
--- /dev/null
+++ b/src/cairo-bentley-ottmann-rectilinear.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ * Carl D. Worth <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-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-traps-private.h"
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+typedef struct _cairo_bo_trap cairo_bo_trap_t;
+
+/* A deferred trapezoid of an edge */
+struct _cairo_bo_trap {
+ cairo_bo_edge_t *right;
+ int32_t top;
+};
+
+struct _cairo_bo_edge {
+ cairo_edge_t edge;
+ cairo_bo_edge_t *prev;
+ cairo_bo_edge_t *next;
+ cairo_bo_trap_t deferred_trap;
+};
+
+typedef enum {
+ CAIRO_BO_EVENT_TYPE_START,
+ CAIRO_BO_EVENT_TYPE_STOP
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+ cairo_bo_edge_t *edge;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_sweep_line {
+ cairo_bo_event_t **events;
+ cairo_bo_edge_t *head;
+ cairo_bo_edge_t *stopped;
+ int32_t current_y;
+ cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+static inline int
+_cairo_point_compare (const cairo_point_t *a,
+ const cairo_point_t *b)
+{
+ int cmp;
+
+ cmp = a->y - b->y;
+ if (likely (cmp))
+ return cmp;
+
+ return a->x - b->x;
+}
+
+static inline int
+_cairo_bo_edge_compare (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b)
+{
+ int cmp;
+
+ cmp = a->edge.line.p1.x - b->edge.line.p1.x;
+ if (likely (cmp))
+ return cmp;
+
+ return b->edge.bottom - a->edge.bottom;
+}
+
+static inline int
+cairo_bo_event_compare (const cairo_bo_event_t *a,
+ const cairo_bo_event_t *b)
+{
+ int cmp;
+
+ cmp = _cairo_point_compare (&a->point, &b->point);
+ if (likely (cmp))
+ return cmp;
+
+ cmp = a->type - b->type;
+ if (cmp)
+ return cmp;
+
+ return a - b;
+}
+
+static inline cairo_bo_event_t *
+_cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line)
+{
+ return *sweep_line->events++;
+}
+
+CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
+ cairo_bo_event_t *,
+ cairo_bo_event_compare)
+
+static void
+_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_event_t **events,
+ int num_events)
+{
+ _cairo_bo_event_queue_sort (events, num_events);
+ events[num_events] = NULL;
+ sweep_line->events = events;
+
+ sweep_line->head = NULL;
+ sweep_line->current_y = INT32_MIN;
+ sweep_line->current_edge = NULL;
+}
+
+static void
+_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (sweep_line->current_edge != NULL) {
+ cairo_bo_edge_t *prev, *next;
+ int cmp;
+
+ cmp = _cairo_bo_edge_compare (sweep_line->current_edge, edge);
+ if (cmp < 0) {
+ prev = sweep_line->current_edge;
+ next = prev->next;
+ while (next != NULL && _cairo_bo_edge_compare (next, edge) < 0)
+ prev = next, next = prev->next;
+
+ prev->next = edge;
+ edge->prev = prev;
+ edge->next = next;
+ if (next != NULL)
+ next->prev = edge;
+ } else if (cmp > 0) {
+ next = sweep_line->current_edge;
+ prev = next->prev;
+ while (prev != NULL && _cairo_bo_edge_compare (prev, edge) > 0)
+ next = prev, prev = next->prev;
+
+ next->prev = edge;
+ edge->next = next;
+ edge->prev = prev;
+ if (prev != NULL)
+ prev->next = edge;
+ else
+ sweep_line->head = edge;
+ } else {
+ prev = sweep_line->current_edge;
+ edge->prev = prev;
+ edge->next = prev->next;
+ if (prev->next != NULL)
+ prev->next->prev = edge;
+ prev->next = edge;
+ }
+ } else {
+ sweep_line->head = edge;
+ }
+
+ sweep_line->current_edge = edge;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (edge->prev != NULL)
+ edge->prev->next = edge->next;
+ else
+ sweep_line->head = edge->next;
+
+ if (edge->next != NULL)
+ edge->next->prev = edge->prev;
+
+ if (sweep_line->current_edge == edge)
+ sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static inline cairo_bool_t
+edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+ return a->edge.line.p1.x == b->edge.line.p1.x;
+}
+
+static cairo_status_t
+_cairo_bo_edge_end_trap (cairo_bo_edge_t *left,
+ int32_t bot,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ cairo_bo_trap_t *trap = &left->deferred_trap;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ /* Only emit (trivial) non-degenerate trapezoids with positive height. */
+ if (likely (trap->top < bot)) {
+ if (do_traps) {
+ _cairo_traps_add_trap (container,
+ trap->top, bot,
+ &left->edge.line, &trap->right->edge.line);
+ status = _cairo_traps_status ((cairo_traps_t *) container);
+ } else {
+ cairo_box_t box;
+
+ box.p1.x = left->edge.line.p1.x;
+ box.p1.y = trap->top;
+ box.p2.x = trap->right->edge.line.p1.x;
+ box.p2.y = bot;
+ status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box);
+ }
+ }
+
+ trap->right = NULL;
+
+ return status;
+}
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline cairo_status_t
+_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right,
+ int top,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ cairo_status_t status;
+
+ if (left->deferred_trap.right == right)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (left->deferred_trap.right != NULL) {
+ if (right != NULL && edges_collinear (left->deferred_trap.right, right))
+ {
+ /* continuation on right, so just swap edges */
+ left->deferred_trap.right = right;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_bo_edge_end_trap (left, top, do_traps, container);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (right != NULL && ! edges_collinear (left, right)) {
+ left->deferred_trap.top = top;
+ left->deferred_trap.right = right;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_active_edges_to_traps (cairo_bo_edge_t *left,
+ int32_t top,
+ cairo_fill_rule_t fill_rule,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ cairo_bo_edge_t *right;
+ cairo_status_t status;
+
+ if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+ while (left != NULL) {
+ int in_out;
+
+ /* Greedily search for the closing edge, so that we generate the
+ * maximal span width with the minimal number of trapezoids.
+ */
+ in_out = left->edge.dir;
+
+ /* Check if there is a co-linear edge with an existing trap */
+ right = left->next;
+ if (left->deferred_trap.right == NULL) {
+ while (right != NULL && right->deferred_trap.right == NULL)
+ right = right->next;
+
+ if (right != NULL && edges_collinear (left, right)) {
+ /* continuation on left */
+ left->deferred_trap = right->deferred_trap;
+ right->deferred_trap.right = NULL;
+ }
+ }
+
+ /* End all subsumed traps */
+ right = left->next;
+ while (right != NULL) {
+ if (right->deferred_trap.right != NULL) {
+ status = _cairo_bo_edge_end_trap (right, top, do_traps, container);
+ if (unlikely (status))
+ return status;
+ }
+
+ in_out += right->edge.dir;
+ if (in_out == 0) {
+ /* skip co-linear edges */
+ if (right->next == NULL ||
+ ! edges_collinear (right, right->next))
+ {
+ break;
+ }
+ }
+
+ right = right->next;
+ }
+
+ status = _cairo_bo_edge_start_or_continue_trap (left, right, top,
+ do_traps, container);
+ if (unlikely (status))
+ return status;
+
+ left = right;
+ if (left != NULL)
+ left = left->next;
+ }
+ } else {
+ while (left != NULL) {
+ int in_out = 0;
+
+ right = left->next;
+ while (right != NULL) {
+ if (right->deferred_trap.right != NULL) {
+ status = _cairo_bo_edge_end_trap (right, top, do_traps, container);
+ if (unlikely (status))
+ return status;
+ }
+
+ if ((in_out++ & 1) == 0) {
+ cairo_bo_edge_t *next;
+ cairo_bool_t skip = FALSE;
+
+ /* skip co-linear edges */
+ next = right->next;
+ if (next != NULL)
+ skip = edges_collinear (right, next);
+
+ if (! skip)
+ break;
+ }
+
+ right = right->next;
+ }
+
+ status = _cairo_bo_edge_start_or_continue_trap (left, right, top,
+ do_traps, container);
+ if (unlikely (status))
+ return status;
+
+ left = right;
+ if (left != NULL)
+ left = left->next;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events,
+ int num_events,
+ cairo_fill_rule_t fill_rule,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ cairo_bo_sweep_line_t sweep_line;
+ cairo_bo_event_t *event;
+ cairo_status_t status;
+
+ _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events);
+
+ while ((event = _cairo_bo_event_dequeue (&sweep_line))) {
+ if (event->point.y != sweep_line.current_y) {
+ status = _active_edges_to_traps (sweep_line.head,
+ sweep_line.current_y,
+ fill_rule, do_traps, container);
+ if (unlikely (status))
+ return status;
+
+ sweep_line.current_y = event->point.y;
+ }
+
+ switch (event->type) {
+ case CAIRO_BO_EVENT_TYPE_START:
+ _cairo_bo_sweep_line_insert (&sweep_line, event->edge);
+ break;
+
+ case CAIRO_BO_EVENT_TYPE_STOP:
+ _cairo_bo_sweep_line_delete (&sweep_line, event->edge);
+
+ if (event->edge->deferred_trap.right != NULL) {
+ status = _cairo_bo_edge_end_trap (event->edge,
+ sweep_line.current_y,
+ do_traps, container);
+ if (unlikely (status))
+ return status;
+ }
+
+ break;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_boxes_t *boxes)
+{
+ cairo_status_t status;
+ cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
+ cairo_bo_event_t *events;
+ cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+ cairo_bo_event_t **event_ptrs;
+ cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
+ cairo_bo_edge_t *edges;
+ int num_events;
+ int i, j;
+
+ if (unlikely (polygon->num_edges == 0))
+ return CAIRO_STATUS_SUCCESS;
+
+ num_events = 2 * polygon->num_edges;
+
+ events = stack_events;
+ event_ptrs = stack_event_ptrs;
+ edges = stack_edges;
+ if (num_events > ARRAY_LENGTH (stack_events)) {
+ events = _cairo_malloc_ab_plus_c (num_events,
+ sizeof (cairo_bo_event_t) +
+ sizeof (cairo_bo_edge_t) +
+ sizeof (cairo_bo_event_t *),
+ sizeof (cairo_bo_event_t *));
+ if (unlikely (events == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ event_ptrs = (cairo_bo_event_t **) (events + num_events);
+ edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1);
+ }
+
+ for (i = j = 0; i < polygon->num_edges; i++) {
+ edges[i].edge = polygon->edges[i];
+ edges[i].deferred_trap.right = NULL;
+ edges[i].prev = NULL;
+ edges[i].next = NULL;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_START;
+ events[j].point.y = polygon->edges[i].top;
+ events[j].point.x = polygon->edges[i].line.p1.x;
+ events[j].edge = &edges[i];
+ j++;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
+ events[j].point.y = polygon->edges[i].bottom;
+ events[j].point.x = polygon->edges[i].line.p1.x;
+ events[j].edge = &edges[i];
+ j++;
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
+ fill_rule,
+ FALSE, boxes);
+ if (events != stack_events)
+ free (events);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
+ cairo_bo_event_t *events;
+ cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+ cairo_bo_event_t **event_ptrs;
+ cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
+ cairo_bo_edge_t *edges;
+ cairo_status_t status;
+ int i, j, k;
+
+ if (unlikely (traps->num_traps == 0))
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (traps->is_rectilinear);
+
+ i = 4 * traps->num_traps;
+
+ events = stack_events;
+ event_ptrs = stack_event_ptrs;
+ edges = stack_edges;
+ if (i > ARRAY_LENGTH (stack_events)) {
+ events = _cairo_malloc_ab_plus_c (i,
+ sizeof (cairo_bo_event_t) +
+ sizeof (cairo_bo_edge_t) +
+ sizeof (cairo_bo_event_t *),
+ sizeof (cairo_bo_event_t *));
+ if (unlikely (events == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ event_ptrs = (cairo_bo_event_t **) (events + i);
+ edges = (cairo_bo_edge_t *) (event_ptrs + i + 1);
+ }
+
+ for (i = j = k = 0; i < traps->num_traps; i++) {
+ edges[k].edge.top = traps->traps[i].top;
+ edges[k].edge.bottom = traps->traps[i].bottom;
+ edges[k].edge.line = traps->traps[i].left;
+ edges[k].edge.dir = 1;
+ edges[k].deferred_trap.right = NULL;
+ edges[k].prev = NULL;
+ edges[k].next = NULL;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_START;
+ events[j].point.y = traps->traps[i].top;
+ events[j].point.x = traps->traps[i].left.p1.x;
+ events[j].edge = &edges[k];
+ j++;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
+ events[j].point.y = traps->traps[i].bottom;
+ events[j].point.x = traps->traps[i].left.p1.x;
+ events[j].edge = &edges[k];
+ j++;
+ k++;
+
+ edges[k].edge.top = traps->traps[i].top;
+ edges[k].edge.bottom = traps->traps[i].bottom;
+ edges[k].edge.line = traps->traps[i].right;
+ edges[k].edge.dir = -1;
+ edges[k].deferred_trap.right = NULL;
+ edges[k].prev = NULL;
+ edges[k].next = NULL;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_START;
+ events[j].point.y = traps->traps[i].top;
+ events[j].point.x = traps->traps[i].right.p1.x;
+ events[j].edge = &edges[k];
+ j++;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
+ events[j].point.y = traps->traps[i].bottom;
+ events[j].point.x = traps->traps[i].right.p1.x;
+ events[j].edge = &edges[k];
+ j++;
+ k++;
+ }
+
+ _cairo_traps_clear (traps);
+ status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
+ fill_rule,
+ TRUE, traps);
+ traps->is_rectilinear = TRUE;
+
+ if (events != stack_events)
+ free (events);
+
+ return status;
+}
diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
new file mode 100755
index 000000000..7259add0c
--- /dev/null
+++ b/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-inline.h"
+#include "cairo-traps-private.h"
+
+#define DEBUG_PRINT_STATE 0
+#define DEBUG_EVENTS 0
+#define DEBUG_TRAPS 0
+
+typedef cairo_point_t cairo_bo_point32_t;
+
+typedef struct _cairo_bo_intersect_ordinate {
+ int32_t ordinate;
+ enum { EXACT, INEXACT } exactness;
+} cairo_bo_intersect_ordinate_t;
+
+typedef struct _cairo_bo_intersect_point {
+ cairo_bo_intersect_ordinate_t x;
+ cairo_bo_intersect_ordinate_t y;
+} cairo_bo_intersect_point_t;
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+typedef struct _cairo_bo_trap cairo_bo_trap_t;
+
+/* A deferred trapezoid of an edge */
+struct _cairo_bo_trap {
+ cairo_bo_edge_t *right;
+ int32_t top;
+};
+
+struct _cairo_bo_edge {
+ cairo_edge_t edge;
+ cairo_bo_edge_t *prev;
+ cairo_bo_edge_t *next;
+ cairo_bo_edge_t *colinear;
+ cairo_bo_trap_t deferred_trap;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef enum {
+ CAIRO_BO_EVENT_TYPE_STOP,
+ CAIRO_BO_EVENT_TYPE_INTERSECTION,
+ CAIRO_BO_EVENT_TYPE_START
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_start_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+ cairo_bo_edge_t edge;
+} cairo_bo_start_event_t;
+
+typedef struct _cairo_bo_queue_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+ cairo_bo_edge_t *e1;
+ cairo_bo_edge_t *e2;
+} cairo_bo_queue_event_t;
+
+typedef struct _pqueue {
+ int size, max_size;
+
+ cairo_bo_event_t **elements;
+ cairo_bo_event_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _cairo_bo_event_queue {
+ cairo_freepool_t pool;
+ pqueue_t pqueue;
+ cairo_bo_event_t **start_events;
+} cairo_bo_event_queue_t;
+
+typedef struct _cairo_bo_sweep_line {
+ cairo_bo_edge_t *head;
+ cairo_bo_edge_t *stopped;
+ int32_t current_y;
+ cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+#if DEBUG_TRAPS
+static void
+dump_traps (cairo_traps_t *traps, const char *filename)
+{
+ FILE *file;
+ cairo_box_t extents;
+ int n;
+
+ if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+ return;
+
+#if 0
+ if (traps->has_limits) {
+ printf ("%s: limits=(%d, %d, %d, %d)\n",
+ filename,
+ traps->limits.p1.x, traps->limits.p1.y,
+ traps->limits.p2.x, traps->limits.p2.y);
+ }
+#endif
+ _cairo_traps_extents (traps, &extents);
+ printf ("%s: extents=(%d, %d, %d, %d)\n",
+ filename,
+ extents.p1.x, extents.p1.y,
+ extents.p2.x, extents.p2.y);
+
+ file = fopen (filename, "a");
+ if (file != NULL) {
+ for (n = 0; n < traps->num_traps; n++) {
+ fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+ traps->traps[n].top,
+ traps->traps[n].bottom,
+ traps->traps[n].left.p1.x,
+ traps->traps[n].left.p1.y,
+ traps->traps[n].left.p2.x,
+ traps->traps[n].left.p2.y,
+ traps->traps[n].right.p1.x,
+ traps->traps[n].right.p1.y,
+ traps->traps[n].right.p2.x,
+ traps->traps[n].right.p2.y);
+ }
+ fprintf (file, "\n");
+ fclose (file);
+ }
+}
+
+static void
+dump_edges (cairo_bo_start_event_t *events,
+ int num_edges,
+ const char *filename)
+{
+ FILE *file;
+ int n;
+
+ if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+ return;
+
+ file = fopen (filename, "a");
+ if (file != NULL) {
+ for (n = 0; n < num_edges; n++) {
+ fprintf (file, "(%d, %d), (%d, %d) %d %d %d\n",
+ events[n].edge.edge.line.p1.x,
+ events[n].edge.edge.line.p1.y,
+ events[n].edge.edge.line.p2.x,
+ events[n].edge.edge.line.p2.y,
+ events[n].edge.edge.top,
+ events[n].edge.edge.bottom,
+ events[n].edge.edge.dir);
+ }
+ fprintf (file, "\n");
+ fclose (file);
+ }
+}
+#endif
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+ cairo_fixed_t y)
+{
+ cairo_fixed_t x, dy;
+
+ if (y == line->p1.y)
+ return line->p1.x;
+ if (y == line->p2.y)
+ return line->p2.x;
+
+ x = line->p1.x;
+ dy = line->p2.y - line->p1.y;
+ if (dy != 0) {
+ x += _cairo_fixed_mul_div_floor (y - line->p1.y,
+ line->p2.x - line->p1.x,
+ dy);
+ }
+
+ return x;
+}
+
+static inline int
+_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
+ cairo_bo_point32_t const *b)
+{
+ int cmp;
+
+ cmp = a->y - b->y;
+ if (cmp)
+ return cmp;
+
+ return a->x - b->x;
+}
+
+/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
+ * slope a is respectively greater than, equal to, or less than the
+ * slope of b.
+ *
+ * For each edge, consider the direction vector formed from:
+ *
+ * top -> bottom
+ *
+ * which is:
+ *
+ * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
+ *
+ * We then define the slope of each edge as dx/dy, (which is the
+ * inverse of the slope typically used in math instruction). We never
+ * compute a slope directly as the value approaches infinity, but we
+ * can derive a slope comparison without division as follows, (where
+ * the ? represents our compare operator).
+ *
+ * 1. slope(a) ? slope(b)
+ * 2. adx/ady ? bdx/bdy
+ * 3. (adx * bdy) ? (bdx * ady)
+ *
+ * Note that from step 2 to step 3 there is no change needed in the
+ * sign of the result since both ady and bdy are guaranteed to be
+ * greater than or equal to 0.
+ *
+ * When using this slope comparison to sort edges, some care is needed
+ * when interpreting the results. Since the slope compare operates on
+ * distance vectors from top to bottom it gives a correct left to
+ * right sort for edges that have a common top point, (such as two
+ * edges with start events at the same location). On the other hand,
+ * the sense of the result will be exactly reversed for two edges that
+ * have a common stop point.
+ */
+static inline int
+_slope_compare (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b)
+{
+ /* XXX: We're assuming here that dx and dy will still fit in 32
+ * bits. That's not true in general as there could be overflow. We
+ * should prevent that before the tessellation algorithm
+ * begins.
+ */
+ int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
+ int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+
+ /* Since the dy's are all positive by construction we can fast
+ * path several common cases.
+ */
+
+ /* First check for vertical lines. */
+ if (adx == 0)
+ return -bdx;
+ if (bdx == 0)
+ return adx;
+
+ /* Then where the two edges point in different directions wrt x. */
+ if ((adx ^ bdx) < 0)
+ return adx;
+
+ /* Finally we actually need to do the general comparison. */
+ {
+ int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
+ int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+ cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+ cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+ return _cairo_int64_cmp (adx_bdy, bdx_ady);
+ }
+}
+
+/*
+ * We need to compare the x-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 inline int
+_cairo_bo_sweep_line_compare_edges (const cairo_bo_sweep_line_t *sweep_line,
+ const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b)
+{
+ int cmp;
+
+ /* compare the edges if not identical */
+ if (! _line_equal (&a->edge.line, &b->edge.line)) {
+ if (MAX (a->edge.line.p1.x, a->edge.line.p2.x) <
+ MIN (b->edge.line.p1.x, b->edge.line.p2.x))
+ return -1;
+ else if (MIN (a->edge.line.p1.x, a->edge.line.p2.x) >
+ MAX (b->edge.line.p1.x, b->edge.line.p2.x))
+ return 1;
+
+ 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)
+{
+ event_queue->start_events = start_events;
+
+ _cairo_freepool_init (&event_queue->pool,
+ sizeof (cairo_bo_queue_event_t));
+ _pqueue_init (&event_queue->pqueue);
+ event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static cairo_status_t
+_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue,
+ cairo_bo_edge_t *edge)
+{
+ cairo_bo_point32_t point;
+
+ point.y = edge->edge.bottom;
+ point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
+ point.y);
+ return _cairo_bo_event_queue_insert (event_queue,
+ CAIRO_BO_EVENT_TYPE_STOP,
+ edge, NULL,
+ &point);
+}
+
+static void
+_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue)
+{
+ _pqueue_fini (&event_queue->pqueue);
+ _cairo_freepool_fini (&event_queue->pool);
+}
+
+static inline cairo_status_t
+_cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue,
+ cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right)
+{
+ cairo_bo_point32_t intersection;
+
+ if (MAX (left->edge.line.p1.x, left->edge.line.p2.x) <=
+ MIN (right->edge.line.p1.x, right->edge.line.p2.x))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_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 void
+_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (sweep_line->current_edge != NULL) {
+ cairo_bo_edge_t *prev, *next;
+ int cmp;
+
+ cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
+ sweep_line->current_edge,
+ edge);
+ if (cmp < 0) {
+ prev = sweep_line->current_edge;
+ next = prev->next;
+ while (next != NULL &&
+ _cairo_bo_sweep_line_compare_edges (sweep_line,
+ next, edge) < 0)
+ {
+ prev = next, next = prev->next;
+ }
+
+ prev->next = edge;
+ edge->prev = prev;
+ edge->next = next;
+ if (next != NULL)
+ next->prev = edge;
+ } else if (cmp > 0) {
+ next = sweep_line->current_edge;
+ prev = next->prev;
+ while (prev != NULL &&
+ _cairo_bo_sweep_line_compare_edges (sweep_line,
+ prev, edge) > 0)
+ {
+ next = prev, prev = next->prev;
+ }
+
+ next->prev = edge;
+ edge->next = next;
+ edge->prev = prev;
+ if (prev != NULL)
+ prev->next = edge;
+ else
+ sweep_line->head = edge;
+ } else {
+ prev = sweep_line->current_edge;
+ edge->prev = prev;
+ edge->next = prev->next;
+ if (prev->next != NULL)
+ prev->next->prev = edge;
+ prev->next = edge;
+ }
+ } else {
+ sweep_line->head = edge;
+ edge->next = NULL;
+ }
+
+ sweep_line->current_edge = edge;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (edge->prev != NULL)
+ edge->prev->next = edge->next;
+ else
+ sweep_line->head = edge->next;
+
+ if (edge->next != NULL)
+ edge->next->prev = edge->prev;
+
+ if (sweep_line->current_edge == edge)
+ sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static void
+_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right)
+{
+ if (left->prev != NULL)
+ left->prev->next = right;
+ else
+ sweep_line->head = right;
+
+ if (right->next != NULL)
+ right->next->prev = left;
+
+ left->next = right->next;
+ right->next = left;
+
+ right->prev = left->prev;
+ left->prev = right;
+}
+
+#if DEBUG_PRINT_STATE
+static void
+_cairo_bo_edge_print (cairo_bo_edge_t *edge)
+{
+ printf ("(0x%x, 0x%x)-(0x%x, 0x%x)",
+ edge->edge.line.p1.x, edge->edge.line.p1.y,
+ edge->edge.line.p2.x, edge->edge.line.p2.y);
+}
+
+static void
+_cairo_bo_event_print (cairo_bo_event_t *event)
+{
+ switch (event->type) {
+ case CAIRO_BO_EVENT_TYPE_START:
+ printf ("Start: ");
+ break;
+ case CAIRO_BO_EVENT_TYPE_STOP:
+ printf ("Stop: ");
+ break;
+ case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+ printf ("Intersection: ");
+ break;
+ }
+ printf ("(%d, %d)\t", event->point.x, event->point.y);
+ _cairo_bo_edge_print (event->e1);
+ if (event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) {
+ printf (" X ");
+ _cairo_bo_edge_print (event->e2);
+ }
+ printf ("\n");
+}
+
+static void
+_cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue)
+{
+ /* XXX: fixme to print the start/stop array too. */
+ printf ("Event queue:\n");
+}
+
+static void
+_cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line)
+{
+ cairo_bool_t first = TRUE;
+ cairo_bo_edge_t *edge;
+
+ printf ("Sweep line from edge list: ");
+ first = TRUE;
+ for (edge = sweep_line->head;
+ edge;
+ edge = edge->next)
+ {
+ if (!first)
+ printf (", ");
+ _cairo_bo_edge_print (edge);
+ first = FALSE;
+ }
+ printf ("\n");
+}
+
+static void
+print_state (const char *msg,
+ cairo_bo_event_t *event,
+ cairo_bo_event_queue_t *event_queue,
+ cairo_bo_sweep_line_t *sweep_line)
+{
+ printf ("%s ", msg);
+ _cairo_bo_event_print (event);
+ _cairo_bo_event_queue_print (event_queue);
+ _cairo_bo_sweep_line_print (sweep_line);
+ printf ("\n");
+}
+#endif
+
+#if DEBUG_EVENTS
+static void CAIRO_PRINTF_FORMAT (1, 2)
+event_log (const char *fmt, ...)
+{
+ FILE *file;
+
+ if (getenv ("CAIRO_DEBUG_EVENTS") == NULL)
+ return;
+
+ file = fopen ("bo-events.txt", "a");
+ if (file != NULL) {
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (file, fmt, ap);
+ va_end (ap);
+
+ fclose (file);
+ }
+}
+#endif
+
+#define HAS_COLINEAR(a, b) ((cairo_bo_edge_t *)(((uintptr_t)(a))&~1) == (b))
+#define IS_COLINEAR(e) (((uintptr_t)(e))&1)
+#define MARK_COLINEAR(e, v) ((cairo_bo_edge_t *)(((uintptr_t)(e))|(v)))
+
+static inline cairo_bool_t
+edges_colinear (cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+ unsigned p;
+
+ if (HAS_COLINEAR(a->colinear, b))
+ return IS_COLINEAR(a->colinear);
+
+ if (HAS_COLINEAR(b->colinear, a)) {
+ p = IS_COLINEAR(b->colinear);
+ a->colinear = MARK_COLINEAR(b, p);
+ return p;
+ }
+
+ p = 0;
+ p |= (a->edge.line.p1.x == b->edge.line.p1.x) << 0;
+ p |= (a->edge.line.p1.y == b->edge.line.p1.y) << 1;
+ p |= (a->edge.line.p2.x == b->edge.line.p2.x) << 3;
+ p |= (a->edge.line.p2.y == b->edge.line.p2.y) << 4;
+ if (p == ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 4))) {
+ a->colinear = MARK_COLINEAR(b, 1);
+ return TRUE;
+ }
+
+ if (_slope_compare (a, b)) {
+ a->colinear = MARK_COLINEAR(b, 0);
+ return FALSE;
+ }
+
+ /* The choice of y is not truly arbitrary since we must guarantee that it
+ * is greater than the start of either line.
+ */
+ if (p != 0) {
+ /* colinear if either end-point are coincident */
+ p = (((p >> 1) & p) & 5) != 0;
+ } else if (a->edge.line.p1.y < b->edge.line.p1.y) {
+ p = edge_compare_for_y_against_x (b,
+ a->edge.line.p1.y,
+ a->edge.line.p1.x) == 0;
+ } else {
+ p = edge_compare_for_y_against_x (a,
+ b->edge.line.p1.y,
+ b->edge.line.p1.x) == 0;
+ }
+
+ a->colinear = MARK_COLINEAR(b, p);
+ return p;
+}
+
+/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */
+static void
+_cairo_bo_edge_end_trap (cairo_bo_edge_t *left,
+ int32_t bot,
+ cairo_traps_t *traps)
+{
+ cairo_bo_trap_t *trap = &left->deferred_trap;
+
+ /* Only emit (trivial) non-degenerate trapezoids with positive height. */
+ if (likely (trap->top < bot)) {
+ _cairo_traps_add_trap (traps,
+ trap->top, bot,
+ &left->edge.line, &trap->right->edge.line);
+
+#if DEBUG_PRINT_STATE
+ printf ("Deferred trap: left=(%x, %x)-(%x,%x) "
+ "right=(%x,%x)-(%x,%x) top=%x, bot=%x\n",
+ left->edge.line.p1.x, left->edge.line.p1.y,
+ left->edge.line.p2.x, left->edge.line.p2.y,
+ trap->right->edge.line.p1.x, trap->right->edge.line.p1.y,
+ trap->right->edge.line.p2.x, trap->right->edge.line.p2.y,
+ trap->top, bot);
+#endif
+#if DEBUG_EVENTS
+ event_log ("end trap: %lu %lu %d %d\n",
+ (long) left,
+ (long) trap->right,
+ trap->top,
+ bot);
+#endif
+ }
+
+ trap->right = NULL;
+}
+
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline void
+_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right,
+ int top,
+ cairo_traps_t *traps)
+{
+ if (left->deferred_trap.right == right)
+ return;
+
+ assert (right);
+ if (left->deferred_trap.right != NULL) {
+ if (edges_colinear (left->deferred_trap.right, right))
+ {
+ /* continuation on right, so just swap edges */
+ left->deferred_trap.right = right;
+ return;
+ }
+
+ _cairo_bo_edge_end_trap (left, top, traps);
+ }
+
+ if (! edges_colinear (left, right)) {
+ left->deferred_trap.top = top;
+ left->deferred_trap.right = right;
+
+#if DEBUG_EVENTS
+ event_log ("begin trap: %lu %lu %d\n",
+ (long) left,
+ (long) right,
+ top);
+#endif
+ }
+}
+
+static inline void
+_active_edges_to_traps (cairo_bo_edge_t *pos,
+ int32_t top,
+ unsigned mask,
+ cairo_traps_t *traps)
+{
+ cairo_bo_edge_t *left;
+ int in_out;
+
+
+#if DEBUG_PRINT_STATE
+ printf ("Processing active edges for %x\n", top);
+#endif
+
+ in_out = 0;
+ left = pos;
+ while (pos != NULL) {
+ if (pos != left && pos->deferred_trap.right) {
+ /* XXX It shouldn't be possible to here with 2 deferred traps
+ * on colinear edges... See bug-bo-rictoz.
+ */
+ if (left->deferred_trap.right == NULL &&
+ edges_colinear (left, pos))
+ {
+ /* continuation on left */
+ left->deferred_trap = pos->deferred_trap;
+ pos->deferred_trap.right = NULL;
+ }
+ else
+ {
+ _cairo_bo_edge_end_trap (pos, top, traps);
+ }
+ }
+
+ in_out += pos->edge.dir;
+ if ((in_out & mask) == 0) {
+ /* skip co-linear edges */
+ if (pos->next == NULL || ! edges_colinear (pos, pos->next)) {
+ _cairo_bo_edge_start_or_continue_trap (left, pos, top, traps);
+ left = pos->next;
+ }
+ }
+
+ pos = pos->next;
+ }
+}
+
+/* Execute a single pass of the Bentley-Ottmann algorithm on edges,
+ * generating trapezoids according to the fill_rule and appending them
+ * to traps. */
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events,
+ int num_events,
+ unsigned fill_rule,
+ cairo_traps_t *traps,
+ int *num_intersections)
+{
+ cairo_status_t status;
+ int intersection_count = 0;
+ cairo_bo_event_queue_t event_queue;
+ cairo_bo_sweep_line_t sweep_line;
+ cairo_bo_event_t *event;
+ cairo_bo_edge_t *left, *right;
+ cairo_bo_edge_t *e1, *e2;
+
+ /* convert the fill_rule into a winding mask */
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ fill_rule = (unsigned) -1;
+ else
+ fill_rule = 1;
+
+#if DEBUG_EVENTS
+ {
+ int i;
+
+ for (i = 0; i < num_events; i++) {
+ cairo_bo_start_event_t *event =
+ ((cairo_bo_start_event_t **) start_events)[i];
+ event_log ("edge: %lu (%d, %d) (%d, %d) (%d, %d) %d\n",
+ (long) &events[i].edge,
+ event->edge.edge.line.p1.x,
+ event->edge.edge.line.p1.y,
+ event->edge.edge.line.p2.x,
+ event->edge.edge.line.p2.y,
+ event->edge.top,
+ event->edge.bottom,
+ event->edge.edge.dir);
+ }
+ }
+#endif
+
+ _cairo_bo_event_queue_init (&event_queue, start_events, num_events);
+ _cairo_bo_sweep_line_init (&sweep_line);
+
+ while ((event = _cairo_bo_event_dequeue (&event_queue))) {
+ if (event->point.y != sweep_line.current_y) {
+ for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
+ if (e1->deferred_trap.right != NULL) {
+ _cairo_bo_edge_end_trap (e1,
+ e1->edge.bottom,
+ traps);
+ }
+ }
+ sweep_line.stopped = NULL;
+
+ _active_edges_to_traps (sweep_line.head,
+ sweep_line.current_y,
+ fill_rule, traps);
+
+ sweep_line.current_y = event->point.y;
+ }
+
+#if DEBUG_EVENTS
+ event_log ("event: %d (%ld, %ld) %lu, %lu\n",
+ event->type,
+ (long) event->point.x,
+ (long) event->point.y,
+ (long) event->e1,
+ (long) event->e2);
+#endif
+
+ switch (event->type) {
+ case CAIRO_BO_EVENT_TYPE_START:
+ e1 = &((cairo_bo_start_event_t *) event)->edge;
+
+ _cairo_bo_sweep_line_insert (&sweep_line, e1);
+
+ status = _cairo_bo_event_queue_insert_stop (&event_queue, e1);
+ if (unlikely (status))
+ goto unwind;
+
+ /* check to see if this is a continuation of a stopped edge */
+ /* XXX change to an infinitesimal lengthening rule */
+ for (left = sweep_line.stopped; left; left = left->next) {
+ if (e1->edge.top <= left->edge.bottom &&
+ edges_colinear (e1, left))
+ {
+ e1->deferred_trap = left->deferred_trap;
+ if (left->prev != NULL)
+ left->prev = left->next;
+ else
+ sweep_line.stopped = left->next;
+ if (left->next != NULL)
+ left->next->prev = left->prev;
+ break;
+ }
+ }
+
+ left = e1->prev;
+ right = e1->next;
+
+ if (left != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ if (right != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+
+ case CAIRO_BO_EVENT_TYPE_STOP:
+ e1 = ((cairo_bo_queue_event_t *) event)->e1;
+ _cairo_bo_event_queue_delete (&event_queue, event);
+
+ left = e1->prev;
+ right = e1->next;
+
+ _cairo_bo_sweep_line_delete (&sweep_line, e1);
+
+ /* first, check to see if we have a continuation via a fresh edge */
+ if (e1->deferred_trap.right != NULL) {
+ e1->next = sweep_line.stopped;
+ if (sweep_line.stopped != NULL)
+ sweep_line.stopped->prev = e1;
+ sweep_line.stopped = e1;
+ e1->prev = NULL;
+ }
+
+ if (left != NULL && right != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+
+ case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+ e1 = ((cairo_bo_queue_event_t *) event)->e1;
+ e2 = ((cairo_bo_queue_event_t *) event)->e2;
+ _cairo_bo_event_queue_delete (&event_queue, event);
+
+ /* skip this intersection if its edges are not adjacent */
+ if (e2 != e1->next)
+ break;
+
+ intersection_count++;
+
+ left = e1->prev;
+ right = e2->next;
+
+ _cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
+
+ /* after the swap e2 is left of e1 */
+
+ if (left != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ if (right != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+ }
+ }
+
+ *num_intersections = intersection_count;
+ for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
+ if (e1->deferred_trap.right != NULL) {
+ _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps);
+ }
+ }
+ status = traps->status;
+ unwind:
+ _cairo_bo_event_queue_fini (&event_queue);
+
+#if DEBUG_EVENTS
+ event_log ("\n");
+#endif
+
+ return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
+ const cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule)
+{
+ int intersections;
+ cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
+ cairo_bo_start_event_t *events;
+ cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+ cairo_bo_event_t **event_ptrs;
+ cairo_bo_start_event_t *stack_event_y[64];
+ cairo_bo_start_event_t **event_y = NULL;
+ int i, num_events, y, ymin = 0, ymax = 0;
+ cairo_status_t status;
+
+ num_events = polygon->num_edges;
+ if (unlikely (0 == num_events))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (polygon->num_limits) {
+ ymin = _cairo_fixed_integer_floor (polygon->limit.p1.y);
+ ymax = _cairo_fixed_integer_ceil (polygon->limit.p2.y) - ymin;
+
+ if (ymax > 64) {
+ event_y = _cairo_malloc_ab(sizeof (cairo_bo_event_t*), ymax);
+ if (unlikely (event_y == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else {
+ event_y = stack_event_y;
+ }
+ memset (event_y, 0, ymax * sizeof(cairo_bo_event_t *));
+ }
+
+ events = stack_events;
+ event_ptrs = stack_event_ptrs;
+ if (num_events > ARRAY_LENGTH (stack_events)) {
+ events = _cairo_malloc_ab_plus_c (num_events,
+ sizeof (cairo_bo_start_event_t) +
+ sizeof (cairo_bo_event_t *),
+ sizeof (cairo_bo_event_t *));
+ if (unlikely (events == NULL)) {
+ if (event_y != stack_event_y)
+ free (event_y);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ event_ptrs = (cairo_bo_event_t **) (events + num_events);
+ }
+
+ for (i = 0; i < num_events; i++) {
+ events[i].type = CAIRO_BO_EVENT_TYPE_START;
+ events[i].point.y = polygon->edges[i].top;
+ events[i].point.x =
+ _line_compute_intersection_x_for_y (&polygon->edges[i].line,
+ events[i].point.y);
+
+ events[i].edge.edge = polygon->edges[i];
+ events[i].edge.deferred_trap.right = NULL;
+ events[i].edge.prev = NULL;
+ events[i].edge.next = NULL;
+ events[i].edge.colinear = NULL;
+
+ if (event_y) {
+ y = _cairo_fixed_integer_floor (events[i].point.y) - ymin;
+ events[i].edge.next = (cairo_bo_edge_t *) event_y[y];
+ event_y[y] = (cairo_bo_start_event_t *) &events[i];
+ } else
+ event_ptrs[i] = (cairo_bo_event_t *) &events[i];
+ }
+
+ if (event_y) {
+ for (y = i = 0; y < ymax && i < num_events; y++) {
+ cairo_bo_start_event_t *e;
+ int j = i;
+ for (e = event_y[y]; e; e = (cairo_bo_start_event_t *)e->edge.next)
+ event_ptrs[i++] = (cairo_bo_event_t *) e;
+ if (i > j + 1)
+ _cairo_bo_event_queue_sort (event_ptrs+j, i-j);
+ }
+ if (event_y != stack_event_y)
+ free (event_y);
+ } else
+ _cairo_bo_event_queue_sort (event_ptrs, i);
+ event_ptrs[i] = NULL;
+
+#if DEBUG_TRAPS
+ dump_edges (events, num_events, "bo-polygon-edges.txt");
+#endif
+
+ /* XXX: This would be the convenient place to throw in multiple
+ * passes of the Bentley-Ottmann algorithm. It would merely
+ * require storing the results of each pass into a temporary
+ * cairo_traps_t. */
+ status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events,
+ fill_rule, traps,
+ &intersections);
+#if DEBUG_TRAPS
+ dump_traps (traps, "bo-polygon-out.txt");
+#endif
+
+ if (events != stack_events)
+ free (events);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_status_t status;
+ cairo_polygon_t polygon;
+ int i;
+
+ if (unlikely (0 == traps->num_traps))
+ return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG_TRAPS
+ dump_traps (traps, "bo-traps-in.txt");
+#endif
+
+ _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
+
+ for (i = 0; i < traps->num_traps; i++) {
+ status = _cairo_polygon_add_line (&polygon,
+ &traps->traps[i].left,
+ traps->traps[i].top,
+ traps->traps[i].bottom,
+ 1);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_polygon_add_line (&polygon,
+ &traps->traps[i].right,
+ traps->traps[i].top,
+ traps->traps[i].bottom,
+ -1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+
+ _cairo_traps_clear (traps);
+ status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+ &polygon,
+ fill_rule);
+
+#if DEBUG_TRAPS
+ dump_traps (traps, "bo-traps-out.txt");
+#endif
+
+ CLEANUP:
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+#if 0
+static cairo_bool_t
+edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges,
+ int num_edges)
+
+{
+ int i, j;
+ cairo_bo_edge_t *a, *b;
+ cairo_bo_point32_t intersection;
+
+ /* We must not be given any upside-down edges. */
+ for (i = 0; i < num_edges; i++) {
+ assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0);
+ edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS;
+ edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS;
+ edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS;
+ edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS;
+ }
+
+ for (i = 0; i < num_edges; i++) {
+ for (j = 0; j < num_edges; j++) {
+ if (i == j)
+ continue;
+
+ a = &edges[i];
+ b = &edges[j];
+
+ if (! _cairo_bo_edge_intersect (a, b, &intersection))
+ continue;
+
+ printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n",
+ intersection.x,
+ intersection.y,
+ a->line.p1.x, a->line.p1.y,
+ a->line.p2.x, a->line.p2.y,
+ b->line.p1.x, b->line.p1.y,
+ b->line.p2.x, b->line.p2.y);
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#define TEST_MAX_EDGES 10
+
+typedef struct test {
+ const char *name;
+ const char *description;
+ int num_edges;
+ cairo_bo_edge_t edges[TEST_MAX_EDGES];
+} test_t;
+
+static test_t
+tests[] = {
+ {
+ "3 near misses",
+ "3 edges all intersecting very close to each other",
+ 3,
+ {
+ { { 4, 2}, {0, 0}, { 9, 9}, NULL, NULL },
+ { { 7, 2}, {0, 0}, { 2, 3}, NULL, NULL },
+ { { 5, 2}, {0, 0}, { 1, 7}, NULL, NULL }
+ }
+ },
+ {
+ "inconsistent data",
+ "Derived from random testing---was leading to skip list and edge list disagreeing.",
+ 2,
+ {
+ { { 2, 3}, {0, 0}, { 8, 9}, NULL, NULL },
+ { { 2, 3}, {0, 0}, { 6, 7}, NULL, NULL }
+ }
+ },
+ {
+ "failed sort",
+ "A test derived from random testing that leads to an inconsistent sort --- looks like we just can't attempt to validate the sweep line with edge_compare?",
+ 3,
+ {
+ { { 6, 2}, {0, 0}, { 6, 5}, NULL, NULL },
+ { { 3, 5}, {0, 0}, { 5, 6}, NULL, NULL },
+ { { 9, 2}, {0, 0}, { 5, 6}, NULL, NULL },
+ }
+ },
+ {
+ "minimal-intersection",
+ "Intersection of a two from among the smallest possible edges.",
+ 2,
+ {
+ { { 0, 0}, {0, 0}, { 1, 1}, NULL, NULL },
+ { { 1, 0}, {0, 0}, { 0, 1}, NULL, NULL }
+ }
+ },
+ {
+ "simple",
+ "A simple intersection of two edges at an integer (2,2).",
+ 2,
+ {
+ { { 1, 1}, {0, 0}, { 3, 3}, NULL, NULL },
+ { { 2, 1}, {0, 0}, { 2, 3}, NULL, NULL }
+ }
+ },
+ {
+ "bend-to-horizontal",
+ "With intersection truncation one edge bends to horizontal",
+ 2,
+ {
+ { { 9, 1}, {0, 0}, {3, 7}, NULL, NULL },
+ { { 3, 5}, {0, 0}, {9, 9}, NULL, NULL }
+ }
+ }
+};
+
+/*
+ {
+ "endpoint",
+ "An intersection that occurs at the endpoint of a segment.",
+ {
+ { { 4, 6}, { 5, 6}, NULL, { { NULL }} },
+ { { 4, 5}, { 5, 7}, NULL, { { NULL }} },
+ { { 0, 0}, { 0, 0}, NULL, { { NULL }} },
+ }
+ }
+ {
+ name = "overlapping",
+ desc = "Parallel segments that share an endpoint, with different slopes.",
+ edges = {
+ { top = { x = 2, y = 0}, bottom = { x = 1, y = 1}},
+ { top = { x = 2, y = 0}, bottom = { x = 0, y = 2}},
+ { top = { x = 0, y = 3}, bottom = { x = 1, y = 3}},
+ { top = { x = 0, y = 3}, bottom = { x = 2, y = 3}},
+ { top = { x = 0, y = 4}, bottom = { x = 0, y = 6}},
+ { top = { x = 0, y = 5}, bottom = { x = 0, y = 6}}
+ }
+ },
+ {
+ name = "hobby_stage_3",
+ desc = "A particularly tricky part of the 3rd stage of the 'hobby' test below.",
+ edges = {
+ { top = { x = -1, y = -2}, bottom = { x = 4, y = 2}},
+ { top = { x = 5, y = 3}, bottom = { x = 9, y = 5}},
+ { top = { x = 5, y = 3}, bottom = { x = 6, y = 3}},
+ }
+ },
+ {
+ name = "hobby",
+ desc = "Example from John Hobby's paper. Requires 3 passes of the iterative algorithm.",
+ edges = {
+ { top = { x = 0, y = 0}, bottom = { x = 9, y = 5}},
+ { top = { x = 0, y = 0}, bottom = { x = 13, y = 6}},
+ { top = { x = -1, y = -2}, bottom = { x = 9, y = 5}}
+ }
+ },
+ {
+ name = "slope",
+ desc = "Edges with same start/stop points but different slopes",
+ edges = {
+ { top = { x = 4, y = 1}, bottom = { x = 6, y = 3}},
+ { top = { x = 4, y = 1}, bottom = { x = 2, y = 3}},
+ { top = { x = 2, y = 4}, bottom = { x = 4, y = 6}},
+ { top = { x = 6, y = 4}, bottom = { x = 4, y = 6}}
+ }
+ },
+ {
+ name = "horizontal",
+ desc = "Test of a horizontal edge",
+ edges = {
+ { top = { x = 1, y = 1}, bottom = { x = 6, y = 6}},
+ { top = { x = 2, y = 3}, bottom = { x = 5, y = 3}}
+ }
+ },
+ {
+ name = "vertical",
+ desc = "Test of a vertical edge",
+ edges = {
+ { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}},
+ { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}}
+ }
+ },
+ {
+ name = "congruent",
+ desc = "Two overlapping edges with the same slope",
+ edges = {
+ { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}},
+ { top = { x = 5, y = 2}, bottom = { x = 5, y = 6}},
+ { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}}
+ }
+ },
+ {
+ name = "multi",
+ desc = "Several segments with a common intersection point",
+ edges = {
+ { top = { x = 1, y = 2}, bottom = { x = 5, y = 4} },
+ { top = { x = 1, y = 1}, bottom = { x = 5, y = 5} },
+ { top = { x = 2, y = 1}, bottom = { x = 4, y = 5} },
+ { top = { x = 4, y = 1}, bottom = { x = 2, y = 5} },
+ { top = { x = 5, y = 1}, bottom = { x = 1, y = 5} },
+ { top = { x = 5, y = 2}, bottom = { x = 1, y = 4} }
+ }
+ }
+};
+*/
+
+static int
+run_test (const char *test_name,
+ cairo_bo_edge_t *test_edges,
+ int num_edges)
+{
+ int i, intersections, passes;
+ cairo_bo_edge_t *edges;
+ cairo_array_t intersected_edges;
+
+ printf ("Testing: %s\n", test_name);
+
+ _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t));
+
+ intersections = _cairo_bentley_ottmann_intersect_edges (test_edges, num_edges, &intersected_edges);
+ if (intersections)
+ printf ("Pass 1 found %d intersections:\n", intersections);
+
+
+ /* XXX: Multi-pass Bentley-Ottmmann. Preferable would be to add a
+ * pass of Hobby's tolerance-square algorithm instead. */
+ passes = 1;
+ while (intersections) {
+ int num_edges = _cairo_array_num_elements (&intersected_edges);
+ passes++;
+ edges = _cairo_malloc_ab (num_edges, sizeof (cairo_bo_edge_t));
+ assert (edges != NULL);
+ memcpy (edges, _cairo_array_index (&intersected_edges, 0), num_edges * sizeof (cairo_bo_edge_t));
+ _cairo_array_fini (&intersected_edges);
+ _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t));
+ intersections = _cairo_bentley_ottmann_intersect_edges (edges, num_edges, &intersected_edges);
+ free (edges);
+
+ if (intersections){
+ printf ("Pass %d found %d remaining intersections:\n", passes, intersections);
+ } else {
+ if (passes > 3)
+ for (i = 0; i < passes; i++)
+ printf ("*");
+ printf ("No remainining intersections found after pass %d\n", passes);
+ }
+ }
+
+ if (edges_have_an_intersection_quadratic (_cairo_array_index (&intersected_edges, 0),
+ _cairo_array_num_elements (&intersected_edges)))
+ printf ("*** FAIL ***\n");
+ else
+ printf ("PASS\n");
+
+ _cairo_array_fini (&intersected_edges);
+
+ return 0;
+}
+
+#define MAX_RANDOM 300
+
+int
+main (void)
+{
+ char random_name[] = "random-XX";
+ cairo_bo_edge_t random_edges[MAX_RANDOM], *edge;
+ unsigned int i, num_random;
+ test_t *test;
+
+ for (i = 0; i < ARRAY_LENGTH (tests); i++) {
+ test = &tests[i];
+ run_test (test->name, test->edges, test->num_edges);
+ }
+
+ for (num_random = 0; num_random < MAX_RANDOM; num_random++) {
+ srand (0);
+ for (i = 0; i < num_random; i++) {
+ do {
+ edge = &random_edges[i];
+ edge->line.p1.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+ edge->line.p1.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+ edge->line.p2.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+ edge->line.p2.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+ if (edge->line.p1.y > edge->line.p2.y) {
+ int32_t tmp = edge->line.p1.y;
+ edge->line.p1.y = edge->line.p2.y;
+ edge->line.p2.y = tmp;
+ }
+ } while (edge->line.p1.y == edge->line.p2.y);
+ }
+
+ sprintf (random_name, "random-%02d", num_random);
+
+ run_test (random_name, random_edges, num_random);
+ }
+
+ return 0;
+}
+#endif
diff --git a/src/cairo-beos-surface.cpp b/src/cairo-beos-surface.cpp
new file mode 100755
index 000000000..c97641685
--- /dev/null
+++ b/src/cairo-beos-surface.cpp
@@ -0,0 +1,984 @@
+/* vim:set ts=8 sw=4 noet cin: */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Christian Biesinger <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):
+ */
+
+// This is a C++ file in order to use the C++ BeOS API
+
+#include "cairoint.h"
+
+#include "cairo-beos.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+
+#include <new>
+
+#include <Bitmap.h>
+#include <Region.h>
+#if 0
+#include <DirectWindow.h>
+#endif
+#include <Screen.h>
+#include <Window.h>
+#include <Locker.h>
+
+/**
+ * SECTION:beos-surface
+ * @Title: BeOS Surfaces
+ * @Short_Description: BeOS surface support
+ * @See_Also: #cairo_surface_t
+ *
+ * The BeOS surface is used to render cairo graphics to BeOS views
+ * and bitmaps.
+ **/
+
+#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS)
+
+struct cairo_beos_surface_t {
+ cairo_surface_t base;
+
+ cairo_region_t *clip_region;
+
+ BView* view;
+
+ /*
+ * A view is either attached to a bitmap, a window, or unattached.
+ * If it is attached to a window, we can copy data out of it using BScreen.
+ * If it is attached to a bitmap, we can read the bitmap data.
+ * If it is not attached, it doesn't draw anything, we need not bother.
+ *
+ * Since there doesn't seem to be a way to get the bitmap from a view if it
+ * is attached to one, we have to use a special surface creation function.
+ */
+
+ BBitmap* bitmap;
+
+ // If true, surface and view should be deleted when this surface is
+ // destroyed
+ bool owns_bitmap_view;
+};
+
+class AutoLockView {
+ public:
+ AutoLockView(BView* view) : mView(view) {
+ mOK = mView->LockLooper();
+ }
+
+ ~AutoLockView() {
+ if (mOK)
+ mView->UnlockLooper();
+ }
+
+ operator bool() {
+ return mOK;
+ }
+
+ private:
+ BView* mView;
+ bool mOK;
+};
+
+static cairo_surface_t *
+_cairo_beos_surface_create_internal (BView* view,
+ BBitmap* bmp,
+ bool owns_bitmap_view = false);
+
+static inline BRect
+_cairo_rectangle_to_brect (const cairo_rectangle_int_t* rect)
+{
+ // A BRect is one pixel wider than you'd think
+ return BRect (rect->x, rect->y,
+ rect->x + rect->width - 1,
+ rect->y + rect->height - 1);
+}
+
+static inline cairo_rectangle_int_t
+_brect_to_cairo_rectangle (const BRect &rect)
+{
+ cairo_rectangle_int_t retval;
+ retval.x = floor (rect.left);
+ retval.y = floor (rect.top);
+ retval.width = ceil (rect.right) - retval.x + 1;
+ retval.height = ceil (rect.bottom) - rectval.y + 1;
+ return retval;
+}
+
+static inline rgb_color
+_cairo_color_to_be_color (const cairo_color_t *color)
+{
+ // This factor ensures a uniform distribution of numbers
+ const float factor = 256 - 1e-5;
+ // Using doubles to have non-premultiplied colors
+ rgb_color be_color = { uint8(color->red * factor),
+ uint8(color->green * factor),
+ uint8(color->blue * factor),
+ uint8(color->alpha * factor) };
+
+ return be_color;
+}
+
+enum ViewCopyStatus {
+ OK,
+ NOT_VISIBLE, // The view or the interest rect is not visible on screen
+ ERROR // The view was visible, but the rect could not be copied. Probably OOM
+};
+
+/**
+ * _cairo_beos_view_to_bitmap:
+ * @bitmap: [out] The resulting bitmap.
+ * @rect: [out] The rectangle that was copied, in the view's coordinate system
+ * @interestRect: If non-null, only this part of the view will be copied (view's coord system).
+ *
+ * Gets the contents of the view as a BBitmap*. Caller must delete the bitmap.
+ **/
+static ViewCopyStatus
+_cairo_beos_view_to_bitmap (BView* view,
+ BBitmap** bitmap,
+ BRect* rect = NULL,
+ const BRect* interestRect = NULL)
+{
+ *bitmap = NULL;
+
+ BWindow* wnd = view->Window();
+ // If we have no window, can't do anything
+ if (!wnd)
+ return NOT_VISIBLE;
+
+ view->Sync();
+ wnd->Sync();
+
+#if 0
+ // Is it a direct window?
+ BDirectWindow* directWnd = dynamic_cast<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;
+}
+
+static void
+unpremultiply_bgra (unsigned char* data,
+ int width,
+ int height,
+ int stride,
+ unsigned char* retdata)
+{
+ unsigned char* end = data + stride * height;
+ for (unsigned char* in = data, *out = retdata;
+ in < end;
+ in += stride, out += stride)
+ {
+ for (int i = 0; i < width; i ++) {
+ uint8_t *b = &out[4*i];
+ uint32_t pixel;
+ uint8_t alpha;
+
+ memcpy (&pixel, &data[4*i], sizeof (uint32_t));
+ alpha = pixel & 0xff;
+ if (alpha == 0) {
+ b[0] = b[1] = b[2] = b[3] = 0;
+ } else {
+ b[0] = (((pixel >> 24) & 0xff) * 255 + alpha / 2) / alpha;
+ b[1] = (((pixel >> 16) & 0xff) * 255 + alpha / 2) / alpha;
+ b[2] = (((pixel >> 8) & 0xff) * 255 + alpha / 2) / alpha;
+ b[3] = alpha;
+ }
+ }
+ }
+}
+
+static inline int
+multiply_alpha (int alpha, int color)
+{
+ int temp = (alpha * color) + 0x80;
+ return ((temp + (temp >> 8)) >> 8);
+}
+
+static unsigned char*
+premultiply_bgra (unsigned char* data,
+ int width,
+ int height,
+ int stride)
+{
+ uint8_t * retdata = reinterpret_cast<unsigned char*>(_cairo_malloc_ab(height, stride));
+ if (!retdata)
+ return NULL;
+
+ uint8_t * end = data + stride * height;
+ for (uint8_t * in = data, *out = retdata;
+ in < end;
+ in += stride, out += stride)
+ {
+ for (int i = 0; i < width; i ++) {
+ uint8_t *base = &in[4*i];
+ uint8_t alpha = base[3];
+ uint32_t p;
+
+ if (alpha == 0) {
+ p = 0;
+ } else {
+ uint8_t blue = base[0];
+ uint8_t green = base[1];
+ uint8_t red = base[2];
+
+ if (alpha != 0xff) {
+ blue = multiply_alpha (alpha, blue);
+ green = multiply_alpha (alpha, green);
+ red = multiply_alpha (alpha, red);
+ }
+ p = (alpha << 0) | (red << 8) | (green << 16) | (blue << 24);
+ }
+ memcpy (&out[4*i], &p, sizeof (uint32_t));
+ }
+ }
+ return retdata;
+}
+
+static cairo_int_status_t
+_cairo_beos_surface_set_clip_region (cairo_beos_surface_t *surface,
+ cairo_region_t *region)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+ AutoLockView locker(surface->view);
+ assert (locker);
+
+ if (region == surface->clip_region)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ cairo_region_destroy (surface->clip_region);
+ surface->clip_region = cairo_region_reference (region);
+
+ if (region == NULL) {
+ // No clipping
+ surface->view->ConstrainClippingRegion(NULL);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ int count = cairo_region_num_rectangles (region);
+ BRegion bregion;
+ for (int i = 0; i < count; ++i) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (region, i, &rect);
+ // Have to subtract one, because for pixman, the second coordinate
+ // lies outside the rectangle.
+ bregion.Include (_cairo_rectangle_to_brect (&rect));
+ }
+ surface->view->ConstrainClippingRegion(&bregion);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+/**
+ * _cairo_beos_bitmap_to_surface:
+ *
+ * Returns an addrefed image surface for a BBitmap. The bitmap need not outlive
+ * the surface.
+ **/
+static cairo_image_surface_t*
+_cairo_beos_bitmap_to_surface (BBitmap* bitmap)
+{
+ color_space format = bitmap->ColorSpace();
+ if (format != B_RGB32 && format != B_RGBA32) {
+ BBitmap bmp(bitmap->Bounds(), B_RGB32, true);
+ BView view(bitmap->Bounds(), "Cairo bitmap drawing view",
+ B_FOLLOW_ALL_SIDES, 0);
+ bmp.AddChild(&view);
+
+ view.LockLooper();
+
+ view.DrawBitmap(bitmap, BPoint(0.0, 0.0));
+ view.Sync();
+
+ cairo_image_surface_t* imgsurf = _cairo_beos_bitmap_to_surface(&bmp);
+
+ view.UnlockLooper();
+ bmp.RemoveChild(&view);
+ return imgsurf;
+ }
+
+ cairo_format_t cformat = format == B_RGB32 ?
+ CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
+
+ BRect bounds(bitmap->Bounds());
+ unsigned char* bits = reinterpret_cast<unsigned char*>(bitmap->Bits());
+ int width = bounds.IntegerWidth() + 1;
+ int height = bounds.IntegerHeight() + 1;
+ unsigned char* premultiplied;
+ if (cformat == CAIRO_FORMAT_ARGB32) {
+ premultiplied = premultiply_bgra (bits, width, height,
+ bitmap->BytesPerRow());
+ } else {
+ premultiplied = reinterpret_cast<unsigned char*>(
+ _cairo_malloc_ab(bitmap->BytesPerRow(), height));
+ if (premultiplied)
+ memcpy(premultiplied, bits, bitmap->BytesPerRow() * height);
+ }
+ if (!premultiplied)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ cairo_image_surface_t* surf = reinterpret_cast<cairo_image_surface_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_bgra (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
+ // XXX This is a fundamental compositing operator, it has to work!
+#if 1
+ return false;
+#else
+ *beos_op = B_OP_ADD;
+ return true;
+#endif
+
+ case CAIRO_OPERATOR_CLEAR:
+ // Does not map to B_OP_ERASE - it replaces the dest with the low
+ // color, instead of transparency; could be done by setting low
+ // color appropriately.
+
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_SATURATE:
+
+ default:
+ return false;
+ }
+}
+
+static cairo_surface_t *
+_cairo_beos_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<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:
+ return NULL;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ bmp = new BBitmap(rect, B_RGBA32, true);
+ break;
+ case CAIRO_CONTENT_COLOR:
+ // Match the color depth
+ if (surface->bitmap) {
+ color_space space = surface->bitmap->ColorSpace();
+ // No alpha was requested -> make sure not to return
+ // a surface with alpha
+ if (space == B_RGBA32)
+ space = B_RGB32;
+ if (space == B_RGBA15)
+ space = B_RGB15;
+ bmp = new BBitmap(rect, space, true);
+ } else {
+ BScreen scr(surface->view->Window());
+ color_space space = B_RGB32;
+ if (scr.IsValid())
+ space = scr.ColorSpace();
+ bmp = new BBitmap(rect, space, true);
+ }
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ return NULL;
+ }
+ BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0);
+ bmp->AddChild(view);
+ return _cairo_beos_surface_create_internal(view, bmp, true);
+}
+
+static cairo_status_t
+_cairo_beos_surface_finish (void *abstract_surface)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<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;
+ }
+
+ cairo_region_destroy (surface->clip_region);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_beos_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<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 (unlikely ((*image_out)->base.status))
+ return (*image_out)->base.status;
+
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ BBitmap* bmp;
+ if (_cairo_beos_view_to_bitmap(surface->view, &bmp) != OK)
+ return CAIRO_STATUS_NO_MEMORY; /// XXX incorrect if the error was NOT_VISIBLE
+
+ *image_out = _cairo_beos_bitmap_to_surface (bmp);
+ if (unlikely ((*image_out)->base.status)) {
+ delete bmp;
+ return (*image_out)->base.status;
+ }
+ *image_extra = bmp;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_beos_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+
+ if (image_extra != NULL) {
+ BBitmap* bmp = static_cast<BBitmap*>(image_extra);
+ delete bmp;
+ }
+}
+
+static cairo_status_t
+_cairo_beos_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+
+ AutoLockView locker(surface->view);
+ if (!locker) {
+ *image_out = NULL;
+ *image_extra = NULL;
+ return (cairo_status_t) CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ if (surface->bitmap) {
+ surface->view->Sync();
+ *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap);
+ if (unlikely ((*image_out)->base.status))
+ return (*image_out)->base.status;
+
+ image_rect->x = 0;
+ image_rect->y = 0;
+ image_rect->width = (*image_out)->width;
+ image_rect->height = (*image_out)->height;
+
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ BRect b_interest_rect (_cairo_rectangle_to_brect (interest_rect));
+
+ BRect rect;
+ BBitmap* bitmap;
+ ViewCopyStatus status = _cairo_beos_view_to_bitmap(surface->view, &bitmap,
+ &rect, &b_interest_rect);
+ if (status == NOT_VISIBLE) {
+ *image_out = NULL;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ if (status == ERROR)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ *image_rect = _brect_to_cairo_rectangle(rect);
+ *image_out = _cairo_beos_bitmap_to_surface(bitmap);
+ delete bitmap;
+ if (unlikely ((*image_out)->base.status))
+ return (*image_out)->base.status;
+
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static void
+_cairo_beos_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *intersect_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<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);
+
+ surface->view->DrawBitmap (bitmap_to_draw,
+ _cairo_rectangle_to_brect (image_rect));
+
+ surface->view->PopState();
+
+ delete bitmap_to_draw;
+ cairo_surface_destroy(&image->base);
+}
+
+static cairo_int_status_t
+_cairo_beos_surface_composite (cairo_operator_t op,
+ cairo_pattern_t *src,
+ cairo_pattern_t *mask,
+ void *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ dst);
+ cairo_int_status_t status;
+ AutoLockView locker(surface->view);
+ if (!locker)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ drawing_mode mode;
+ if (!_cairo_op_to_be_op(op, &mode))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ // XXX Masks are not yet supported
+ if (mask)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ // XXX should eventually support the others
+ if (src->type != CAIRO_PATTERN_TYPE_SURFACE ||
+ src->extend != CAIRO_EXTEND_NONE)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ // Can we maybe support other matrices as well? (scale? if the filter is right)
+ int itx, ity;
+ if (!_cairo_matrix_is_integer_translation(&src->matrix, &itx, &ity))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_beos_surface_set_clip_region (surface, clip_region);
+ if (unlikely (status))
+ return status;
+
+ BRect srcRect(src_x + itx,
+ src_y + ity,
+ src_x + itx + width - 1,
+ src_y + ity + height - 1);
+ BRect dstRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1);
+
+ cairo_surface_t* src_surface = reinterpret_cast<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;
+ }
+
+ // Draw it on screen.
+ surface->view->PushState();
+
+ // If our image rect is only a subrect of the desired size, and we
+ // aren't using B_OP_ALPHA, then we need to fill the rect first.
+ if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) {
+ rgb_color black = { 0, 0, 0, 0 };
+
+ surface->view->SetDrawingMode(mode);
+ surface->view->SetHighColor(black);
+ surface->view->FillRect(dstRect);
+ }
+
+ if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) {
+ mode = B_OP_COPY;
+ }
+ surface->view->SetDrawingMode(mode);
+
+ if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
+ surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
+ else
+ surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+
+ surface->view->DrawBitmap(bmp, srcRect, dstRect);
+
+ surface->view->PopState();
+
+ if (free_bmp)
+ delete bmp;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+static cairo_int_status_t
+_cairo_beos_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects,
+ cairo_region_t *clip_region)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+ cairo_int_status_t status;
+
+ if (num_rects <= 0)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ AutoLockView locker(surface->view);
+ if (!locker)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ drawing_mode mode;
+ if (!_cairo_op_to_be_op(op, &mode))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_beos_surface_set_clip_region (surface, clip_region);
+ if (unlikely (status))
+ return status;
+
+ rgb_color be_color = _cairo_color_to_be_color(color);
+
+ if (mode == B_OP_ALPHA && be_color.alpha == 0xFF)
+ mode = B_OP_COPY;
+
+ // For CAIRO_OPERATOR_SOURCE, cairo expects us to use the premultiplied
+ // color info. This is only relevant when drawing into an rgb24 buffer
+ // (as for others, we can convert when asked for the image)
+ if (mode == B_OP_COPY && be_color.alpha != 0xFF &&
+ (!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32))
+ {
+ be_color.red = color->red_short >> 8;
+ be_color.green = color->green_short >> 8;
+ be_color.blue = color->blue_short >> 8;
+ }
+
+ surface->view->PushState();
+
+ surface->view->SetDrawingMode(mode);
+ surface->view->SetHighColor(be_color);
+ if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
+ surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
+ else
+ surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
+
+ for (int i = 0; i < num_rects; ++i)
+ surface->view->FillRect (_cairo_rectangle_to_brect (&rects[i]));
+
+ surface->view->PopState();
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_beos_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+ AutoLockView locker(surface->view);
+ if (!locker)
+ return FALSE;
+
+ *rectangle = _brect_to_cairo_rectangle (surface->view->Bounds());
+ return TRUE;
+}
+
+static const struct _cairo_surface_backend cairo_beos_surface_backend = {
+ CAIRO_SURFACE_TYPE_BEOS,
+ _cairo_beos_surface_create_similar,
+ _cairo_beos_surface_finish,
+ _cairo_beos_surface_acquire_source_image,
+ _cairo_beos_surface_release_source_image,
+ _cairo_beos_surface_acquire_dest_image,
+ _cairo_beos_surface_release_dest_image,
+ NULL, /* clone_similar */
+ _cairo_beos_surface_composite, /* composite */
+ _cairo_beos_surface_fill_rectangles,
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_beos_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ NULL /* show_glyphs */
+};
+
+static cairo_surface_t *
+_cairo_beos_surface_create_internal (BView* view,
+ BBitmap* bmp,
+ bool owns_bitmap_view)
+{
+ // Must use malloc, because cairo code will use free() on the surface
+ cairo_beos_surface_t *surface = static_cast<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,
+ NULL, /* device */
+ content);
+
+ surface->view = view;
+ surface->bitmap = bmp;
+ surface->owns_bitmap_view = owns_bitmap_view;
+
+ surface->clip_region = NULL;
+
+ return &surface->base;
+}
+
+/**
+ * cairo_beos_surface_create:
+ * @view: The view to draw on
+ *
+ * Creates a Cairo surface that draws onto a BeOS BView.
+ * The caller must ensure that the view does not get deleted before the surface.
+ * If the view is attached to a bitmap rather than an on-screen window, use
+ * cairo_beos_surface_create_for_bitmap() instead of this function.
+ *
+ * Since: TBD
+ **/
+cairo_surface_t *
+cairo_beos_surface_create (BView* view)
+{
+ return cairo_beos_surface_create_for_bitmap(view, NULL);
+}
+
+/**
+ * cairo_beos_surface_create_for_bitmap:
+ * @view: The view to draw on
+ * @bmp: The bitmap to which the view is attached
+ *
+ * Creates a Cairo surface that draws onto a BeOS BView which is attached to a
+ * BBitmap.
+ * The caller must ensure that the view and the bitmap do not get deleted
+ * before the surface.
+ *
+ * For views that draw to a bitmap (as opposed to a screen), use this function
+ * rather than cairo_beos_surface_create(). Not using this function WILL lead to
+ * incorrect behaviour.
+ *
+ * For now, only views that draw to the entire area of bmp are supported.
+ * The view must already be attached to the bitmap.
+ *
+ * Since: TBD
+ **/
+cairo_surface_t *
+cairo_beos_surface_create_for_bitmap (BView* view,
+ BBitmap* bmp)
+{
+ return _cairo_beos_surface_create_internal(view, bmp);
+}
diff --git a/src/cairo-beos.h b/src/cairo-beos.h
new file mode 100755
index 000000000..fdb89a6c4
--- /dev/null
+++ b/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/src/cairo-botor-scan-converter.c b/src/cairo-botor-scan-converter.c
new file mode 100755
index 000000000..515305bf2
--- /dev/null
+++ b/src/cairo-botor-scan-converter.c
@@ -0,0 +1,2164 @@
+/*
+ * 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-inline.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-inline.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);
+}
+
+static struct cell *
+coverage_alloc (sweep_line_t *sweep_line,
+ struct cell *tail,
+ int x)
+{
+ struct cell *cell;
+
+ cell = _cairo_freepool_alloc (&sweep_line->coverage.pool);
+ if (unlikely (NULL == cell)) {
+ longjmp (sweep_line->unwind,
+ _cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ tail->prev->next = cell;
+ cell->prev = tail->prev;
+ cell->next = tail;
+ tail->prev = cell;
+ cell->x = x;
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ sweep_line->coverage.count++;
+ return cell;
+}
+
+inline static struct cell *
+coverage_find (sweep_line_t *sweep_line, int x)
+{
+ struct cell *cell;
+
+ cell = sweep_line->coverage.cursor;
+ if (unlikely (cell->x > x)) {
+ do {
+ if (cell->prev->x < x)
+ break;
+ cell = cell->prev;
+ } while (TRUE);
+ } else {
+ if (cell->x == x)
+ return cell;
+
+ do {
+ UNROLL3({
+ cell = cell->next;
+ if (cell->x >= x)
+ break;
+ });
+ } while (TRUE);
+ }
+
+ if (cell->x != x)
+ cell = coverage_alloc (sweep_line, cell, x);
+
+ return sweep_line->coverage.cursor = cell;
+}
+
+static void
+coverage_render_cells (sweep_line_t *sweep_line,
+ cairo_fixed_t left, cairo_fixed_t right,
+ cairo_fixed_t y1, cairo_fixed_t y2,
+ int sign)
+{
+ int fx1, fx2;
+ int ix1, ix2;
+ int dx, dy;
+
+ /* Orient the edge left-to-right. */
+ dx = right - left;
+ if (dx >= 0) {
+ ix1 = _cairo_fixed_integer_part (left);
+ fx1 = _cairo_fixed_fractional_part (left);
+
+ ix2 = _cairo_fixed_integer_part (right);
+ fx2 = _cairo_fixed_fractional_part (right);
+
+ dy = y2 - y1;
+ } else {
+ ix1 = _cairo_fixed_integer_part (right);
+ fx1 = _cairo_fixed_fractional_part (right);
+
+ ix2 = _cairo_fixed_integer_part (left);
+ fx2 = _cairo_fixed_fractional_part (left);
+
+ dx = -dx;
+ sign = -sign;
+ dy = y1 - y2;
+ y1 = y2 - dy;
+ y2 = y1 + dy;
+ }
+
+ /* Add coverage for all pixels [ix1,ix2] on this row crossed
+ * by the edge. */
+ {
+ struct quorem y = floored_divrem ((STEP_X - fx1)*dy, dx);
+ struct cell *cell;
+
+ cell = sweep_line->coverage.cursor;
+ if (cell->x != ix1) {
+ if (unlikely (cell->x > ix1)) {
+ do {
+ if (cell->prev->x < ix1)
+ break;
+ cell = cell->prev;
+ } while (TRUE);
+ } else do {
+ UNROLL3({
+ if (cell->x >= ix1)
+ break;
+ cell = cell->next;
+ });
+ } while (TRUE);
+
+ if (cell->x != ix1)
+ cell = coverage_alloc (sweep_line, cell, ix1);
+ }
+
+ cell->uncovered_area += sign * y.quo * (STEP_X + fx1);
+ cell->covered_height += sign * y.quo;
+ y.quo += y1;
+
+ cell = cell->next;
+ if (cell->x != ++ix1)
+ cell = coverage_alloc (sweep_line, cell, ix1);
+ if (ix1 < ix2) {
+ struct quorem dydx_full = floored_divrem (STEP_X*dy, dx);
+
+ do {
+ cairo_fixed_t y_skip = dydx_full.quo;
+ y.rem += dydx_full.rem;
+ if (y.rem >= dx) {
+ ++y_skip;
+ y.rem -= dx;
+ }
+
+ y.quo += y_skip;
+
+ y_skip *= sign;
+ cell->covered_height += y_skip;
+ cell->uncovered_area += y_skip*STEP_X;
+
+ cell = cell->next;
+ if (cell->x != ++ix1)
+ cell = coverage_alloc (sweep_line, cell, ix1);
+ } while (ix1 != ix2);
+ }
+ cell->uncovered_area += sign*(y2 - y.quo)*fx2;
+ cell->covered_height += sign*(y2 - y.quo);
+ sweep_line->coverage.cursor = cell;
+ }
+}
+
+inline static void
+full_inc_edge (edge_t *edge)
+{
+ edge->x.quo += edge->dxdy_full.quo;
+ edge->x.rem += edge->dxdy_full.rem;
+ if (edge->x.rem >= 0) {
+ ++edge->x.quo;
+ edge->x.rem -= edge->dy;
+ }
+}
+
+static void
+full_add_edge (sweep_line_t *sweep_line, edge_t *edge, int sign)
+{
+ struct cell *cell;
+ cairo_fixed_t x1, x2;
+ int ix1, ix2;
+ int frac;
+
+ edge->current_sign = sign;
+
+ ix1 = _cairo_fixed_integer_part (edge->x.quo);
+
+ if (edge->vertical) {
+ frac = _cairo_fixed_fractional_part (edge->x.quo);
+ cell = coverage_find (sweep_line, ix1);
+ cell->covered_height += sign * STEP_Y;
+ cell->uncovered_area += sign * 2 * frac * STEP_Y;
+ return;
+ }
+
+ x1 = edge->x.quo;
+ full_inc_edge (edge);
+ x2 = edge->x.quo;
+
+ ix2 = _cairo_fixed_integer_part (edge->x.quo);
+
+ /* Edge is entirely within a column? */
+ if (likely (ix1 == ix2)) {
+ frac = _cairo_fixed_fractional_part (x1) +
+ _cairo_fixed_fractional_part (x2);
+ cell = coverage_find (sweep_line, ix1);
+ cell->covered_height += sign * STEP_Y;
+ cell->uncovered_area += sign * frac * STEP_Y;
+ return;
+ }
+
+ coverage_render_cells (sweep_line, x1, x2, 0, STEP_Y, sign);
+}
+
+static void
+full_nonzero (sweep_line_t *sweep_line)
+{
+ cairo_list_t *pos;
+
+ sweep_line->is_vertical = TRUE;
+ pos = sweep_line->active.next;
+ do {
+ edge_t *left = link_to_edge (pos), *right;
+ int winding = left->edge.dir;
+
+ sweep_line->is_vertical &= left->vertical;
+
+ pos = left->link.next;
+ do {
+ if (unlikely (pos == &sweep_line->active)) {
+ full_add_edge (sweep_line, left, +1);
+ return;
+ }
+
+ right = link_to_edge (pos);
+ pos = pos->next;
+ sweep_line->is_vertical &= right->vertical;
+
+ winding += right->edge.dir;
+ if (0 == winding) {
+ if (pos == &sweep_line->active ||
+ link_to_edge (pos)->x.quo != right->x.quo)
+ {
+ break;
+ }
+ }
+
+ if (! right->vertical)
+ full_inc_edge (right);
+ } while (TRUE);
+
+ full_add_edge (sweep_line, left, +1);
+ full_add_edge (sweep_line, right, -1);
+ } while (pos != &sweep_line->active);
+}
+
+static void
+full_evenodd (sweep_line_t *sweep_line)
+{
+ cairo_list_t *pos;
+
+ sweep_line->is_vertical = TRUE;
+ pos = sweep_line->active.next;
+ do {
+ edge_t *left = link_to_edge (pos), *right;
+ int winding = 0;
+
+ sweep_line->is_vertical &= left->vertical;
+
+ pos = left->link.next;
+ do {
+ if (pos == &sweep_line->active) {
+ full_add_edge (sweep_line, left, +1);
+ return;
+ }
+
+ right = link_to_edge (pos);
+ pos = pos->next;
+ sweep_line->is_vertical &= right->vertical;
+
+ if (++winding & 1) {
+ if (pos == &sweep_line->active ||
+ link_to_edge (pos)->x.quo != right->x.quo)
+ {
+ break;
+ }
+ }
+
+ if (! right->vertical)
+ full_inc_edge (right);
+ } while (TRUE);
+
+ full_add_edge (sweep_line, left, +1);
+ full_add_edge (sweep_line, right, -1);
+ } while (pos != &sweep_line->active);
+}
+
+static void
+render_rows (cairo_botor_scan_converter_t *self,
+ sweep_line_t *sweep_line,
+ int y, int height,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)];
+ cairo_half_open_span_t *spans = spans_stack;
+ struct cell *cell;
+ int prev_x, cover;
+ int num_spans;
+ cairo_status_t status;
+
+ if (unlikely (sweep_line->coverage.count == 0)) {
+ status = renderer->render_rows (renderer, y, height, NULL, 0);
+ if (unlikely (status))
+ longjmp (sweep_line->unwind, status);
+ return;
+ }
+
+ /* Allocate enough spans for the row. */
+
+ num_spans = 2*sweep_line->coverage.count+2;
+ if (unlikely (num_spans > ARRAY_LENGTH (spans_stack))) {
+ spans = _cairo_malloc_ab (num_spans, sizeof (cairo_half_open_span_t));
+ if (unlikely (spans == NULL)) {
+ longjmp (sweep_line->unwind,
+ _cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ /* Form the spans from the coverage and areas. */
+ num_spans = 0;
+ prev_x = self->xmin;
+ cover = 0;
+ cell = sweep_line->coverage.head.next;
+ do {
+ int x = cell->x;
+ int area;
+
+ if (x > prev_x) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].inverse = 0;
+ spans[num_spans].coverage = AREA_TO_ALPHA (cover);
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*STEP_X*2;
+ area = cover - cell->uncovered_area;
+
+ spans[num_spans].x = x;
+ spans[num_spans].coverage = AREA_TO_ALPHA (area);
+ ++num_spans;
+
+ prev_x = x + 1;
+ } while ((cell = cell->next) != &sweep_line->coverage.tail);
+
+ if (prev_x <= self->xmax) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].inverse = 0;
+ spans[num_spans].coverage = AREA_TO_ALPHA (cover);
+ ++num_spans;
+ }
+
+ if (cover && prev_x < self->xmax) {
+ spans[num_spans].x = self->xmax;
+ spans[num_spans].inverse = 1;
+ spans[num_spans].coverage = 0;
+ ++num_spans;
+ }
+
+ status = renderer->render_rows (renderer, y, height, spans, num_spans);
+
+ if (unlikely (spans != spans_stack))
+ free (spans);
+
+ coverage_reset (&sweep_line->coverage);
+
+ if (unlikely (status))
+ longjmp (sweep_line->unwind, status);
+}
+
+static void
+full_repeat (sweep_line_t *sweep)
+{
+ edge_t *edge;
+
+ cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) {
+ if (edge->current_sign)
+ full_add_edge (sweep, edge, edge->current_sign);
+ else if (! edge->vertical)
+ full_inc_edge (edge);
+ }
+}
+
+static void
+full_reset (sweep_line_t *sweep)
+{
+ edge_t *edge;
+
+ cairo_list_foreach_entry (edge, edge_t, &sweep->active, link)
+ edge->current_sign = 0;
+}
+
+static void
+full_step (cairo_botor_scan_converter_t *self,
+ sweep_line_t *sweep_line,
+ cairo_fixed_t row,
+ cairo_span_renderer_t *renderer)
+{
+ int top, bottom;
+
+ top = _cairo_fixed_integer_part (sweep_line->current_row);
+ bottom = _cairo_fixed_integer_part (row);
+ if (cairo_list_is_empty (&sweep_line->active)) {
+ cairo_status_t status;
+
+ status = renderer->render_rows (renderer, top, bottom - top, NULL, 0);
+ if (unlikely (status))
+ longjmp (sweep_line->unwind, status);
+
+ return;
+ }
+
+ if (self->fill_rule == CAIRO_FILL_RULE_WINDING)
+ full_nonzero (sweep_line);
+ else
+ full_evenodd (sweep_line);
+
+ if (sweep_line->is_vertical || bottom == top + 1) {
+ render_rows (self, sweep_line, top, bottom - top, renderer);
+ full_reset (sweep_line);
+ return;
+ }
+
+ render_rows (self, sweep_line, top++, 1, renderer);
+ do {
+ full_repeat (sweep_line);
+ render_rows (self, sweep_line, top, 1, renderer);
+ } while (++top != bottom);
+
+ full_reset (sweep_line);
+}
+
+cairo_always_inline static void
+sub_inc_edge (edge_t *edge,
+ cairo_fixed_t height)
+{
+ if (height == 1) {
+ edge->x.quo += edge->dxdy.quo;
+ edge->x.rem += edge->dxdy.rem;
+ if (edge->x.rem >= 0) {
+ ++edge->x.quo;
+ edge->x.rem -= edge->dy;
+ }
+ } else {
+ edge->x.quo += height * edge->dxdy.quo;
+ edge->x.rem += height * edge->dxdy.rem;
+ if (edge->x.rem >= 0) {
+ int carry = edge->x.rem / edge->dy + 1;
+ edge->x.quo += carry;
+ edge->x.rem -= carry * edge->dy;
+ }
+ }
+}
+
+static void
+sub_add_run (sweep_line_t *sweep_line, edge_t *edge, int y, int sign)
+{
+ struct run *run;
+
+ run = _cairo_freepool_alloc (&sweep_line->runs);
+ if (unlikely (run == NULL))
+ longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ run->y = y;
+ run->sign = sign;
+ run->next = edge->runs;
+ edge->runs = run;
+
+ edge->current_sign = sign;
+}
+
+inline static cairo_bool_t
+edges_coincident (edge_t *left, edge_t *right, cairo_fixed_t y)
+{
+ /* XXX is compare_x_for_y() worth executing during sub steps? */
+ return line_equal (&left->edge.line, &right->edge.line);
+ //edges_compare_x_for_y (&left->edge, &right->edge, y) >= 0;
+}
+
+static void
+sub_nonzero (sweep_line_t *sweep_line)
+{
+ cairo_fixed_t y = sweep_line->current_subrow;
+ cairo_fixed_t fy = _cairo_fixed_fractional_part (y);
+ cairo_list_t *pos;
+
+ pos = sweep_line->active.next;
+ do {
+ edge_t *left = link_to_edge (pos), *right;
+ int winding = left->edge.dir;
+
+ pos = left->link.next;
+ do {
+ if (unlikely (pos == &sweep_line->active)) {
+ if (left->current_sign != +1)
+ sub_add_run (sweep_line, left, fy, +1);
+ return;
+ }
+
+ right = link_to_edge (pos);
+ pos = pos->next;
+
+ winding += right->edge.dir;
+ if (0 == winding) {
+ if (pos == &sweep_line->active ||
+ ! edges_coincident (right, link_to_edge (pos), y))
+ {
+ break;
+ }
+ }
+
+ if (right->current_sign)
+ sub_add_run (sweep_line, right, fy, 0);
+ } while (TRUE);
+
+ if (left->current_sign != +1)
+ sub_add_run (sweep_line, left, fy, +1);
+ if (right->current_sign != -1)
+ sub_add_run (sweep_line, right, fy, -1);
+ } while (pos != &sweep_line->active);
+}
+
+static void
+sub_evenodd (sweep_line_t *sweep_line)
+{
+ cairo_fixed_t y = sweep_line->current_subrow;
+ cairo_fixed_t fy = _cairo_fixed_fractional_part (y);
+ cairo_list_t *pos;
+
+ pos = sweep_line->active.next;
+ do {
+ edge_t *left = link_to_edge (pos), *right;
+ int winding = 0;
+
+ pos = left->link.next;
+ do {
+ if (unlikely (pos == &sweep_line->active)) {
+ if (left->current_sign != +1)
+ sub_add_run (sweep_line, left, fy, +1);
+ return;
+ }
+
+ right = link_to_edge (pos);
+ pos = pos->next;
+
+ if (++winding & 1) {
+ if (pos == &sweep_line->active ||
+ ! edges_coincident (right, link_to_edge (pos), y))
+ {
+ break;
+ }
+ }
+
+ if (right->current_sign)
+ sub_add_run (sweep_line, right, fy, 0);
+ } while (TRUE);
+
+ if (left->current_sign != +1)
+ sub_add_run (sweep_line, left, fy, +1);
+ if (right->current_sign != -1)
+ sub_add_run (sweep_line, right, fy, -1);
+ } while (pos != &sweep_line->active);
+}
+
+cairo_always_inline static void
+sub_step (cairo_botor_scan_converter_t *self,
+ sweep_line_t *sweep_line)
+{
+ if (cairo_list_is_empty (&sweep_line->active))
+ return;
+
+ if (self->fill_rule == CAIRO_FILL_RULE_WINDING)
+ sub_nonzero (sweep_line);
+ else
+ sub_evenodd (sweep_line);
+}
+
+static void
+coverage_render_runs (sweep_line_t *sweep, edge_t *edge,
+ cairo_fixed_t y1, cairo_fixed_t y2)
+{
+ struct run tail;
+ struct run *run = &tail;
+
+ tail.next = NULL;
+ tail.y = y2;
+
+ /* Order the runs top->bottom */
+ while (edge->runs) {
+ struct run *r;
+
+ r = edge->runs;
+ edge->runs = r->next;
+ r->next = run;
+ run = r;
+ }
+
+ if (run->y > y1)
+ sub_inc_edge (edge, run->y - y1);
+
+ do {
+ cairo_fixed_t x1, x2;
+
+ y1 = run->y;
+ y2 = run->next->y;
+
+ x1 = edge->x.quo;
+ if (y2 - y1 == STEP_Y)
+ full_inc_edge (edge);
+ else
+ sub_inc_edge (edge, y2 - y1);
+ x2 = edge->x.quo;
+
+ if (run->sign) {
+ int ix1, ix2;
+
+ ix1 = _cairo_fixed_integer_part (x1);
+ ix2 = _cairo_fixed_integer_part (x2);
+
+ /* Edge is entirely within a column? */
+ if (likely (ix1 == ix2)) {
+ struct cell *cell;
+ int frac;
+
+ frac = _cairo_fixed_fractional_part (x1) +
+ _cairo_fixed_fractional_part (x2);
+ cell = coverage_find (sweep, ix1);
+ cell->covered_height += run->sign * (y2 - y1);
+ cell->uncovered_area += run->sign * (y2 - y1) * frac;
+ } else {
+ coverage_render_cells (sweep, x1, x2, y1, y2, run->sign);
+ }
+ }
+
+ run = run->next;
+ } while (run->next != NULL);
+}
+
+static void
+coverage_render_vertical_runs (sweep_line_t *sweep, edge_t *edge, cairo_fixed_t y2)
+{
+ struct cell *cell;
+ struct run *run;
+ int height = 0;
+
+ for (run = edge->runs; run != NULL; run = run->next) {
+ if (run->sign)
+ height += run->sign * (y2 - run->y);
+ y2 = run->y;
+ }
+
+ cell = coverage_find (sweep, _cairo_fixed_integer_part (edge->x.quo));
+ cell->covered_height += height;
+ cell->uncovered_area += 2 * _cairo_fixed_fractional_part (edge->x.quo) * height;
+}
+
+cairo_always_inline static void
+sub_emit (cairo_botor_scan_converter_t *self,
+ sweep_line_t *sweep,
+ cairo_span_renderer_t *renderer)
+{
+ edge_t *edge;
+
+ sub_step (self, sweep);
+
+ /* convert the runs into coverages */
+
+ cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) {
+ if (edge->runs == NULL) {
+ if (! edge->vertical) {
+ if (edge->flags & START) {
+ sub_inc_edge (edge,
+ STEP_Y - _cairo_fixed_fractional_part (edge->edge.top));
+ edge->flags &= ~START;
+ } else
+ full_inc_edge (edge);
+ }
+ } else {
+ if (edge->vertical) {
+ coverage_render_vertical_runs (sweep, edge, STEP_Y);
+ } else {
+ int y1 = 0;
+ if (edge->flags & START) {
+ y1 = _cairo_fixed_fractional_part (edge->edge.top);
+ edge->flags &= ~START;
+ }
+ coverage_render_runs (sweep, edge, y1, STEP_Y);
+ }
+ }
+ edge->current_sign = 0;
+ edge->runs = NULL;
+ }
+
+ cairo_list_foreach_entry (edge, edge_t, &sweep->stopped, link) {
+ int y2 = _cairo_fixed_fractional_part (edge->edge.bottom);
+ if (edge->vertical) {
+ coverage_render_vertical_runs (sweep, edge, y2);
+ } else {
+ int y1 = 0;
+ if (edge->flags & START)
+ y1 = _cairo_fixed_fractional_part (edge->edge.top);
+ coverage_render_runs (sweep, edge, y1, y2);
+ }
+ }
+ cairo_list_init (&sweep->stopped);
+
+ _cairo_freepool_reset (&sweep->runs);
+
+ render_rows (self, sweep,
+ _cairo_fixed_integer_part (sweep->current_row), 1,
+ renderer);
+}
+
+static void
+sweep_line_init (sweep_line_t *sweep_line,
+ event_t **start_events,
+ int num_events)
+{
+ cairo_list_init (&sweep_line->active);
+ cairo_list_init (&sweep_line->stopped);
+ sweep_line->insert_cursor = &sweep_line->active;
+
+ sweep_line->current_row = INT32_MIN;
+ sweep_line->current_subrow = INT32_MIN;
+
+ coverage_init (&sweep_line->coverage);
+ _cairo_freepool_init (&sweep_line->runs, sizeof (struct run));
+
+ start_event_sort (start_events, num_events);
+ start_events[num_events] = NULL;
+
+ sweep_line->queue.start_events = start_events;
+
+ _cairo_freepool_init (&sweep_line->queue.pool,
+ sizeof (queue_event_t));
+ pqueue_init (&sweep_line->queue.pq);
+ sweep_line->queue.pq.elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static void
+sweep_line_delete (sweep_line_t *sweep_line,
+ edge_t *edge)
+{
+ if (sweep_line->insert_cursor == &edge->link)
+ sweep_line->insert_cursor = edge->link.prev;
+
+ cairo_list_del (&edge->link);
+ if (edge->runs)
+ cairo_list_add_tail (&edge->link, &sweep_line->stopped);
+ edge->flags |= STOP;
+}
+
+static void
+sweep_line_swap (sweep_line_t *sweep_line,
+ edge_t *left,
+ edge_t *right)
+{
+ right->link.prev = left->link.prev;
+ left->link.next = right->link.next;
+ right->link.next = &left->link;
+ left->link.prev = &right->link;
+ left->link.next->prev = &left->link;
+ right->link.prev->next = &right->link;
+}
+
+static void
+sweep_line_fini (sweep_line_t *sweep_line)
+{
+ pqueue_fini (&sweep_line->queue.pq);
+ _cairo_freepool_fini (&sweep_line->queue.pool);
+ coverage_fini (&sweep_line->coverage);
+ _cairo_freepool_fini (&sweep_line->runs);
+}
+
+static cairo_status_t
+botor_generate (cairo_botor_scan_converter_t *self,
+ event_t **start_events,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_status_t status;
+ sweep_line_t sweep_line;
+ cairo_fixed_t ybot;
+ event_t *event;
+ cairo_list_t *left, *right;
+ edge_t *e1, *e2;
+ int bottom;
+
+ sweep_line_init (&sweep_line, start_events, self->num_edges);
+ if ((status = setjmp (sweep_line.unwind)))
+ goto unwind;
+
+ ybot = self->extents.p2.y;
+ sweep_line.current_subrow = self->extents.p1.y;
+ sweep_line.current_row = _cairo_fixed_floor (self->extents.p1.y);
+ event = *sweep_line.queue.start_events++;
+ do {
+ /* Can we process a full step in one go? */
+ if (event->y >= sweep_line.current_row + STEP_Y) {
+ bottom = _cairo_fixed_floor (event->y);
+ full_step (self, &sweep_line, bottom, renderer);
+ sweep_line.current_row = bottom;
+ sweep_line.current_subrow = bottom;
+ }
+
+ do {
+ if (event->y > sweep_line.current_subrow) {
+ sub_step (self, &sweep_line);
+ sweep_line.current_subrow = event->y;
+ }
+
+ do {
+ /* Update the active list using Bentley-Ottmann */
+ switch (event->type) {
+ case EVENT_TYPE_START:
+ e1 = ((start_event_t *) event)->edge;
+
+ sweep_line_insert (&sweep_line, e1);
+ event_insert_stop (&sweep_line, e1);
+
+ left = e1->link.prev;
+ right = e1->link.next;
+
+ if (left != &sweep_line.active) {
+ event_insert_if_intersect_below_current_y (&sweep_line,
+ link_to_edge (left), e1);
+ }
+
+ if (right != &sweep_line.active) {
+ event_insert_if_intersect_below_current_y (&sweep_line,
+ e1, link_to_edge (right));
+ }
+
+ break;
+
+ case EVENT_TYPE_STOP:
+ e1 = ((queue_event_t *) event)->e1;
+ event_delete (&sweep_line, event);
+
+ left = e1->link.prev;
+ right = e1->link.next;
+
+ sweep_line_delete (&sweep_line, e1);
+
+ if (left != &sweep_line.active &&
+ right != &sweep_line.active)
+ {
+ event_insert_if_intersect_below_current_y (&sweep_line,
+ link_to_edge (left),
+ link_to_edge (right));
+ }
+
+ break;
+
+ case EVENT_TYPE_INTERSECTION:
+ e1 = ((queue_event_t *) event)->e1;
+ e2 = ((queue_event_t *) event)->e2;
+
+ event_delete (&sweep_line, event);
+ if (e1->flags & STOP)
+ break;
+ if (e2->flags & STOP)
+ break;
+
+ /* skip this intersection if its edges are not adjacent */
+ if (&e2->link != e1->link.next)
+ break;
+
+ left = e1->link.prev;
+ right = e2->link.next;
+
+ sweep_line_swap (&sweep_line, e1, e2);
+
+ /* after the swap e2 is left of e1 */
+ if (left != &sweep_line.active) {
+ event_insert_if_intersect_below_current_y (&sweep_line,
+ link_to_edge (left), e2);
+ }
+
+ if (right != &sweep_line.active) {
+ event_insert_if_intersect_below_current_y (&sweep_line,
+ e1, link_to_edge (right));
+ }
+
+ break;
+ }
+
+ event = event_next (&sweep_line);
+ if (event == NULL)
+ goto end;
+ } while (event->y == sweep_line.current_subrow);
+ } while (event->y < sweep_line.current_row + STEP_Y);
+
+ bottom = sweep_line.current_row + STEP_Y;
+ sub_emit (self, &sweep_line, renderer);
+ sweep_line.current_subrow = bottom;
+ sweep_line.current_row = sweep_line.current_subrow;
+ } while (TRUE);
+
+ end:
+ /* flush any partial spans */
+ if (sweep_line.current_subrow != sweep_line.current_row) {
+ sub_emit (self, &sweep_line, renderer);
+ sweep_line.current_row += STEP_Y;
+ sweep_line.current_subrow = sweep_line.current_row;
+ }
+ /* clear the rest */
+ if (sweep_line.current_subrow < ybot) {
+ bottom = _cairo_fixed_integer_part (sweep_line.current_row);
+ status = renderer->render_rows (renderer,
+ bottom, _cairo_fixed_integer_ceil (ybot) - bottom,
+ NULL, 0);
+ }
+
+ unwind:
+ sweep_line_fini (&sweep_line);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_botor_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_botor_scan_converter_t *self = converter;
+ start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (start_event_t)];
+ start_event_t *events;
+ event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+ event_t **event_ptrs;
+ struct _cairo_botor_scan_converter_chunk *chunk;
+ cairo_status_t status;
+ int num_events;
+ int i, j;
+
+ num_events = self->num_edges;
+ if (unlikely (0 == num_events)) {
+ return renderer->render_rows (renderer,
+ _cairo_fixed_integer_floor (self->extents.p1.y),
+ _cairo_fixed_integer_ceil (self->extents.p2.y) -
+ _cairo_fixed_integer_floor (self->extents.p1.y),
+ NULL, 0);
+ }
+
+ events = stack_events;
+ event_ptrs = stack_event_ptrs;
+ if (unlikely (num_events >= ARRAY_LENGTH (stack_events))) {
+ events = _cairo_malloc_ab_plus_c (num_events,
+ sizeof (start_event_t) + sizeof (event_t *),
+ sizeof (event_t *));
+ if (unlikely (events == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ event_ptrs = (event_t **) (events + num_events);
+ }
+
+ j = 0;
+ for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) {
+ edge_t *edge;
+
+ edge = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ event_ptrs[j] = (event_t *) &events[j];
+
+ events[j].y = edge->edge.top;
+ events[j].type = EVENT_TYPE_START;
+ events[j].edge = edge;
+
+ edge++, j++;
+ }
+ }
+
+ status = botor_generate (self, event_ptrs, renderer);
+
+ if (events != stack_events)
+ free (events);
+
+ return status;
+}
+
+static edge_t *
+botor_allocate_edge (cairo_botor_scan_converter_t *self)
+{
+ struct _cairo_botor_scan_converter_chunk *chunk;
+
+ chunk = self->tail;
+ if (chunk->count == chunk->size) {
+ int size;
+
+ size = chunk->size * 2;
+ chunk->next = _cairo_malloc_ab_plus_c (size,
+ sizeof (edge_t),
+ sizeof (struct _cairo_botor_scan_converter_chunk));
+ if (unlikely (chunk->next == NULL))
+ return NULL;
+
+ chunk = chunk->next;
+ chunk->next = NULL;
+ chunk->count = 0;
+ chunk->size = size;
+ chunk->base = chunk + 1;
+ self->tail = chunk;
+ }
+
+ return (edge_t *) chunk->base + chunk->count++;
+}
+
+static cairo_status_t
+botor_add_edge (cairo_botor_scan_converter_t *self,
+ const cairo_edge_t *edge)
+{
+ edge_t *e;
+ cairo_fixed_t dx, dy;
+
+ e = botor_allocate_edge (self);
+ if (unlikely (e == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ cairo_list_init (&e->link);
+ e->edge = *edge;
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+
+ if (dx == 0) {
+ e->vertical = TRUE;
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ e->dxdy.quo = 0;
+ e->dxdy.rem = 0;
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_divrem (dx, dy);
+ if (edge->top == edge->line.p1.y) {
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ } else {
+ e->x = floored_muldivrem (edge->top - edge->line.p1.y,
+ dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+
+ if (_cairo_fixed_integer_part (edge->bottom) - _cairo_fixed_integer_part (edge->top) > 1) {
+ e->dxdy_full = floored_muldivrem (STEP_Y, dx, dy);
+ } else {
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ }
+ }
+
+ e->x.rem = -e->dy;
+ e->current_sign = 0;
+ e->runs = NULL;
+ e->flags = START;
+
+ self->num_edges++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_botor_scan_converter_destroy (void *converter)
+{
+ cairo_botor_scan_converter_t *self = converter;
+ struct _cairo_botor_scan_converter_chunk *chunk, *next;
+
+ for (chunk = self->chunks.next; chunk != NULL; chunk = next) {
+ next = chunk->next;
+ free (chunk);
+ }
+}
+
+void
+_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self,
+ const cairo_box_t *extents,
+ cairo_fill_rule_t fill_rule)
+{
+ self->base.destroy = _cairo_botor_scan_converter_destroy;
+ self->base.generate = _cairo_botor_scan_converter_generate;
+
+ self->extents = *extents;
+ self->fill_rule = fill_rule;
+
+ self->xmin = _cairo_fixed_integer_floor (extents->p1.x);
+ self->xmax = _cairo_fixed_integer_ceil (extents->p2.x);
+
+ self->chunks.base = self->buf;
+ self->chunks.next = NULL;
+ self->chunks.count = 0;
+ self->chunks.size = sizeof (self->buf) / sizeof (edge_t);
+ self->tail = &self->chunks;
+
+ self->num_edges = 0;
+}
diff --git a/src/cairo-box-inline.h b/src/cairo-box-inline.h
new file mode 100755
index 000000000..d6b994127
--- /dev/null
+++ b/src/cairo-box-inline.h
@@ -0,0 +1,121 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Andrea Canciani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ * Andrea Canciani <ranma42@gmail.com>
+ */
+
+#ifndef CAIRO_BOX_H
+#define CAIRO_BOX_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-fixed-private.h"
+
+static inline void
+_cairo_box_set (cairo_box_t *box,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ box->p1 = *p1;
+ box->p2 = *p2;
+}
+
+static inline void
+_cairo_box_from_integers (cairo_box_t *box, int x, int y, int w, int h)
+{
+ box->p1.x = _cairo_fixed_from_int (x);
+ box->p1.y = _cairo_fixed_from_int (y);
+ box->p2.x = _cairo_fixed_from_int (x + w);
+ box->p2.y = _cairo_fixed_from_int (y + h);
+}
+
+/* assumes box->p1 is top-left, p2 bottom-right */
+static inline void
+_cairo_box_add_point (cairo_box_t *box,
+ const cairo_point_t *point)
+{
+ if (point->x < box->p1.x)
+ box->p1.x = point->x;
+ else if (point->x > box->p2.x)
+ box->p2.x = point->x;
+
+ if (point->y < box->p1.y)
+ box->p1.y = point->y;
+ else if (point->y > box->p2.y)
+ box->p2.y = point->y;
+}
+
+static inline void
+_cairo_box_add_box (cairo_box_t *box,
+ const cairo_box_t *add)
+{
+ if (add->p1.x < box->p1.x)
+ box->p1.x = add->p1.x;
+ if (add->p2.x > box->p2.x)
+ box->p2.x = add->p2.x;
+
+ if (add->p1.y < box->p1.y)
+ box->p1.y = add->p1.y;
+ if (add->p2.y > box->p2.y)
+ box->p2.y = add->p2.y;
+}
+
+/* assumes box->p1 is top-left, p2 bottom-right */
+static inline cairo_bool_t
+_cairo_box_contains_point (const cairo_box_t *box,
+ const cairo_point_t *point)
+{
+ return box->p1.x <= point->x && point->x <= box->p2.x &&
+ box->p1.y <= point->y && point->y <= box->p2.y;
+}
+
+static inline cairo_bool_t
+_cairo_box_is_pixel_aligned (const cairo_box_t *box)
+{
+#if CAIRO_FIXED_FRAC_BITS <= 8 && 0
+ return ((box->p1.x & CAIRO_FIXED_FRAC_MASK) << 24 |
+ (box->p1.y & CAIRO_FIXED_FRAC_MASK) << 16 |
+ (box->p2.x & CAIRO_FIXED_FRAC_MASK) << 8 |
+ (box->p2.y & CAIRO_FIXED_FRAC_MASK) << 0) == 0;
+#else /* GCC on i7 prefers this variant (bizarrely according to the profiler) */
+ cairo_fixed_t f;
+
+ f = 0;
+ f |= box->p1.x & CAIRO_FIXED_FRAC_MASK;
+ f |= box->p1.y & CAIRO_FIXED_FRAC_MASK;
+ f |= box->p2.x & CAIRO_FIXED_FRAC_MASK;
+ f |= box->p2.y & CAIRO_FIXED_FRAC_MASK;
+
+ return f == 0;
+#endif
+}
+
+#endif /* CAIRO_BOX_H */
diff --git a/src/cairo-boxes-intersect.c b/src/cairo-boxes-intersect.c
new file mode 100755
index 000000000..96ae66334
--- /dev/null
+++ b/src/cairo-boxes-intersect.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ * Carl D. Worth <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-inline.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 a_or_b;
+ int dir;
+};
+
+struct _rectangle {
+ edge_t left, right;
+ int32_t top, bottom;
+};
+
+#define UNROLL3(x) x x x
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef struct _pqueue {
+ int size, max_size;
+
+ rectangle_t **elements;
+ rectangle_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _sweep_line {
+ rectangle_t **rectangles;
+ pqueue_t pq;
+ edge_t head, tail;
+ edge_t *insert_left, *insert_right;
+ int32_t current_y;
+ int32_t last_y;
+
+ jmp_buf unwind;
+} sweep_line_t;
+
+#define DEBUG_TRAPS 0
+
+#if DEBUG_TRAPS
+static void
+dump_traps (cairo_traps_t *traps, const char *filename)
+{
+ FILE *file;
+ int n;
+
+ if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+ return;
+
+ file = fopen (filename, "a");
+ if (file != NULL) {
+ for (n = 0; n < traps->num_traps; n++) {
+ fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+ traps->traps[n].top,
+ traps->traps[n].bottom,
+ traps->traps[n].left.p1.x,
+ traps->traps[n].left.p1.y,
+ traps->traps[n].left.p2.x,
+ traps->traps[n].left.p2.y,
+ traps->traps[n].right.p1.x,
+ traps->traps[n].right.p1.y,
+ traps->traps[n].right.p2.x,
+ traps->traps[n].right.p2.y);
+ }
+ fprintf (file, "\n");
+ fclose (file);
+ }
+}
+#else
+#define dump_traps(traps, filename)
+#endif
+
+static inline int
+rectangle_compare_start (const rectangle_t *a,
+ const rectangle_t *b)
+{
+ return a->top - b->top;
+}
+
+static inline int
+rectangle_compare_stop (const rectangle_t *a,
+ const rectangle_t *b)
+{
+ return a->bottom - b->bottom;
+}
+
+static inline void
+pqueue_init (pqueue_t *pq)
+{
+ pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
+ pq->size = 0;
+
+ pq->elements = pq->elements_embedded;
+ pq->elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static inline void
+pqueue_fini (pqueue_t *pq)
+{
+ if (pq->elements != pq->elements_embedded)
+ free (pq->elements);
+}
+
+static cairo_bool_t
+pqueue_grow (pqueue_t *pq)
+{
+ rectangle_t **new_elements;
+ pq->max_size *= 2;
+
+ if (pq->elements == pq->elements_embedded) {
+ new_elements = _cairo_malloc_ab (pq->max_size,
+ sizeof (rectangle_t *));
+ if (unlikely (new_elements == NULL))
+ return FALSE;
+
+ memcpy (new_elements, pq->elements_embedded,
+ sizeof (pq->elements_embedded));
+ } else {
+ new_elements = _cairo_realloc_ab (pq->elements,
+ pq->max_size,
+ sizeof (rectangle_t *));
+ if (unlikely (new_elements == NULL))
+ return FALSE;
+ }
+
+ pq->elements = new_elements;
+ return TRUE;
+}
+
+static inline void
+pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+ rectangle_t **elements;
+ int i, parent;
+
+ if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) {
+ if (unlikely (! pqueue_grow (&sweep->pq))) {
+ longjmp (sweep->unwind,
+ _cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ elements = sweep->pq.elements;
+ for (i = ++sweep->pq.size;
+ i != PQ_FIRST_ENTRY &&
+ rectangle_compare_stop (rectangle,
+ elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+ i = parent)
+ {
+ elements[i] = elements[parent];
+ }
+
+ elements[i] = rectangle;
+}
+
+static inline void
+pqueue_pop (pqueue_t *pq)
+{
+ rectangle_t **elements = pq->elements;
+ rectangle_t *tail;
+ int child, i;
+
+ tail = elements[pq->size--];
+ if (pq->size == 0) {
+ elements[PQ_FIRST_ENTRY] = NULL;
+ return;
+ }
+
+ for (i = PQ_FIRST_ENTRY;
+ (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+ i = child)
+ {
+ if (child != pq->size &&
+ rectangle_compare_stop (elements[child+1],
+ elements[child]) < 0)
+ {
+ child++;
+ }
+
+ if (rectangle_compare_stop (elements[child], tail) >= 0)
+ break;
+
+ elements[i] = elements[child];
+ }
+ elements[i] = tail;
+}
+
+static inline rectangle_t *
+rectangle_pop_start (sweep_line_t *sweep_line)
+{
+ return *sweep_line->rectangles++;
+}
+
+static inline rectangle_t *
+rectangle_peek_stop (sweep_line_t *sweep_line)
+{
+ return sweep_line->pq.elements[PQ_FIRST_ENTRY];
+}
+
+CAIRO_COMBSORT_DECLARE (_rectangle_sort,
+ rectangle_t *,
+ rectangle_compare_start)
+
+static void
+sweep_line_init (sweep_line_t *sweep_line,
+ rectangle_t **rectangles,
+ int num_rectangles)
+{
+ _rectangle_sort (rectangles, num_rectangles);
+ rectangles[num_rectangles] = NULL;
+ sweep_line->rectangles = rectangles;
+
+ sweep_line->head.x = INT32_MIN;
+ sweep_line->head.right = NULL;
+ sweep_line->head.dir = 0;
+ sweep_line->head.next = &sweep_line->tail;
+ sweep_line->tail.x = INT32_MAX;
+ sweep_line->tail.right = NULL;
+ sweep_line->tail.dir = 0;
+ sweep_line->tail.prev = &sweep_line->head;
+
+ sweep_line->insert_left = &sweep_line->tail;
+ sweep_line->insert_right = &sweep_line->tail;
+
+ sweep_line->current_y = INT32_MIN;
+ sweep_line->last_y = INT32_MIN;
+
+ pqueue_init (&sweep_line->pq);
+}
+
+static void
+sweep_line_fini (sweep_line_t *sweep_line)
+{
+ pqueue_fini (&sweep_line->pq);
+}
+
+static void
+end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot, cairo_boxes_t *out)
+{
+ if (likely (left->top < bot)) {
+ cairo_status_t status;
+ cairo_box_t box;
+
+ box.p1.x = left->x;
+ box.p1.y = left->top;
+ box.p2.x = left->right->x;
+ box.p2.y = bot;
+
+ status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box);
+ if (unlikely (status))
+ longjmp (sweep_line->unwind, status);
+ }
+
+ left->right = NULL;
+}
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline void
+start_or_continue_box (sweep_line_t *sweep_line,
+ edge_t *left,
+ edge_t *right,
+ int top,
+ cairo_boxes_t *out)
+{
+ if (left->right == right)
+ return;
+
+ if (left->right != NULL) {
+ if (right != NULL && left->right->x == right->x) {
+ /* continuation on right, so just swap edges */
+ left->right = right;
+ return;
+ }
+
+ end_box (sweep_line, left, top, out);
+ }
+
+ if (right != NULL && left->x != right->x) {
+ left->top = top;
+ left->right = right;
+ }
+}
+
+static inline int is_zero(const int *winding)
+{
+ return winding[0] == 0 || winding[1] == 0;
+}
+
+static inline void
+active_edges (sweep_line_t *sweep, cairo_boxes_t *out)
+{
+ int top = sweep->current_y;
+ int winding[2] = { 0 };
+ edge_t *pos;
+
+ if (sweep->last_y == sweep->current_y)
+ return;
+
+ pos = sweep->head.next;
+ if (pos == &sweep->tail)
+ return;
+
+ do {
+ edge_t *left, *right;
+
+ left = pos;
+ do {
+ winding[left->a_or_b] += left->dir;
+ if (!is_zero (winding))
+ break;
+ if (left->next == &sweep->tail)
+ goto out;
+
+ if (unlikely (left->right != NULL))
+ end_box (sweep, left, top, out);
+
+ left = left->next;
+ } while (1);
+
+ right = left->next;
+ do {
+ if (unlikely (right->right != NULL))
+ end_box (sweep, right, top, out);
+
+ winding[right->a_or_b] += right->dir;
+ if (is_zero (winding)) {
+ /* skip co-linear edges */
+ if (likely (right->x != right->next->x))
+ break;
+ }
+
+ right = right->next;
+ } while (TRUE);
+
+ start_or_continue_box (sweep, left, right, top, out);
+
+ pos = right->next;
+ } while (pos != &sweep->tail);
+
+out:
+ sweep->last_y = sweep->current_y;
+}
+
+static inline void
+sweep_line_delete_edge (sweep_line_t *sweep_line, edge_t *edge, cairo_boxes_t *out)
+{
+ if (edge->right != NULL) {
+ edge_t *next = edge->next;
+ if (next->x == edge->x) {
+ next->top = edge->top;
+ next->right = edge->right;
+ } else {
+ end_box (sweep_line, edge, sweep_line->current_y, out);
+ }
+ }
+
+ if (sweep_line->insert_left == edge)
+ sweep_line->insert_left = edge->next;
+ if (sweep_line->insert_right == edge)
+ sweep_line->insert_right = edge->next;
+
+ edge->prev->next = edge->next;
+ edge->next->prev = edge->prev;
+}
+
+static inline void
+sweep_line_delete (sweep_line_t *sweep,
+ rectangle_t *rectangle,
+ cairo_boxes_t *out)
+{
+ sweep_line_delete_edge (sweep, &rectangle->left, out);
+ sweep_line_delete_edge (sweep, &rectangle->right, out);
+
+ pqueue_pop (&sweep->pq);
+}
+
+static inline void
+insert_edge (edge_t *edge, edge_t *pos)
+{
+ if (pos->x != edge->x) {
+ if (pos->x > edge->x) {
+ do {
+ UNROLL3({
+ if (pos->prev->x <= edge->x)
+ break;
+ pos = pos->prev;
+ })
+ } while (TRUE);
+ } else {
+ do {
+ UNROLL3({
+ pos = pos->next;
+ if (pos->x >= edge->x)
+ break;
+ })
+ } while (TRUE);
+ }
+ }
+
+ pos->prev->next = edge;
+ edge->prev = pos->prev;
+ edge->next = pos;
+ pos->prev = edge;
+}
+
+static inline void
+sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+ edge_t *pos;
+
+ /* right edge */
+ pos = sweep->insert_right;
+ insert_edge (&rectangle->right, pos);
+ sweep->insert_right = &rectangle->right;
+
+ /* left edge */
+ pos = sweep->insert_left;
+ if (pos->x > sweep->insert_right->x)
+ pos = sweep->insert_right->prev;
+ insert_edge (&rectangle->left, pos);
+ sweep->insert_left = &rectangle->left;
+
+ pqueue_push (sweep, rectangle);
+}
+
+static cairo_status_t
+intersect (rectangle_t **rectangles, int num_rectangles, cairo_boxes_t *out)
+{
+ sweep_line_t sweep_line;
+ rectangle_t *rectangle;
+ cairo_status_t status;
+
+ sweep_line_init (&sweep_line, rectangles, num_rectangles);
+ if ((status = setjmp (sweep_line.unwind)))
+ goto unwind;
+
+ rectangle = rectangle_pop_start (&sweep_line);
+ do {
+ if (rectangle->top != sweep_line.current_y) {
+ rectangle_t *stop;
+
+ stop = rectangle_peek_stop (&sweep_line);
+ while (stop != NULL && stop->bottom < rectangle->top) {
+ if (stop->bottom != sweep_line.current_y) {
+ active_edges (&sweep_line, out);
+ sweep_line.current_y = stop->bottom;
+ }
+
+ sweep_line_delete (&sweep_line, stop, out);
+
+ stop = rectangle_peek_stop (&sweep_line);
+ }
+
+ active_edges (&sweep_line, out);
+ sweep_line.current_y = rectangle->top;
+ }
+
+ sweep_line_insert (&sweep_line, rectangle);
+ } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL);
+
+ while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) {
+ if (rectangle->bottom != sweep_line.current_y) {
+ active_edges (&sweep_line, out);
+ sweep_line.current_y = rectangle->bottom;
+ }
+
+ sweep_line_delete (&sweep_line, rectangle, out);
+ }
+
+unwind:
+ sweep_line_fini (&sweep_line);
+ return status;
+}
+
+static cairo_status_t
+_cairo_boxes_intersect_with_box (const cairo_boxes_t *boxes,
+ const cairo_box_t *box,
+ cairo_boxes_t *out)
+{
+ cairo_status_t status;
+ int i, j;
+
+ if (out == boxes) { /* inplace update */
+ struct _cairo_boxes_chunk *chunk;
+
+ out->num_boxes = 0;
+ for (chunk = &out->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = j = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+
+ b->p1.x = MAX (b->p1.x, box->p1.x);
+ b->p1.y = MAX (b->p1.y, box->p1.y);
+ b->p2.x = MIN (b->p2.x, box->p2.x);
+ b->p2.y = MIN (b->p2.y, box->p2.y);
+ if (b->p1.x < b->p2.x && b->p1.y < b->p2.y) {
+ if (i != j)
+ chunk->base[j] = *b;
+ j++;
+ }
+ }
+ /* XXX unlink empty chains? */
+ chunk->count = j;
+ out->num_boxes += j;
+ }
+ } else {
+ const struct _cairo_boxes_chunk *chunk;
+
+ _cairo_boxes_clear (out);
+ _cairo_boxes_limit (out, box, 1);
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (out,
+ CAIRO_ANTIALIAS_DEFAULT,
+ &chunk->base[i]);
+ if (unlikely (status))
+ return status;
+ }
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_boxes_intersect (const cairo_boxes_t *a,
+ const cairo_boxes_t *b,
+ cairo_boxes_t *out)
+{
+ rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
+ rectangle_t *rectangles;
+ rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
+ rectangle_t **rectangles_ptrs;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ int i, j, count;
+
+ if (unlikely (a->num_boxes == 0 || b->num_boxes == 0)) {
+ _cairo_boxes_clear (out);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (a->num_boxes == 1) {
+ cairo_box_t box = a->chunks.base[0];
+ return _cairo_boxes_intersect_with_box (b, &box, out);
+ }
+ if (b->num_boxes == 1) {
+ cairo_box_t box = b->chunks.base[0];
+ return _cairo_boxes_intersect_with_box (a, &box, out);
+ }
+
+ rectangles = stack_rectangles;
+ rectangles_ptrs = stack_rectangles_ptrs;
+ count = a->num_boxes + b->num_boxes;
+ if (count > ARRAY_LENGTH (stack_rectangles)) {
+ rectangles = _cairo_malloc_ab_plus_c (count,
+ sizeof (rectangle_t) +
+ sizeof (rectangle_t *),
+ sizeof (rectangle_t *));
+ if (unlikely (rectangles == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ rectangles_ptrs = (rectangle_t **) (rectangles + count);
+ }
+
+ j = 0;
+ for (chunk = &a->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ if (box[i].p1.x < box[i].p2.x) {
+ rectangles[j].left.x = box[i].p1.x;
+ rectangles[j].left.dir = 1;
+
+ rectangles[j].right.x = box[i].p2.x;
+ rectangles[j].right.dir = -1;
+ } else {
+ rectangles[j].right.x = box[i].p1.x;
+ rectangles[j].right.dir = 1;
+
+ rectangles[j].left.x = box[i].p2.x;
+ rectangles[j].left.dir = -1;
+ }
+
+ rectangles[j].left.a_or_b = 0;
+ rectangles[j].left.right = NULL;
+ rectangles[j].right.a_or_b = 0;
+ rectangles[j].right.right = NULL;
+
+ rectangles[j].top = box[i].p1.y;
+ rectangles[j].bottom = box[i].p2.y;
+
+ rectangles_ptrs[j] = &rectangles[j];
+ j++;
+ }
+ }
+ for (chunk = &b->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ if (box[i].p1.x < box[i].p2.x) {
+ rectangles[j].left.x = box[i].p1.x;
+ rectangles[j].left.dir = 1;
+
+ rectangles[j].right.x = box[i].p2.x;
+ rectangles[j].right.dir = -1;
+ } else {
+ rectangles[j].right.x = box[i].p1.x;
+ rectangles[j].right.dir = 1;
+
+ rectangles[j].left.x = box[i].p2.x;
+ rectangles[j].left.dir = -1;
+ }
+
+ rectangles[j].left.a_or_b = 1;
+ rectangles[j].left.right = NULL;
+ rectangles[j].right.a_or_b = 1;
+ rectangles[j].right.right = NULL;
+
+ rectangles[j].top = box[i].p1.y;
+ rectangles[j].bottom = box[i].p2.y;
+
+ rectangles_ptrs[j] = &rectangles[j];
+ j++;
+ }
+ }
+ assert (j == count);
+
+ _cairo_boxes_clear (out);
+ status = intersect (rectangles_ptrs, j, out);
+ if (rectangles != stack_rectangles)
+ free (rectangles);
+
+ return status;
+}
diff --git a/src/cairo-boxes-private.h b/src/cairo-boxes-private.h
new file mode 100755
index 000000000..d1f9dfcd1
--- /dev/null
+++ b/src/cairo-boxes-private.h
@@ -0,0 +1,123 @@
+/* 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"
+
+#include <stdio.h>
+#include <stdlib.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;
+
+ struct _cairo_boxes_chunk {
+ struct _cairo_boxes_chunk *next;
+ cairo_box_t *base;
+ int count;
+ int size;
+ } chunks, *tail;
+ cairo_box_t boxes_embedded[32];
+};
+
+cairo_private void
+_cairo_boxes_init (cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
+ cairo_clip_t *clip);
+
+cairo_private void
+_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
+ cairo_box_t *array,
+ int num_boxes);
+
+cairo_private void
+_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
+ int x, int y, int w, int h);
+
+cairo_private void
+_cairo_boxes_limit (cairo_boxes_t *boxes,
+ const cairo_box_t *limits,
+ int num_limits);
+
+cairo_private cairo_status_t
+_cairo_boxes_add (cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ const cairo_box_t *box);
+
+cairo_private void
+_cairo_boxes_extents (const cairo_boxes_t *boxes,
+ cairo_box_t *box);
+
+cairo_private cairo_box_t *
+_cairo_boxes_to_array (const cairo_boxes_t *boxes,
+ int *num_boxes,
+ cairo_bool_t force_allocation);
+
+cairo_private cairo_status_t
+_cairo_boxes_intersect (const cairo_boxes_t *a,
+ const cairo_boxes_t *b,
+ cairo_boxes_t *out);
+
+cairo_private void
+_cairo_boxes_clear (cairo_boxes_t *boxes);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_boxes_for_each_box (cairo_boxes_t *boxes,
+ cairo_bool_t (*func) (cairo_box_t *box, void *data),
+ void *data);
+
+cairo_private cairo_status_t
+_cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_boxes_fini (cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_debug_print_boxes (FILE *stream,
+ const cairo_boxes_t *boxes);
+
+#endif /* CAIRO_BOXES_H */
diff --git a/src/cairo-boxes.c b/src/cairo-boxes.c
new file mode 100755
index 000000000..63b68ddfb
--- /dev/null
+++ b/src/cairo-boxes.c
@@ -0,0 +1,460 @@
+/* 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-box-inline.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+
+void
+_cairo_boxes_init (cairo_boxes_t *boxes)
+{
+ boxes->status = CAIRO_STATUS_SUCCESS;
+ boxes->num_limits = 0;
+ boxes->num_boxes = 0;
+
+ boxes->tail = &boxes->chunks;
+ boxes->chunks.next = NULL;
+ boxes->chunks.base = boxes->boxes_embedded;
+ boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
+ boxes->chunks.count = 0;
+
+ boxes->is_pixel_aligned = TRUE;
+}
+
+void
+_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
+ int x, int y, int w, int h)
+{
+ _cairo_boxes_init (boxes);
+
+ _cairo_box_from_integers (&boxes->chunks.base[0], x, y, w, h);
+ boxes->num_boxes = 1;
+}
+
+void
+_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
+ cairo_clip_t *clip)
+{
+ _cairo_boxes_init (boxes);
+ if (clip)
+ _cairo_boxes_limit (boxes, clip->boxes, clip->num_boxes);
+}
+
+void
+_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
+ cairo_box_t *array,
+ int num_boxes)
+{
+ int n;
+
+ boxes->status = CAIRO_STATUS_SUCCESS;
+ boxes->num_limits = 0;
+ boxes->num_boxes = num_boxes;
+
+ boxes->tail = &boxes->chunks;
+ boxes->chunks.next = NULL;
+ boxes->chunks.base = array;
+ boxes->chunks.size = num_boxes;
+ boxes->chunks.count = num_boxes;
+
+ for (n = 0; n < num_boxes; n++) {
+ if (! _cairo_fixed_is_integer (array[n].p1.x) ||
+ ! _cairo_fixed_is_integer (array[n].p1.y) ||
+ ! _cairo_fixed_is_integer (array[n].p2.x) ||
+ ! _cairo_fixed_is_integer (array[n].p2.y))
+ {
+ break;
+ }
+ }
+
+ boxes->is_pixel_aligned = n == num_boxes;
+}
+
+void
+_cairo_boxes_limit (cairo_boxes_t *boxes,
+ const cairo_box_t *limits,
+ int num_limits)
+{
+ int n;
+
+ boxes->limits = limits;
+ boxes->num_limits = num_limits;
+
+ if (boxes->num_limits) {
+ boxes->limit = limits[0];
+ for (n = 1; n < num_limits; n++) {
+ if (limits[n].p1.x < boxes->limit.p1.x)
+ boxes->limit.p1.x = limits[n].p1.x;
+
+ if (limits[n].p1.y < boxes->limit.p1.y)
+ boxes->limit.p1.y = limits[n].p1.y;
+
+ if (limits[n].p2.x > boxes->limit.p2.x)
+ boxes->limit.p2.x = limits[n].p2.x;
+
+ if (limits[n].p2.y > boxes->limit.p2.y)
+ boxes->limit.p2.y = limits[n].p2.y;
+ }
+ }
+}
+
+static void
+_cairo_boxes_add_internal (cairo_boxes_t *boxes,
+ const cairo_box_t *box)
+{
+ struct _cairo_boxes_chunk *chunk;
+
+ if (unlikely (boxes->status))
+ return;
+
+ chunk = boxes->tail;
+ if (unlikely (chunk->count == chunk->size)) {
+ int size;
+
+ size = chunk->size * 2;
+ chunk->next = _cairo_malloc_ab_plus_c (size,
+ sizeof (cairo_box_t),
+ sizeof (struct _cairo_boxes_chunk));
+
+ if (unlikely (chunk->next == NULL)) {
+ boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return;
+ }
+
+ chunk = chunk->next;
+ boxes->tail = chunk;
+
+ chunk->next = NULL;
+ chunk->count = 0;
+ chunk->size = size;
+ chunk->base = (cairo_box_t *) (chunk + 1);
+ }
+
+ chunk->base[chunk->count++] = *box;
+ boxes->num_boxes++;
+
+ if (boxes->is_pixel_aligned)
+ boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box);
+}
+
+cairo_status_t
+_cairo_boxes_add (cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ const cairo_box_t *box)
+{
+ cairo_box_t b;
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ b.p1.x = _cairo_fixed_round_down (box->p1.x);
+ b.p1.y = _cairo_fixed_round_down (box->p1.y);
+ b.p2.x = _cairo_fixed_round_down (box->p2.x);
+ b.p2.y = _cairo_fixed_round_down (box->p2.y);
+ box = &b;
+ }
+
+ if (box->p1.y == box->p2.y)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (box->p1.x == box->p2.x)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (boxes->num_limits) {
+ cairo_point_t p1, p2;
+ cairo_bool_t reversed = FALSE;
+ int n;
+
+ /* support counter-clockwise winding for rectangular tessellation */
+ if (box->p1.x < box->p2.x) {
+ p1.x = box->p1.x;
+ p2.x = box->p2.x;
+ } else {
+ p2.x = box->p1.x;
+ p1.x = box->p2.x;
+ reversed = ! reversed;
+ }
+
+ if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (box->p1.y < box->p2.y) {
+ p1.y = box->p1.y;
+ p2.y = box->p2.y;
+ } else {
+ p2.y = box->p1.y;
+ p1.y = box->p2.y;
+ reversed = ! reversed;
+ }
+
+ if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y)
+ return CAIRO_STATUS_SUCCESS;
+
+ for (n = 0; n < boxes->num_limits; n++) {
+ const cairo_box_t *limits = &boxes->limits[n];
+ cairo_box_t _box;
+ cairo_point_t _p1, _p2;
+
+ if (p1.x >= limits->p2.x || p2.x <= limits->p1.x)
+ continue;
+ if (p1.y >= limits->p2.y || p2.y <= limits->p1.y)
+ continue;
+
+ /* Otherwise, clip the box to the limits. */
+ _p1 = p1;
+ if (_p1.x < limits->p1.x)
+ _p1.x = limits->p1.x;
+ if (_p1.y < limits->p1.y)
+ _p1.y = limits->p1.y;
+
+ _p2 = p2;
+ if (_p2.x > limits->p2.x)
+ _p2.x = limits->p2.x;
+ if (_p2.y > limits->p2.y)
+ _p2.y = limits->p2.y;
+
+ if (_p2.y <= _p1.y || _p2.x <= _p1.x)
+ continue;
+
+ _box.p1.y = _p1.y;
+ _box.p2.y = _p2.y;
+ if (reversed) {
+ _box.p1.x = _p2.x;
+ _box.p2.x = _p1.x;
+ } else {
+ _box.p1.x = _p1.x;
+ _box.p2.x = _p2.x;
+ }
+
+ _cairo_boxes_add_internal (boxes, &_box);
+ }
+ } else {
+ _cairo_boxes_add_internal (boxes, box);
+ }
+
+ return boxes->status;
+}
+
+void
+_cairo_boxes_extents (const cairo_boxes_t *boxes,
+ cairo_box_t *box)
+{
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_box_t b;
+ int i;
+
+ if (boxes->num_boxes == 0) {
+ box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0;
+ return;
+ }
+
+ b = boxes->chunks.base[0];
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ if (chunk->base[i].p1.x < b.p1.x)
+ b.p1.x = chunk->base[i].p1.x;
+
+ if (chunk->base[i].p1.y < b.p1.y)
+ b.p1.y = chunk->base[i].p1.y;
+
+ if (chunk->base[i].p2.x > b.p2.x)
+ b.p2.x = chunk->base[i].p2.x;
+
+ if (chunk->base[i].p2.y > b.p2.y)
+ b.p2.y = chunk->base[i].p2.y;
+ }
+ }
+ *box = b;
+}
+
+void
+_cairo_boxes_clear (cairo_boxes_t *boxes)
+{
+ struct _cairo_boxes_chunk *chunk, *next;
+
+ for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
+ next = chunk->next;
+ free (chunk);
+ }
+
+ boxes->tail = &boxes->chunks;
+ boxes->chunks.next = 0;
+ boxes->chunks.count = 0;
+ boxes->chunks.base = boxes->boxes_embedded;
+ boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
+ boxes->num_boxes = 0;
+
+ boxes->is_pixel_aligned = TRUE;
+}
+
+cairo_box_t *
+_cairo_boxes_to_array (const cairo_boxes_t *boxes,
+ int *num_boxes,
+ cairo_bool_t force_allocation)
+{
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_box_t *box;
+ int i, j;
+
+ *num_boxes = boxes->num_boxes;
+ if (boxes->chunks.next == NULL && ! force_allocation)
+ return boxes->chunks.base;
+
+ box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t));
+ if (box == NULL) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ j = 0;
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++)
+ box[j++] = chunk->base[i];
+ }
+
+ return box;
+}
+
+void
+_cairo_boxes_fini (cairo_boxes_t *boxes)
+{
+ struct _cairo_boxes_chunk *chunk, *next;
+
+ for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
+ next = chunk->next;
+ free (chunk);
+ }
+}
+
+cairo_bool_t
+_cairo_boxes_for_each_box (cairo_boxes_t *boxes,
+ cairo_bool_t (*func) (cairo_box_t *box, void *data),
+ void *data)
+{
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++)
+ if (! func (&chunk->base[i], data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+struct cairo_box_renderer {
+ cairo_span_renderer_t base;
+ cairo_boxes_t *boxes;
+};
+
+static cairo_status_t
+span_to_boxes (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ struct cairo_box_renderer *r = abstract_renderer;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_box_t box;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ box.p1.y = _cairo_fixed_from_int (y);
+ box.p2.y = _cairo_fixed_from_int (y + h);
+ do {
+ if (spans[0].coverage) {
+ box.p1.x = _cairo_fixed_from_int(spans[0].x);
+ box.p2.x = _cairo_fixed_from_int(spans[1].x);
+ status = _cairo_boxes_add (r->boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
+ }
+ spans++;
+ } while (--num_spans > 1 && status == CAIRO_STATUS_SUCCESS);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_boxes_t *boxes)
+{
+ struct cairo_box_renderer renderer;
+ cairo_scan_converter_t *converter;
+ cairo_int_status_t status;
+ cairo_rectangle_int_t r;
+
+ TRACE ((stderr, "%s: fill_rule=%d\n", __FUNCTION__, fill_rule));
+
+ _cairo_box_round_to_rectangle (&polygon->extents, &r);
+ converter = _cairo_mono_scan_converter_create (r.x, r.y,
+ r.x + r.width,
+ r.y + r.height,
+ fill_rule);
+ status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
+ if (unlikely (status))
+ goto cleanup_converter;
+
+ renderer.boxes = boxes;
+ renderer.base.render_rows = span_to_boxes;
+
+ status = converter->generate (converter, &renderer.base);
+cleanup_converter:
+ converter->destroy (converter);
+ return status;
+}
+
+void
+_cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes)
+{
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_box_t extents;
+ int i;
+
+ _cairo_boxes_extents (boxes, &extents);
+ fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n",
+ boxes->num_boxes,
+ _cairo_fixed_to_double (extents.p1.x),
+ _cairo_fixed_to_double (extents.p1.y),
+ _cairo_fixed_to_double (extents.p2.x),
+ _cairo_fixed_to_double (extents.p2.y));
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ fprintf (stderr, " box[%d]: (%f, %f), (%f, %f)\n", i,
+ _cairo_fixed_to_double (chunk->base[i].p1.x),
+ _cairo_fixed_to_double (chunk->base[i].p1.y),
+ _cairo_fixed_to_double (chunk->base[i].p2.x),
+ _cairo_fixed_to_double (chunk->base[i].p2.y));
+ }
+ }
+}
diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h
new file mode 100755
index 000000000..76b556194
--- /dev/null
+++ b/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 {
+ unsigned long hash;
+ unsigned long size;
+} cairo_cache_entry_t;
+
+typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry);
+
+struct _cairo_cache {
+ cairo_hash_table_t *hash_table;
+
+ cairo_cache_predicate_func_t predicate;
+ cairo_destroy_func_t entry_destroy;
+
+ unsigned long max_size;
+ unsigned long size;
+
+ int freeze_count;
+};
+
+typedef cairo_bool_t
+(*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b);
+
+typedef void
+(*cairo_cache_callback_func_t) (void *entry,
+ void *closure);
+
+cairo_private cairo_status_t
+_cairo_cache_init (cairo_cache_t *cache,
+ cairo_cache_keys_equal_func_t keys_equal,
+ cairo_cache_predicate_func_t predicate,
+ cairo_destroy_func_t entry_destroy,
+ unsigned long max_size);
+
+cairo_private void
+_cairo_cache_fini (cairo_cache_t *cache);
+
+cairo_private void
+_cairo_cache_freeze (cairo_cache_t *cache);
+
+cairo_private void
+_cairo_cache_thaw (cairo_cache_t *cache);
+
+cairo_private void *
+_cairo_cache_lookup (cairo_cache_t *cache,
+ cairo_cache_entry_t *key);
+
+cairo_private cairo_status_t
+_cairo_cache_insert (cairo_cache_t *cache,
+ cairo_cache_entry_t *entry);
+
+cairo_private void
+_cairo_cache_remove (cairo_cache_t *cache,
+ cairo_cache_entry_t *entry);
+
+cairo_private void
+_cairo_cache_foreach (cairo_cache_t *cache,
+ cairo_cache_callback_func_t cache_callback,
+ void *closure);
+
+#endif
diff --git a/src/cairo-cache.c b/src/cairo-cache.c
new file mode 100755
index 000000000..5c4e4caa3
--- /dev/null
+++ b/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/src/cairo-cff-subset.c b/src/cairo-cff-subset.c
new file mode 100755
index 000000000..6e60568e6
--- /dev/null
+++ b/src/cairo-cff-subset.c
@@ -0,0 +1,3482 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ * Adrian Johnson <ajohnson@redneon.com>
+ * Eugeniy Meshcheryakov <eugen@debian.org>
+ */
+
+/*
+ * Useful links:
+ * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
+ * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
+ */
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-truetype-subset-private.h"
+#include <string.h>
+#include <locale.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 DEFAULTWIDTH_OP 0x0014
+#define ENCODING_OP 0x0010
+#define FAMILYNAME_OP 0x0003
+#define FDARRAY_OP 0x0c24
+#define FDSELECT_OP 0x0c25
+#define FONTBBOX_OP 0x0005
+#define FONTMATRIX_OP 0x0c07
+#define FONTNAME_OP 0x0c26
+#define FULLNAME_OP 0x0002
+#define LOCAL_SUB_OP 0x0013
+#define NOMINALWIDTH_OP 0x0015
+#define NOTICE_OP 0x0001
+#define POSTSCRIPT_OP 0x0c15
+#define PRIVATE_OP 0x0012
+#define ROS_OP 0x0c1e
+#define UNIQUEID_OP 0x000d
+#define VERSION_OP 0x0000
+#define WEIGHT_OP 0x0004
+#define XUID_OP 0x000e
+
+#define NUM_STD_STRINGS 391
+
+/* Type 2 Charstring operators */
+#define TYPE2_hstem 0x0001
+#define TYPE2_vstem 0x0003
+#define TYPE2_callsubr 0x000a
+
+#define TYPE2_return 0x000b
+#define TYPE2_endchar 0x000e
+
+#define TYPE2_hstemhm 0x0012
+#define TYPE2_hintmask 0x0013
+#define TYPE2_cntrmask 0x0014
+#define TYPE2_vstemhm 0x0017
+#define TYPE2_callgsubr 0x001d
+
+#define TYPE2_rmoveto 0x0015
+#define TYPE2_hmoveto 0x0016
+#define TYPE2_vmoveto 0x0004
+
+
+#define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */
+
+
+typedef struct _cff_header {
+ uint8_t major;
+ uint8_t minor;
+ uint8_t header_size;
+ uint8_t offset_size;
+} cff_header_t;
+
+typedef struct _cff_index_element {
+ cairo_bool_t is_copy;
+ unsigned char *data;
+ int length;
+} cff_index_element_t;
+
+typedef struct _cff_dict_operator {
+ cairo_hash_entry_t base;
+
+ unsigned short operator;
+ unsigned char *operand;
+ int operand_length;
+ int operand_offset;
+} cff_dict_operator_t;
+
+typedef struct _cairo_cff_font {
+
+ cairo_scaled_font_subset_t *scaled_font_subset;
+ const cairo_scaled_font_backend_t *backend;
+
+ /* Font Data */
+ unsigned char *data;
+ unsigned long data_length;
+ unsigned char *current_ptr;
+ unsigned char *data_end;
+ cff_header_t *header;
+ char *font_name;
+ char *ps_name;
+ cairo_hash_table_t *top_dict;
+ cairo_hash_table_t *private_dict;
+ cairo_array_t strings_index;
+ cairo_array_t charstrings_index;
+ cairo_array_t global_sub_index;
+ cairo_array_t local_sub_index;
+ unsigned char *charset;
+ int num_glyphs;
+ cairo_bool_t is_cid;
+ cairo_bool_t is_opentype;
+ int units_per_em;
+ int global_sub_bias;
+ int local_sub_bias;
+ double default_width;
+ double nominal_width;
+
+ /* CID Font Data */
+ int *fdselect;
+ unsigned int num_fontdicts;
+ cairo_hash_table_t **fd_dict;
+ cairo_hash_table_t **fd_private_dict;
+ cairo_array_t *fd_local_sub_index;
+ int *fd_local_sub_bias;
+ double *fd_default_width;
+ double *fd_nominal_width;
+
+ /* Subsetted Font Data */
+ char *subset_font_name;
+ cairo_array_t charstrings_subset_index;
+ cairo_array_t strings_subset_index;
+ int euro_sid;
+ int *fdselect_subset;
+ unsigned int num_subset_fontdicts;
+ int *fd_subset_map;
+ int *private_dict_offset;
+ cairo_bool_t subset_subroutines;
+ cairo_bool_t *global_subs_used;
+ cairo_bool_t *local_subs_used;
+ cairo_bool_t **fd_local_subs_used;
+ cairo_array_t output;
+
+ /* Subset Metrics */
+ int *widths;
+ int x_min, y_min, x_max, y_max;
+ int ascent, descent;
+
+ /* Type 2 charstring data */
+ int type2_stack_size;
+ int type2_stack_top_value;
+ cairo_bool_t type2_stack_top_is_int;
+ int type2_num_hints;
+ int type2_hintmask_bytes;
+ int type2_nesting_level;
+ cairo_bool_t type2_seen_first_int;
+ cairo_bool_t type2_find_width;
+ cairo_bool_t type2_found_width;
+ int type2_width;
+ cairo_bool_t type2_has_path;
+
+} cairo_cff_font_t;
+
+/* Encoded integer using maximum sized encoding. This is required for
+ * operands that are later modified after encoding. */
+static unsigned char *
+encode_integer_max (unsigned char *p, int i)
+{
+ *p++ = 29;
+ *p++ = i >> 24;
+ *p++ = (i >> 16) & 0xff;
+ *p++ = (i >> 8) & 0xff;
+ *p++ = i & 0xff;
+ return p;
+}
+
+static unsigned char *
+encode_integer (unsigned char *p, int i)
+{
+ if (i >= -107 && i <= 107) {
+ *p++ = i + 139;
+ } else if (i >= 108 && i <= 1131) {
+ i -= 108;
+ *p++ = (i >> 8)+ 247;
+ *p++ = i & 0xff;
+ } else if (i >= -1131 && i <= -108) {
+ i = -i - 108;
+ *p++ = (i >> 8)+ 251;
+ *p++ = i & 0xff;
+ } else if (i >= -32768 && i <= 32767) {
+ *p++ = 28;
+ *p++ = (i >> 8) & 0xff;
+ *p++ = i & 0xff;
+ } else {
+ p = encode_integer_max (p, i);
+ }
+ return p;
+}
+
+static unsigned char *
+decode_integer (unsigned char *p, int *integer)
+{
+ if (*p == 28) {
+ *integer = (int)(p[1]<<8 | p[2]);
+ p += 3;
+ } else if (*p == 29) {
+ *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]);
+ p += 5;
+ } else if (*p >= 32 && *p <= 246) {
+ *integer = *p++ - 139;
+ } else if (*p <= 250) {
+ *integer = (p[0] - 247) * 256 + p[1] + 108;
+ p += 2;
+ } else if (*p <= 254) {
+ *integer = -(p[0] - 251) * 256 - p[1] - 108;
+ p += 2;
+ } else {
+ *integer = 0;
+ p += 1;
+ }
+ return p;
+}
+
+static char *
+decode_nibble (int n, char *buf)
+{
+ switch (n)
+ {
+ case 0xa:
+ *buf++ = '.';
+ break;
+ case 0xb:
+ *buf++ = 'E';
+ break;
+ case 0xc:
+ *buf++ = 'E';
+ *buf++ = '-';
+ break;
+ case 0xd:
+ *buf++ = '-';
+ break;
+ case 0xe:
+ *buf++ = '-';
+ break;
+ case 0xf:
+ break;
+ default:
+ *buf++ = '0' + n;
+ break;
+ }
+
+ return buf;
+}
+
+static unsigned char *
+decode_real (unsigned char *p, double *real)
+{
+ struct lconv *locale_data;
+ const char *decimal_point;
+ int decimal_point_len;
+ int n;
+ char buffer[100];
+ char buffer2[200];
+ char *q;
+ char *buf = buffer;
+ char *buf_end = buffer + sizeof (buffer);
+
+ locale_data = localeconv ();
+ decimal_point = locale_data->decimal_point;
+ decimal_point_len = strlen (decimal_point);
+
+ assert (decimal_point_len != 0);
+ assert (sizeof(buffer) + decimal_point_len < sizeof(buffer2));
+
+ p++;
+ while (buf + 2 < buf_end) {
+ n = *p >> 4;
+ buf = decode_nibble (n, buf);
+ n = *p & 0x0f;
+ buf = decode_nibble (n, buf);
+ if ((*p & 0x0f) == 0x0f) {
+ p++;
+ break;
+ }
+ p++;
+ };
+ *buf = 0;
+
+ buf = buffer;
+ if (strchr (buffer, '.')) {
+ q = strchr (buffer, '.');
+ strncpy (buffer2, buffer, q - buffer);
+ buf = buffer2 + (q - buffer);
+ strncpy (buf, decimal_point, decimal_point_len);
+ buf += decimal_point_len;
+ strcpy (buf, q + 1);
+ buf = buffer2;
+ }
+
+ if (sscanf(buf, "%lf", real) != 1)
+ *real = 0.0;
+
+ return p;
+}
+
+static unsigned char *
+decode_number (unsigned char *p, double *number)
+{
+ if (*p == 30) {
+ p = decode_real (p, number);
+ } else {
+ int i;
+ p = decode_integer (p, &i);
+ *number = i;
+ }
+ return p;
+}
+
+static unsigned char *
+decode_operator (unsigned char *p, unsigned short *operator)
+{
+ unsigned short op = 0;
+
+ op = *p++;
+ if (op == 12) {
+ op <<= 8;
+ op |= *p++;
+ }
+ *operator = op;
+ return p;
+}
+
+/* return 0 if not an operand */
+static int
+operand_length (unsigned char *p)
+{
+ unsigned char *begin = p;
+
+ if (*p == 28)
+ return 3;
+
+ if (*p == 29)
+ return 5;
+
+ if (*p >= 32 && *p <= 246)
+ return 1;
+
+ if (*p >= 247 && *p <= 254)
+ return 2;
+
+ if (*p == 30) {
+ while ((*p & 0x0f) != 0x0f)
+ p++;
+ return p - begin + 1;
+ }
+
+ return 0;
+}
+
+static unsigned char *
+encode_index_offset (unsigned char *p, int offset_size, unsigned long offset)
+{
+ while (--offset_size >= 0) {
+ p[offset_size] = (unsigned char) (offset & 0xff);
+ offset >>= 8;
+ }
+ return p + offset_size;
+}
+
+static unsigned long
+decode_index_offset(unsigned char *p, int off_size)
+{
+ unsigned long offset = 0;
+
+ while (off_size-- > 0)
+ offset = offset*256 + *p++;
+ return offset;
+}
+
+static void
+cff_index_init (cairo_array_t *index)
+{
+ _cairo_array_init (index, sizeof (cff_index_element_t));
+}
+
+static cairo_int_status_t
+cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr)
+{
+ cff_index_element_t element;
+ unsigned char *data, *p;
+ cairo_status_t status;
+ int offset_size, count, start, i;
+ int end = 0;
+
+ p = *ptr;
+ if (p + 2 > end_ptr)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ count = 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);
+ if (element->length > 0) {
+ status = _cairo_array_append_multiple (output,
+ element->data,
+ element->length);
+ }
+ if (unlikely (status))
+ return status;
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cff_index_set_object (cairo_array_t *index, int obj_index,
+ unsigned char *object , int length)
+{
+ cff_index_element_t *element;
+
+ element = _cairo_array_index (index, obj_index);
+ if (element == NULL)
+ return;
+
+ if (element->is_copy)
+ free (element->data);
+
+ element->data = object;
+ element->length = length;
+ element->is_copy = FALSE;
+}
+
+static cairo_status_t
+cff_index_append (cairo_array_t *index, unsigned char *object , int length)
+{
+ cff_index_element_t element;
+
+ element.length = length;
+ element.is_copy = FALSE;
+ element.data = object;
+
+ return _cairo_array_append (index, &element);
+}
+
+static cairo_status_t
+cff_index_append_copy (cairo_array_t *index,
+ const unsigned char *object,
+ unsigned int length)
+{
+ cff_index_element_t element;
+ cairo_status_t status;
+
+ element.length = length;
+ element.is_copy = TRUE;
+ element.data = malloc (element.length);
+ if (unlikely (element.data == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (element.data, object, element.length);
+
+ status = _cairo_array_append (index, &element);
+ if (unlikely (status)) {
+ free (element.data);
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cff_index_fini (cairo_array_t *index)
+{
+ cff_index_element_t *element;
+ unsigned int i;
+
+ for (i = 0; i < _cairo_array_num_elements (index); i++) {
+ element = _cairo_array_index (index, i);
+ if (element->is_copy && element->data)
+ free (element->data);
+ }
+ _cairo_array_fini (index);
+}
+
+static cairo_bool_t
+_cairo_cff_dict_equal (const void *key_a, const void *key_b)
+{
+ const cff_dict_operator_t *op_a = key_a;
+ const cff_dict_operator_t *op_b = key_b;
+
+ return op_a->operator == op_b->operator;
+}
+
+static cairo_status_t
+cff_dict_init (cairo_hash_table_t **dict)
+{
+ *dict = _cairo_hash_table_create (_cairo_cff_dict_equal);
+ if (unlikely (*dict == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_dict_init_key (cff_dict_operator_t *key, int operator)
+{
+ key->base.hash = (unsigned long) operator;
+ key->operator = operator;
+}
+
+static cairo_status_t
+cff_dict_create_operator (int operator,
+ unsigned char *operand,
+ int size,
+ cff_dict_operator_t **out)
+{
+ cff_dict_operator_t *op;
+
+ op = 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;
+ cff_index_element_t *element;
+ unsigned char *p;
+ int i, len;
+
+ cff_index_init (&index);
+ status = cff_index_read (&index, &font->current_ptr, font->data_end);
+ if (!font->is_opentype) {
+ element = _cairo_array_index (&index, 0);
+ if (element == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ p = element->data;
+ len = element->length;
+
+ /* If font name is prefixed with a subset tag, strip it off. */
+ if (len > 7 && p[6] == '+') {
+ for (i = 0; i < 6; i++)
+ if (p[i] < 'A' || p[i] > 'Z')
+ break;
+ if (i == 6) {
+ p += 7;
+ len -= 7;
+ }
+ }
+ font->ps_name = malloc (len + 1);
+ if (unlikely (font->ps_name == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (font->ps_name, p, len);
+ font->ps_name[len] = 0;
+ }
+ cff_index_fini (&index);
+
+ return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_private_dict (cairo_cff_font_t *font,
+ cairo_hash_table_t *private_dict,
+ cairo_array_t *local_sub_index,
+ int *local_sub_bias,
+ cairo_bool_t **local_subs_used,
+ double *default_width,
+ double *nominal_width,
+ unsigned char *ptr,
+ int size)
+{
+ cairo_int_status_t status;
+ unsigned char buf[10];
+ unsigned char *end_buf;
+ int offset;
+ int i;
+ unsigned char *operand;
+ unsigned char *p;
+ int num_subs;
+
+ status = cff_dict_read (private_dict, ptr, size);
+ if (unlikely (status))
+ return status;
+
+ operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i);
+ if (operand) {
+ decode_integer (operand, &offset);
+ p = ptr + offset;
+ status = cff_index_read (local_sub_index, &p, font->data_end);
+ if (unlikely (status))
+ return status;
+
+ /* Use maximum sized encoding to reserve space for later modification. */
+ end_buf = encode_integer_max (buf, 0);
+ status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+ }
+
+ *default_width = 0;
+ operand = cff_dict_get_operands (private_dict, DEFAULTWIDTH_OP, &i);
+ if (operand)
+ decode_number (operand, default_width);
+
+ *nominal_width = 0;
+ operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i);
+ if (operand)
+ decode_number (operand, nominal_width);
+
+ num_subs = _cairo_array_num_elements (local_sub_index);
+ *local_subs_used = calloc (num_subs, sizeof (cairo_bool_t));
+ if (unlikely (*local_subs_used == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (num_subs < 1240)
+ *local_sub_bias = 107;
+ else if (num_subs < 33900)
+ *local_sub_bias = 1131;
+ else
+ *local_sub_bias = 32768;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p)
+{
+ int type, num_ranges, first, last, fd, i, j;
+
+ font->fdselect = calloc (font->num_glyphs, sizeof (int));
+ if (unlikely (font->fdselect == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ type = *p++;
+ if (type == 0)
+ {
+ for (i = 0; i < font->num_glyphs; i++)
+ font->fdselect[i] = *p++;
+ } else if (type == 3) {
+ num_ranges = 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;
+ }
+
+ font->fd_local_sub_bias = calloc (sizeof (int), font->num_fontdicts);
+ if (unlikely (font->fd_local_sub_bias == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+
+ font->fd_local_subs_used = calloc (sizeof (cairo_bool_t *), font->num_fontdicts);
+ if (unlikely (font->fd_local_subs_used == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+
+ font->fd_default_width = calloc (sizeof (int), font->num_fontdicts);
+ if (unlikely (font->fd_default_width == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+
+ font->fd_nominal_width = calloc (sizeof (int), font->num_fontdicts);
+ if (unlikely (font->fd_nominal_width == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+
+ for (i = 0; i < font->num_fontdicts; i++) {
+ status = cff_dict_init (&font->fd_dict[i]);
+ if (unlikely (status))
+ goto fail;
+
+ element = _cairo_array_index (&index, i);
+ status = cff_dict_read (font->fd_dict[i], element->data, element->length);
+ if (unlikely (status))
+ goto fail;
+
+ operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size);
+ if (operand == NULL) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto fail;
+ }
+ operand = decode_integer (operand, &size);
+ decode_integer (operand, &offset);
+ status = cff_dict_init (&font->fd_private_dict[i]);
+ if (unlikely (status))
+ goto fail;
+
+ cff_index_init (&font->fd_local_sub_index[i]);
+ status = cairo_cff_font_read_private_dict (font,
+ font->fd_private_dict[i],
+ &font->fd_local_sub_index[i],
+ &font->fd_local_sub_bias[i],
+ &font->fd_local_subs_used[i],
+ &font->fd_default_width[i],
+ &font->fd_nominal_width[i],
+ font->data + offset,
+ size);
+ if (unlikely (status))
+ goto fail;
+
+ /* Set integer operand to max value to use max size encoding to reserve
+ * space for any value later */
+ end_buf = encode_integer_max (buf, 0);
+ end_buf = encode_integer_max (end_buf, 0);
+ status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail:
+ cff_index_fini (&index);
+
+ return status;
+}
+
+static void
+cairo_cff_font_read_font_metrics (cairo_cff_font_t *font, cairo_hash_table_t *top_dict)
+{
+ unsigned char *p;
+ unsigned char *end;
+ int size;
+ double x_min, y_min, x_max, y_max;
+ double xx, yx, xy, yy;
+
+ x_min = 0.0;
+ y_min = 0.0;
+ x_max = 0.0;
+ y_max = 0.0;
+ p = cff_dict_get_operands (font->top_dict, FONTBBOX_OP, &size);
+ if (p) {
+ end = p + size;
+ if (p < end)
+ p = decode_number (p, &x_min);
+ if (p < end)
+ p = decode_number (p, &y_min);
+ if (p < end)
+ p = decode_number (p, &x_max);
+ if (p < end)
+ p = decode_number (p, &y_max);
+ }
+ font->x_min = floor (x_min);
+ font->y_min = floor (y_min);
+ font->x_max = floor (x_max);
+ font->y_max = floor (y_max);
+ font->ascent = font->y_max;
+ font->descent = font->y_min;
+
+ xx = 0.001;
+ yx = 0.0;
+ xy = 0.0;
+ yy = 0.001;
+ p = cff_dict_get_operands (font->top_dict, FONTMATRIX_OP, &size);
+ if (p) {
+ end = p + size;
+ if (p < end)
+ p = decode_number (p, &xx);
+ if (p < end)
+ p = decode_number (p, &yx);
+ if (p < end)
+ p = decode_number (p, &xy);
+ if (p < end)
+ p = decode_number (p, &yy);
+ }
+ /* Freetype uses 1/yy to get units per EM */
+ font->units_per_em = _cairo_round(1.0/yy);
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
+{
+ cairo_array_t index;
+ cff_index_element_t *element;
+ unsigned char buf[20];
+ unsigned char *end_buf;
+ unsigned char *operand;
+ cairo_int_status_t status;
+ unsigned char *p;
+ int size;
+ int offset;
+
+ cff_index_init (&index);
+ status = cff_index_read (&index, &font->current_ptr, font->data_end);
+ if (unlikely (status))
+ goto fail;
+
+ element = _cairo_array_index (&index, 0);
+ if (element == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto fail;
+ }
+
+ 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);
+ if (operand == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto fail;
+ }
+
+ decode_integer (operand, &offset);
+ p = font->data + offset;
+ status = cff_index_read (&font->charstrings_index, &p, font->data_end);
+ if (unlikely (status))
+ goto fail;
+ font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index);
+
+ if (font->is_cid) {
+ operand = cff_dict_get_operands (font->top_dict, CHARSET_OP, &size);
+ if (!operand)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ decode_integer (operand, &offset);
+ font->charset = font->data + offset;
+ if (font->charset >= font->data_end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (!font->is_opentype)
+ cairo_cff_font_read_font_metrics (font, font->top_dict);
+
+ if (font->is_cid) {
+ operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size);
+ if (operand == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto fail;
+ }
+ 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);
+ if (operand == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ goto fail;
+ }
+
+ decode_integer (operand, &offset);
+ status = cairo_cff_font_read_cid_fontdict (font, font->data + offset);
+ if (unlikely (status))
+ goto fail;
+ } else {
+ operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size);
+ operand = decode_integer (operand, &size);
+ decode_integer (operand, &offset);
+ status = cairo_cff_font_read_private_dict (font,
+ font->private_dict,
+ &font->local_sub_index,
+ &font->local_sub_bias,
+ &font->local_subs_used,
+ &font->default_width,
+ &font->nominal_width,
+ font->data + offset,
+ size);
+ if (unlikely (status))
+ goto fail;
+ }
+
+ /* Use maximum sized encoding to reserve space for later modification. */
+ end_buf = encode_integer_max (buf, 0);
+ status = cff_dict_set_operands (font->top_dict,
+ CHARSTRINGS_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ status = cff_dict_set_operands (font->top_dict,
+ CHARSET_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ if (font->scaled_font_subset->is_latin) {
+ status = cff_dict_set_operands (font->top_dict,
+ ENCODING_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ /* Private has two operands - size and offset */
+ end_buf = encode_integer_max (end_buf, 0);
+ cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf);
+
+ } else {
+ status = cff_dict_set_operands (font->top_dict,
+ FDSELECT_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ status = cff_dict_set_operands (font->top_dict,
+ FDARRAY_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ cff_dict_remove (font->top_dict, ENCODING_OP);
+ cff_dict_remove (font->top_dict, PRIVATE_OP);
+ }
+
+ /* Remove the unique identifier operators as the subsetted font is
+ * not the same is the original font. */
+ cff_dict_remove (font->top_dict, UNIQUEID_OP);
+ cff_dict_remove (font->top_dict, XUID_OP);
+
+fail:
+ cff_index_fini (&index);
+
+ return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_strings (cairo_cff_font_t *font)
+{
+ return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end);
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font)
+{
+ cairo_int_status_t status;
+ int num_subs;
+
+ status = cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
+ if (unlikely (status))
+ return status;
+
+ num_subs = _cairo_array_num_elements (&font->global_sub_index);
+ font->global_subs_used = calloc (num_subs, sizeof(cairo_bool_t));
+ if (unlikely (font->global_subs_used == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (num_subs < 1240)
+ font->global_sub_bias = 107;
+ else if (num_subs < 33900)
+ font->global_sub_bias = 1131;
+ else
+ font->global_sub_bias = 32768;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_int_status_t
+(*font_read_t) (cairo_cff_font_t *font);
+
+static const font_read_t font_read_funcs[] = {
+ cairo_cff_font_read_header,
+ cairo_cff_font_read_name,
+ cairo_cff_font_read_top_dict,
+ cairo_cff_font_read_strings,
+ cairo_cff_font_read_global_subroutines,
+};
+
+static cairo_int_status_t
+cairo_cff_font_read_font (cairo_cff_font_t *font)
+{
+ cairo_int_status_t status;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) {
+ status = font_read_funcs[i] (font);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_set_ros_strings (cairo_cff_font_t *font)
+{
+ cairo_status_t status;
+ unsigned char buf[30];
+ unsigned char *p;
+ int sid1, sid2;
+ const char *registry = "Adobe";
+ const char *ordering = "Identity";
+
+ sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+ status = cff_index_append_copy (&font->strings_subset_index,
+ (unsigned char *)registry,
+ strlen(registry));
+ if (unlikely (status))
+ return status;
+
+ sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+ status = cff_index_append_copy (&font->strings_subset_index,
+ (unsigned char *)ordering,
+ strlen(ordering));
+ if (unlikely (status))
+ return status;
+
+ p = encode_integer (buf, sid1);
+ p = encode_integer (p, sid2);
+ p = encode_integer (p, 0);
+ status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf);
+ if (unlikely (status))
+ return status;
+
+ p = encode_integer (buf, font->scaled_font_subset->num_glyphs);
+ status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_dict_string(cairo_cff_font_t *font,
+ cairo_hash_table_t *dict,
+ int operator)
+{
+ int size;
+ unsigned char *p;
+ int sid;
+ unsigned char buf[100];
+ cff_index_element_t *element;
+ cairo_status_t status;
+
+ p = cff_dict_get_operands (dict, operator, &size);
+ if (!p)
+ return CAIRO_STATUS_SUCCESS;
+
+ decode_integer (p, &sid);
+ if (sid < NUM_STD_STRINGS)
+ return CAIRO_STATUS_SUCCESS;
+
+ element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS);
+ if(element == NULL)
+ return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+ sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+ status = cff_index_append (&font->strings_subset_index, element->data, element->length);
+ if (unlikely (status))
+ return status;
+
+ p = encode_integer (buf, sid);
+ status = cff_dict_set_operands (dict, operator, buf, p - buf);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const int dict_strings[] = {
+ VERSION_OP,
+ NOTICE_OP,
+ COPYRIGHT_OP,
+ FULLNAME_OP,
+ FAMILYNAME_OP,
+ WEIGHT_OP,
+ POSTSCRIPT_OP,
+ BASEFONTNAME_OP,
+ FONTNAME_OP,
+};
+
+static cairo_status_t
+cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font,
+ cairo_hash_table_t *dict)
+{
+ cairo_status_t status;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) {
+ status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned char *
+type2_decode_integer (unsigned char *p, int *integer)
+{
+ if (*p == 28) {
+ *integer = p[1] << 8 | p[2];
+ p += 3;
+ } else if (*p <= 246) {
+ *integer = *p++ - 139;
+ } else if (*p <= 250) {
+ *integer = (p[0] - 247) * 256 + p[1] + 108;
+ p += 2;
+ } else if (*p <= 254) {
+ *integer = -(p[0] - 251) * 256 - p[1] - 108;
+ p += 2;
+ } else { /* *p == 255 */
+ /* 16.16 fixed-point number. The fraction is ignored. */
+ *integer = (int16_t)((p[1] << 8) | p[2]);
+ p += 5;
+ }
+ return p;
+}
+
+/* Type 2 charstring parser for finding calls to local or global
+ * subroutines. For non Opentype CFF fonts it also gets the glyph
+ * widths.
+ *
+ * When we find a subroutine operator, the subroutine is marked as in
+ * use and recursively followed. The subroutine number is the value on
+ * the top of the stack when the subroutine operator is executed. In
+ * most fonts the subroutine number is encoded in an integer
+ * immediately preceding the subroutine operator. However it is
+ * possible for the subroutine number on the stack to be the result of
+ * a computation (in which case there will be an operator preceding
+ * the subroutine operator). If this occurs, subroutine subsetting is
+ * disabled since we can't easily determine which subroutines are
+ * used.
+ *
+ * The width, if present, is the first integer in the charstring. The
+ * only way to confirm if the integer at the start of the charstring is
+ * the width is when the first stack clearing operator is parsed,
+ * check if there is an extra integer left over on the stack.
+ *
+ * When the first stack clearing operator is encountered
+ * type2_find_width is set to FALSE and type2_found_width is set to
+ * TRUE if an extra argument is found, otherwise FALSE.
+ */
+static cairo_status_t
+cairo_cff_parse_charstring (cairo_cff_font_t *font,
+ unsigned char *charstring, int length,
+ int glyph_id,
+ cairo_bool_t need_width)
+{
+ unsigned char *p = charstring;
+ unsigned char *end = charstring + length;
+ int integer;
+ int hint_bytes;
+ int sub_num;
+ cff_index_element_t *element;
+ int fd;
+
+ while (p < end) {
+ if (*p == 28 || *p >= 32) {
+ /* Integer value */
+ p = type2_decode_integer (p, &integer);
+ font->type2_stack_size++;
+ font->type2_stack_top_value = integer;
+ font->type2_stack_top_is_int = TRUE;
+ if (!font->type2_seen_first_int) {
+ font->type2_width = integer;
+ font->type2_seen_first_int = TRUE;
+ }
+ } else if (*p == TYPE2_hstem || *p == TYPE2_vstem ||
+ *p == TYPE2_hstemhm || *p == TYPE2_vstemhm) {
+ /* Hint operator. The number of hints declared by the
+ * operator depends on the size of the stack. */
+ font->type2_stack_top_is_int = FALSE;
+ font->type2_num_hints += font->type2_stack_size/2;
+ if (font->type2_find_width && font->type2_stack_size % 2)
+ font->type2_found_width = TRUE;
+
+ font->type2_stack_size = 0;
+ font->type2_find_width = FALSE;
+ p++;
+ } else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) {
+ /* Hintmask operator. These operators are followed by a
+ * variable length mask where the length depends on the
+ * number of hints declared. The first time this is called
+ * it is also an implicit vstem if there are arguments on
+ * the stack. */
+ if (font->type2_hintmask_bytes == 0) {
+ font->type2_stack_top_is_int = FALSE;
+ font->type2_num_hints += font->type2_stack_size/2;
+ if (font->type2_find_width && font->type2_stack_size % 2)
+ font->type2_found_width = TRUE;
+
+ font->type2_stack_size = 0;
+ font->type2_find_width = FALSE;
+ font->type2_hintmask_bytes = (font->type2_num_hints+7)/8;
+ }
+
+ hint_bytes = font->type2_hintmask_bytes;
+ p++;
+ p += hint_bytes;
+ } else if (*p == TYPE2_rmoveto) {
+ if (font->type2_find_width && font->type2_stack_size > 2)
+ font->type2_found_width = TRUE;
+
+ font->type2_stack_size = 0;
+ font->type2_find_width = FALSE;
+ font->type2_has_path = TRUE;
+ p++;
+ } else if (*p == TYPE2_hmoveto || *p == TYPE2_vmoveto) {
+ if (font->type2_find_width && font->type2_stack_size > 1)
+ font->type2_found_width = TRUE;
+
+ font->type2_stack_size = 0;
+ font->type2_find_width = FALSE;
+ font->type2_has_path = TRUE;
+ p++;
+ } else if (*p == TYPE2_endchar) {
+ if (!font->type2_has_path && font->type2_stack_size > 3)
+ return CAIRO_INT_STATUS_UNSUPPORTED; /* seac (Ref Appendix C of Type 2 Charstring Format */
+
+ if (font->type2_find_width && font->type2_stack_size > 0)
+ font->type2_found_width = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+ } else if (*p == TYPE2_callsubr) {
+ /* call to local subroutine */
+ if (! font->type2_stack_top_is_int)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ p++;
+ font->type2_stack_top_is_int = FALSE;
+ font->type2_stack_size--;
+ if (font->type2_find_width && font->type2_stack_size == 0)
+ font->type2_seen_first_int = FALSE;
+
+ if (font->is_cid) {
+ fd = font->fdselect[glyph_id];
+ sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd];
+ element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num);
+ if (element == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ if (! font->fd_local_subs_used[fd][sub_num]) {
+ font->fd_local_subs_used[fd][sub_num] = TRUE;
+ cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
+ }
+ } else {
+ sub_num = font->type2_stack_top_value + font->local_sub_bias;
+ element = _cairo_array_index (&font->local_sub_index, sub_num);
+ if (element == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ if (! font->local_subs_used[sub_num] ||
+ (need_width && !font->type2_found_width))
+ {
+ font->local_subs_used[sub_num] = TRUE;
+ cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
+ }
+ }
+ font->type2_nesting_level--;
+ } else if (*p == TYPE2_callgsubr) {
+ /* call to global subroutine */
+ if (! font->type2_stack_top_is_int)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ p++;
+ font->type2_stack_size--;
+ font->type2_stack_top_is_int = FALSE;
+ if (font->type2_find_width && font->type2_stack_size == 0)
+ font->type2_seen_first_int = FALSE;
+
+ sub_num = font->type2_stack_top_value + font->global_sub_bias;
+ element = _cairo_array_index (&font->global_sub_index, sub_num);
+ if (element == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ if (! font->global_subs_used[sub_num] ||
+ (need_width && !font->type2_found_width))
+ {
+ font->global_subs_used[sub_num] = TRUE;
+ cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
+ }
+ font->type2_nesting_level--;
+ } else if (*p == 12) {
+ /* 2 byte instruction */
+
+ /* All the 2 byte operators are either not valid before a
+ * stack clearing operator or they are one of the
+ * arithmetic, storage, or conditional operators. */
+ if (need_width && font->type2_find_width)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ p += 2;
+ font->type2_stack_top_is_int = FALSE;
+ } else {
+ /* 1 byte instruction */
+ p++;
+ font->type2_stack_top_is_int = FALSE;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_find_width_and_subroutines_used (cairo_cff_font_t *font,
+ unsigned char *charstring, int length,
+ int glyph_id, int subset_id)
+{
+ cairo_status_t status;
+ int width;
+ int fd;
+
+ font->type2_stack_size = 0;
+ font->type2_stack_top_value = 0;;
+ font->type2_stack_top_is_int = FALSE;
+ font->type2_num_hints = 0;
+ font->type2_hintmask_bytes = 0;
+ font->type2_nesting_level = 0;
+ font->type2_seen_first_int = FALSE;
+ font->type2_find_width = TRUE;
+ font->type2_found_width = FALSE;
+ font->type2_width = 0;
+ font->type2_has_path = FALSE;
+
+ status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE);
+ if (status)
+ return status;
+
+ if (!font->is_opentype) {
+ if (font->is_cid) {
+ fd = font->fdselect[glyph_id];
+ if (font->type2_found_width)
+ width = font->fd_nominal_width[fd] + font->type2_width;
+ else
+ width = font->fd_default_width[fd];
+ } else {
+ if (font->type2_found_width)
+ width = font->nominal_width + font->type2_width;
+ else
+ width = font->default_width;
+ }
+ font->widths[subset_id] = width;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_get_gid_for_cid (cairo_cff_font_t *font, unsigned long cid, unsigned long *gid)
+{
+ unsigned char *p;
+ unsigned long first_gid;
+ unsigned long first_cid;
+ int num_left;
+ unsigned long c, g;
+
+ if (cid == 0) {
+ *gid = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ switch (font->charset[0]) {
+ /* Format 0 */
+ case 0:
+ p = font->charset + 1;
+ g = 1;
+ while (g <= (unsigned)font->num_glyphs && p < font->data_end) {
+ c = be16_to_cpu( *((uint16_t *)p) );
+ if (c == cid) {
+ *gid = g;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ g++;
+ p += 2;
+ }
+ break;
+
+ /* Format 1 */
+ case 1:
+ first_gid = 1;
+ p = font->charset + 1;
+ while (first_gid <= (unsigned)font->num_glyphs && p + 2 < font->data_end) {
+ first_cid = be16_to_cpu( *((uint16_t *)p) );
+ num_left = p[2];
+ if (cid >= first_cid && cid <= first_cid + num_left) {
+ *gid = first_gid + cid - first_cid;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ first_gid += num_left + 1;
+ p += 3;
+ }
+ break;
+
+ /* Format 2 */
+ case 2:
+ first_gid = 1;
+ p = font->charset + 1;
+ while (first_gid <= (unsigned)font->num_glyphs && p + 3 < font->data_end) {
+ first_cid = be16_to_cpu( *((uint16_t *)p) );
+ num_left = be16_to_cpu( *((uint16_t *)(p+2)) );
+ if (cid >= first_cid && cid <= first_cid + num_left) {
+ *gid = first_gid + cid - first_cid;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ first_gid += num_left + 1;
+ p += 4;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font)
+{
+ cff_index_element_t *element;
+ unsigned int i;
+ cairo_int_status_t status;
+ unsigned long glyph, cid;
+
+ font->subset_subroutines = TRUE;
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ if (font->is_cid) {
+ cid = font->scaled_font_subset->glyphs[i];
+ status = cairo_cff_font_get_gid_for_cid (font, cid, &glyph);
+ if (unlikely (status))
+ return status;
+ } else {
+ glyph = font->scaled_font_subset->glyphs[i];
+ }
+ element = _cairo_array_index (&font->charstrings_index, glyph);
+ if (element == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ status = cff_index_append (&font->charstrings_subset_index,
+ element->data,
+ element->length);
+ if (unlikely (status))
+ return status;
+
+ if (font->subset_subroutines) {
+ status = cairo_cff_find_width_and_subroutines_used (font,
+ element->data, element->length,
+ glyph, i);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ /* If parsing the charstrings fails we embed all the
+ * subroutines. But if the font is not opentype we
+ * need to successfully parse all charstrings to get
+ * the widths. */
+ font->subset_subroutines = FALSE;
+ if (!font->is_opentype)
+ return status;
+ } else if (unlikely (status)) {
+ return status;
+ }
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_fontdict (cairo_cff_font_t *font)
+{
+ unsigned int i;
+ int fd;
+ int *reverse_map;
+ unsigned long cid, gid;
+ cairo_int_status_t status;
+
+ font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs,
+ sizeof (int));
+ if (unlikely (font->fdselect_subset == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int));
+ if (unlikely (font->fd_subset_map == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int));
+ if (unlikely (font->private_dict_offset == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ reverse_map = calloc (font->num_fontdicts, sizeof (int));
+ if (unlikely (reverse_map == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (i = 0; i < font->num_fontdicts; i++)
+ reverse_map[i] = -1;
+
+ font->num_subset_fontdicts = 0;
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ cid = font->scaled_font_subset->glyphs[i];
+ status = cairo_cff_font_get_gid_for_cid (font, cid, &gid);
+ if (unlikely (status)) {
+ free (reverse_map);
+ return status;
+ }
+
+ fd = font->fdselect[gid];
+ if (reverse_map[fd] < 0) {
+ font->fd_subset_map[font->num_subset_fontdicts] = fd;
+ reverse_map[fd] = font->num_subset_fontdicts++;
+ }
+ font->fdselect_subset[i] = reverse_map[fd];
+ }
+
+ free (reverse_map);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font)
+{
+ unsigned char buf[100];
+ unsigned char *end_buf;
+ cairo_status_t status;
+
+ font->num_fontdicts = 1;
+ font->fd_dict = 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;
+}
+
+/* The Euro is the only the only character in the winansi encoding
+ * with a glyph name that is not a CFF standard string. As the strings
+ * are written before the charset, we need to check during the
+ * subsetting phase if the Euro glyph is required and add the
+ * glyphname to the list of strings to write out.
+ */
+static cairo_status_t
+cairo_cff_font_add_euro_charset_string (cairo_cff_font_t *font)
+{
+ cairo_status_t status;
+ unsigned int i;
+ int ch;
+ const char *euro = "Euro";
+
+ for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+ ch = font->scaled_font_subset->to_latin_char[i];
+ if (ch == 128) {
+ font->euro_sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+ status = cff_index_append_copy (&font->strings_subset_index,
+ (unsigned char *)euro, strlen(euro));
+ return status;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_font (cairo_cff_font_t *font)
+{
+ cairo_status_t status;
+
+ if (!font->scaled_font_subset->is_latin) {
+ status = cairo_cff_font_set_ros_strings (font);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cairo_cff_font_subset_charstrings_and_subroutines (font);
+ if (unlikely (status))
+ return status;
+
+ if (!font->scaled_font_subset->is_latin) {
+ if (font->is_cid)
+ status = cairo_cff_font_subset_fontdict (font);
+ else
+ status = cairo_cff_font_create_cid_fontdict (font);
+ if (unlikely (status))
+ return status;
+ } else {
+ font->private_dict_offset = malloc (sizeof (int));
+ if (unlikely (font->private_dict_offset == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ status = cairo_cff_font_subset_strings (font);
+ if (unlikely (status))
+ return status;
+
+ if (font->scaled_font_subset->is_latin)
+ status = cairo_cff_font_add_euro_charset_string (font);
+
+ return status;
+}
+
+/* Set the operand of the specified operator in the (already written)
+ * top dict to point to the current position in the output
+ * array. Operands updated with this function must have previously
+ * been encoded with the 5-byte (max) integer encoding. */
+static void
+cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t *font,
+ int operator)
+{
+ int cur_pos;
+ int offset;
+ int size;
+ unsigned char buf[10];
+ unsigned char *buf_end;
+ unsigned char *op_ptr;
+
+ cur_pos = _cairo_array_num_elements (&font->output);
+ buf_end = encode_integer_max (buf, cur_pos);
+ offset = cff_dict_get_location (font->top_dict, operator, &size);
+ assert (offset > 0);
+ op_ptr = _cairo_array_index (&font->output, offset);
+ memcpy (op_ptr, buf, buf_end - buf);
+}
+
+static cairo_status_t
+cairo_cff_font_write_header (cairo_cff_font_t *font)
+{
+ return _cairo_array_append_multiple (&font->output,
+ font->header,
+ font->header->header_size);
+}
+
+static cairo_status_t
+cairo_cff_font_write_name (cairo_cff_font_t *font)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_array_t index;
+
+ cff_index_init (&index);
+
+ status = cff_index_append_copy (&index,
+ (unsigned char *) font->ps_name,
+ strlen(font->ps_name));
+ if (unlikely (status))
+ goto FAIL;
+
+ status = cff_index_write (&index, &font->output);
+ if (unlikely (status))
+ goto FAIL;
+
+FAIL:
+ cff_index_fini (&index);
+
+ return status;
+}
+
+static cairo_status_t
+cairo_cff_font_write_top_dict (cairo_cff_font_t *font)
+{
+ uint16_t count;
+ unsigned char buf[10];
+ unsigned char *p;
+ int offset_index;
+ int dict_start, dict_size;
+ int offset_size = 4;
+ cairo_status_t status;
+
+ /* Write an index containing the top dict */
+
+ count = cpu_to_be16 (1);
+ status = _cairo_array_append_multiple (&font->output, &count, 2);
+ if (unlikely (status))
+ return status;
+ buf[0] = offset_size;
+ status = _cairo_array_append (&font->output, buf);
+ if (unlikely (status))
+ return status;
+ encode_index_offset (buf, offset_size, 1);
+ status = _cairo_array_append_multiple (&font->output, buf, offset_size);
+ if (unlikely (status))
+ return status;
+
+ /* Reserve space for last element of offset array and update after
+ * dict is written */
+ offset_index = _cairo_array_num_elements (&font->output);
+ status = _cairo_array_append_multiple (&font->output, buf, offset_size);
+ if (unlikely (status))
+ return status;
+
+ dict_start = _cairo_array_num_elements (&font->output);
+ status = cff_dict_write (font->top_dict, &font->output);
+ if (unlikely (status))
+ return status;
+ dict_size = _cairo_array_num_elements (&font->output) - dict_start;
+
+ encode_index_offset (buf, offset_size, dict_size + 1);
+ p = _cairo_array_index (&font->output, offset_index);
+ if (p == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ memcpy (p, buf, offset_size);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_strings (cairo_cff_font_t *font)
+{
+ return cff_index_write (&font->strings_subset_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_global_subrs (cairo_cff_font_t *font)
+{
+ unsigned int i;
+ unsigned char return_op = TYPE2_return;
+
+ /* poppler and fontforge don't like zero length subroutines so we
+ * replace unused subroutines with a 'return' instruction. */
+ if (font->subset_subroutines) {
+ for (i = 0; i < _cairo_array_num_elements (&font->global_sub_index); i++) {
+ if (! font->global_subs_used[i])
+ cff_index_set_object (&font->global_sub_index, i, &return_op, 1);
+ }
+ }
+
+ return cff_index_write (&font->global_sub_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_encoding (cairo_cff_font_t *font)
+{
+ unsigned char buf[2];
+ cairo_status_t status;
+ unsigned int i;
+
+ cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP);
+ buf[0] = 0; /* Format 0 */
+ buf[1] = font->scaled_font_subset->num_glyphs - 1;
+ status = _cairo_array_append_multiple (&font->output, buf, 2);
+ if (unlikely (status))
+ return status;
+
+ for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+ unsigned char ch = font->scaled_font_subset->to_latin_char[i];
+ status = _cairo_array_append (&font->output, &ch);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_fdselect (cairo_cff_font_t *font)
+{
+ unsigned char data;
+ unsigned int i;
+ cairo_int_status_t status;
+
+ cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP);
+
+ if (font->is_cid) {
+ data = 0;
+ status = _cairo_array_append (&font->output, &data);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ data = font->fdselect_subset[i];
+ status = _cairo_array_append (&font->output, &data);
+ if (unlikely (status))
+ return status;
+ }
+ } else {
+ unsigned char byte;
+ uint16_t word;
+
+ status = _cairo_array_grow_by (&font->output, 9);
+ if (unlikely (status))
+ return status;
+
+ byte = 3;
+ status = _cairo_array_append (&font->output, &byte);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ word = cpu_to_be16 (1);
+ status = _cairo_array_append_multiple (&font->output, &word, 2);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ word = cpu_to_be16 (0);
+ status = _cairo_array_append_multiple (&font->output, &word, 2);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ byte = 0;
+ status = _cairo_array_append (&font->output, &byte);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ word = cpu_to_be16 (font->scaled_font_subset->num_glyphs);
+ status = _cairo_array_append_multiple (&font->output, &word, 2);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Winansi to CFF standard strings mapping for characters 128 to 255 */
+static const int winansi_to_cff_std_string[] = {
+ /* 128 */
+ 0, 0, 117, 101, 118, 121, 112, 113,
+ 126, 122, 192, 107, 142, 0, 199, 0,
+ /* 144 */
+ 0, 65, 8, 105, 119, 116, 111, 137,
+ 127, 153, 221, 108, 148, 0, 228, 198,
+ /* 160 */
+ 0, 96, 97, 98, 103, 100, 160, 102,
+ 131, 170, 139, 106, 151, 0, 165, 128,
+ /* 176 */
+ 161, 156, 164, 169, 125, 152, 115, 114,
+ 133, 150, 143, 120, 158, 155, 163, 123,
+ /* 192 */
+ 174, 171, 172, 176, 173, 175, 138, 177,
+ 181, 178, 179, 180, 185, 182, 183, 184,
+ /* 208 */
+ 154, 186, 190, 187, 188, 191, 189, 168,
+ 141, 196, 193, 194, 195, 197, 157, 149,
+ /* 224 */
+ 203, 200, 201, 205, 202, 204, 144, 206,
+ 210, 207, 208, 209, 214, 211, 212, 213,
+ /* 240 */
+ 167, 215, 219, 216, 217, 220, 218, 159,
+ 147, 225, 222, 223, 224, 226, 162, 227,
+};
+
+static int
+cairo_cff_font_get_sid_for_winansi_char (cairo_cff_font_t *font, int ch)
+{
+ int sid;
+
+ if (ch == 39) {
+ sid = 104;
+
+ } else if (ch == 96) {
+ sid = 124;
+
+ } else if (ch >= 32 && ch <= 126) {
+ sid = ch - 31;
+
+ } else if (ch == 128) {
+ assert (font->euro_sid >= NUM_STD_STRINGS);
+ sid = font->euro_sid;
+
+ } else if (ch >= 128 && ch <= 255) {
+ sid = winansi_to_cff_std_string[ch - 128];
+
+ } else {
+ sid = 0;
+ }
+
+ return sid;
+}
+
+static cairo_status_t
+cairo_cff_font_write_type1_charset (cairo_cff_font_t *font)
+{
+ unsigned char format = 0;
+ unsigned int i;
+ int ch, sid;
+ cairo_status_t status;
+ uint16_t sid_be16;
+
+ cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
+ status = _cairo_array_append (&font->output, &format);
+ if (unlikely (status))
+ return status;
+
+ for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+ ch = font->scaled_font_subset->to_latin_char[i];
+ sid = cairo_cff_font_get_sid_for_winansi_char (font, ch);
+
+ sid_be16 = cpu_to_be16(sid);
+ status = _cairo_array_append_multiple (&font->output, &sid_be16, sizeof(sid_be16));
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_cid_charset (cairo_cff_font_t *font)
+{
+ unsigned char byte;
+ uint16_t word;
+ cairo_status_t status;
+
+ cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
+ status = _cairo_array_grow_by (&font->output, 5);
+ if (unlikely (status))
+ return status;
+
+ byte = 2;
+ status = _cairo_array_append (&font->output, &byte);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ word = cpu_to_be16 (1);
+ status = _cairo_array_append_multiple (&font->output, &word, 2);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2);
+ status = _cairo_array_append_multiple (&font->output, &word, 2);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_charstrings (cairo_cff_font_t *font)
+{
+ cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP);
+
+ return cff_index_write (&font->charstrings_subset_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font)
+{
+ unsigned int i;
+ cairo_int_status_t status;
+ unsigned int offset_array;
+ 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,
+ cairo_bool_t *local_subs_used)
+{
+ int offset;
+ int size;
+ unsigned char buf[10];
+ unsigned char *buf_end;
+ unsigned char *p;
+ cairo_status_t status;
+ unsigned int i;
+ unsigned char return_op = TYPE2_return;
+
+ if (_cairo_array_num_elements (local_sub_index) > 0) {
+ /* Write local subroutines and update offset in private
+ * dict. Local subroutines offset is relative to start of
+ * private dict */
+ offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num];
+ buf_end = encode_integer_max (buf, offset);
+ offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size);
+ assert (offset > 0);
+ p = _cairo_array_index (&font->output, offset);
+ memcpy (p, buf, buf_end - buf);
+
+ /* poppler and fontforge don't like zero length subroutines so
+ * we replace unused subroutines with a 'return' instruction.
+ */
+ if (font->subset_subroutines) {
+ for (i = 0; i < _cairo_array_num_elements (local_sub_index); i++) {
+ if (! local_subs_used[i])
+ cff_index_set_object (local_sub_index, i, &return_op, 1);
+ }
+ }
+ status = cff_index_write (local_sub_index, &font->output);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font)
+{
+ unsigned int i;
+ cairo_int_status_t status;
+
+ if (font->is_cid) {
+ for (i = 0; i < font->num_subset_fontdicts; i++) {
+ status = cairo_cff_font_write_private_dict (
+ font,
+ i,
+ font->fd_dict[font->fd_subset_map[i]],
+ font->fd_private_dict[font->fd_subset_map[i]]);
+ if (unlikely (status))
+ return status;
+ }
+
+ for (i = 0; i < font->num_subset_fontdicts; i++) {
+ status = cairo_cff_font_write_local_sub (
+ font,
+ i,
+ font->fd_private_dict[font->fd_subset_map[i]],
+ &font->fd_local_sub_index[font->fd_subset_map[i]],
+ font->fd_local_subs_used[font->fd_subset_map[i]]);
+ if (unlikely (status))
+ return status;
+ }
+ } else {
+ status = cairo_cff_font_write_private_dict (font,
+ 0,
+ font->fd_dict[0],
+ font->private_dict);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_cff_font_write_local_sub (font,
+ 0,
+ font->private_dict,
+ &font->local_sub_index,
+ font->local_subs_used);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_type1_private_dict_and_local_sub (cairo_cff_font_t *font)
+{
+ cairo_int_status_t status;
+
+ status = cairo_cff_font_write_private_dict (font,
+ 0,
+ font->top_dict,
+ font->private_dict);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_cff_font_write_local_sub (font,
+ 0,
+ font->private_dict,
+ &font->local_sub_index,
+ font->local_subs_used);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+typedef cairo_status_t
+(*font_write_t) (cairo_cff_font_t *font);
+
+static const font_write_t font_write_cid_funcs[] = {
+ cairo_cff_font_write_header,
+ cairo_cff_font_write_name,
+ cairo_cff_font_write_top_dict,
+ cairo_cff_font_write_strings,
+ cairo_cff_font_write_global_subrs,
+ cairo_cff_font_write_cid_charset,
+ cairo_cff_font_write_fdselect,
+ cairo_cff_font_write_charstrings,
+ cairo_cff_font_write_cid_fontdict,
+ cairo_cff_font_write_cid_private_dict_and_local_sub,
+};
+
+static const font_write_t font_write_type1_funcs[] = {
+ cairo_cff_font_write_header,
+ cairo_cff_font_write_name,
+ cairo_cff_font_write_top_dict,
+ cairo_cff_font_write_strings,
+ cairo_cff_font_write_global_subrs,
+ cairo_cff_font_write_encoding,
+ cairo_cff_font_write_type1_charset,
+ cairo_cff_font_write_charstrings,
+ cairo_cff_font_write_type1_private_dict_and_local_sub,
+};
+
+static cairo_status_t
+cairo_cff_font_write_subset (cairo_cff_font_t *font)
+{
+ cairo_int_status_t status;
+ unsigned int i;
+
+ if (font->scaled_font_subset->is_latin) {
+ for (i = 0; i < ARRAY_LENGTH (font_write_type1_funcs); i++) {
+ status = font_write_type1_funcs[i] (font);
+ if (unlikely (status))
+ return status;
+ }
+ } else {
+ for (i = 0; i < ARRAY_LENGTH (font_write_cid_funcs); i++) {
+ status = font_write_cid_funcs[i] (font);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_generate (cairo_cff_font_t *font,
+ const char **data,
+ unsigned long *length)
+{
+ cairo_int_status_t status;
+
+ status = cairo_cff_font_read_font (font);
+ if (unlikely (status))
+ return status;
+
+ /* If the PS name is not found, create a CairoFont-x-y name. */
+ if (font->ps_name == NULL) {
+ font->ps_name = malloc (30);
+ if (unlikely (font->ps_name == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ snprintf(font->ps_name, 30, "CairoFont-%u-%u",
+ font->scaled_font_subset->font_id,
+ font->scaled_font_subset->subset_id);
+ }
+
+ status = cairo_cff_font_subset_font (font);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_cff_font_write_subset (font);
+ if (unlikely (status))
+ return status;
+
+
+ *data = _cairo_array_index (&font->output, 0);
+ *length = _cairo_array_num_elements (&font->output);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_create_set_widths (cairo_cff_font_t *font)
+{
+ unsigned long size;
+ unsigned long long_entry_size;
+ unsigned long short_entry_size;
+ unsigned int i;
+ tt_hhea_t hhea;
+ int num_hmetrics;
+ 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 = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ glyph_index = font->scaled_font_subset->glyphs[i];
+ long_entry_size = 2 * sizeof (int16_t);
+ short_entry_size = sizeof (int16_t);
+ if (glyph_index < num_hmetrics) {
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_hmtx,
+ glyph_index * long_entry_size,
+ 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_bool_t
+check_fontdata_is_cff (const unsigned char *data, long length)
+{
+ cff_header_t *header;
+
+ if (length < (long)sizeof (cff_header_t))
+ return FALSE;
+
+ header = (cff_header_t *) data;
+ if (header->major == 1 &&
+ header->minor == 0 &&
+ header->header_size == 4)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_load_opentype_cff (cairo_cff_font_t *font)
+{
+ const cairo_scaled_font_backend_t *backend = font->backend;
+ cairo_status_t status;
+ tt_head_t head;
+ tt_hhea_t hhea;
+ unsigned long size, data_length;
+
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ data_length = 0;
+ status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_CFF, 0, NULL, &data_length);
+ if (status)
+ return status;
+
+ size = sizeof (tt_head_t);
+ status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_head, 0,
+ (unsigned char *) &head, &size);
+ if (unlikely (status))
+ return status;
+
+ size = sizeof (tt_hhea_t);
+ status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_hhea, 0,
+ (unsigned char *) &hhea, &size);
+ if (unlikely (status))
+ return status;
+
+ size = 0;
+ status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_hmtx, 0, NULL, &size);
+ if (unlikely (status))
+ return status;
+
+ font->x_min = (int16_t) be16_to_cpu (head.x_min);
+ font->y_min = (int16_t) be16_to_cpu (head.y_min);
+ font->x_max = (int16_t) be16_to_cpu (head.x_max);
+ font->y_max = (int16_t) be16_to_cpu (head.y_max);
+ font->ascent = (int16_t) be16_to_cpu (hhea.ascender);
+ font->descent = (int16_t) be16_to_cpu (hhea.descender);
+ font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em);
+ if (font->units_per_em == 0)
+ font->units_per_em = 1000;
+
+ font->font_name = NULL;
+ status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font,
+ &font->ps_name,
+ &font->font_name);
+ if (_cairo_status_is_error (status))
+ return status;
+
+ font->is_opentype = TRUE;
+ font->data_length = data_length;
+ font->data = malloc (data_length);
+ if (unlikely (font->data == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_CFF, 0, font->data,
+ &font->data_length);
+ if (unlikely (status))
+ return status;
+
+ if (!check_fontdata_is_cff (font->data, data_length))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_load_cff (cairo_cff_font_t *font)
+{
+ const cairo_scaled_font_backend_t *backend = font->backend;
+ cairo_status_t status;
+ unsigned long data_length;
+
+ if (!backend->load_type1_data)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ data_length = 0;
+ status = backend->load_type1_data (font->scaled_font_subset->scaled_font,
+ 0, NULL, &data_length);
+ if (unlikely (status))
+ return status;
+
+ font->font_name = NULL;
+ font->is_opentype = FALSE;
+ font->data_length = data_length;
+ font->data = malloc (data_length);
+ if (unlikely (font->data == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = font->backend->load_type1_data (font->scaled_font_subset->scaled_font,
+ 0, font->data, &font->data_length);
+ if (unlikely (status))
+ return status;
+
+ if (!check_fontdata_is_cff (font->data, data_length))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_cff_font_t **font_return,
+ const char *subset_name)
+{
+ const cairo_scaled_font_backend_t *backend;
+ cairo_int_status_t status;
+ cairo_cff_font_t *font;
+
+ backend = scaled_font_subset->scaled_font->backend;
+
+ /* We need to use a fallback font generated from the synthesized outlines. */
+ if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ font = calloc (1, sizeof (cairo_cff_font_t));
+ if (unlikely (font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->backend = backend;
+ font->scaled_font_subset = scaled_font_subset;
+
+ status = _cairo_cff_font_load_opentype_cff (font);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ status = _cairo_cff_font_load_cff (font);
+ if (status)
+ goto fail1;
+
+ font->data_end = font->data + font->data_length;
+ _cairo_array_init (&font->output, sizeof (char));
+ status = _cairo_array_grow_by (&font->output, 4096);
+ if (unlikely (status))
+ goto fail2;
+
+ font->subset_font_name = strdup (subset_name);
+ if (unlikely (font->subset_font_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+
+ font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
+ if (unlikely (font->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+
+ if (font->is_opentype) {
+ status = cairo_cff_font_create_set_widths (font);
+ if (unlikely (status))
+ goto fail4;
+ }
+
+ status = cff_dict_init (&font->top_dict);
+ if (unlikely (status))
+ goto fail4;
+
+ status = cff_dict_init (&font->private_dict);
+ if (unlikely (status))
+ goto fail5;
+
+ cff_index_init (&font->strings_index);
+ cff_index_init (&font->charstrings_index);
+ cff_index_init (&font->global_sub_index);
+ cff_index_init (&font->local_sub_index);
+ cff_index_init (&font->charstrings_subset_index);
+ cff_index_init (&font->strings_subset_index);
+ font->euro_sid = 0;
+ font->fdselect = NULL;
+ font->fd_dict = NULL;
+ font->fd_private_dict = NULL;
+ font->fd_local_sub_index = NULL;
+ font->fd_local_sub_bias = NULL;
+ font->fdselect_subset = NULL;
+ font->fd_subset_map = NULL;
+ font->private_dict_offset = NULL;
+ font->global_subs_used = NULL;
+ font->local_subs_used = NULL;
+ font->fd_local_subs_used = NULL;
+
+ *font_return = font;
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail5:
+ _cairo_hash_table_destroy (font->top_dict);
+fail4:
+ free (font->widths);
+fail3:
+ free (font->subset_font_name);
+fail2:
+ free (font->ps_name);
+ _cairo_array_fini (&font->output);
+fail1:
+ free (font->data);
+ free (font->font_name);
+ free (font);
+
+ return status;
+}
+
+static void
+cairo_cff_font_destroy (cairo_cff_font_t *font)
+{
+ unsigned int i;
+
+ free (font->widths);
+ free (font->font_name);
+ free (font->ps_name);
+ free (font->subset_font_name);
+ _cairo_array_fini (&font->output);
+ cff_dict_fini (font->top_dict);
+ cff_dict_fini (font->private_dict);
+ cff_index_fini (&font->strings_index);
+ cff_index_fini (&font->charstrings_index);
+ cff_index_fini (&font->global_sub_index);
+ cff_index_fini (&font->local_sub_index);
+ cff_index_fini (&font->charstrings_subset_index);
+ cff_index_fini (&font->strings_subset_index);
+
+ /* If we bailed out early as a result of an error some of the
+ * following cairo_cff_font_t members may still be NULL */
+ if (font->fd_dict) {
+ for (i = 0; i < font->num_fontdicts; i++) {
+ if (font->fd_dict[i])
+ cff_dict_fini (font->fd_dict[i]);
+ }
+ free (font->fd_dict);
+ }
+ free (font->global_subs_used);
+ free (font->local_subs_used);
+ free (font->fd_subset_map);
+ free (font->private_dict_offset);
+
+ if (font->is_cid) {
+ free (font->fdselect);
+ free (font->fdselect_subset);
+ if (font->fd_private_dict) {
+ for (i = 0; i < font->num_fontdicts; i++) {
+ if (font->fd_private_dict[i])
+ cff_dict_fini (font->fd_private_dict[i]);
+ }
+ free (font->fd_private_dict);
+ }
+ if (font->fd_local_sub_index) {
+ for (i = 0; i < font->num_fontdicts; i++)
+ cff_index_fini (&font->fd_local_sub_index[i]);
+ free (font->fd_local_sub_index);
+ }
+ free (font->fd_local_sub_bias);
+ if (font->fd_local_subs_used) {
+ for (i = 0; i < font->num_fontdicts; i++) {
+ free (font->fd_local_subs_used[i]);
+ }
+ free (font->fd_local_subs_used);
+ }
+ free (font->fd_default_width);
+ free (font->fd_nominal_width);
+ }
+
+ free (font->data);
+
+ free (font);
+}
+
+cairo_status_t
+_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
+ const char *subset_name,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
+ cairo_status_t status;
+ const char *data = NULL; /* squelch bogus compiler warning */
+ unsigned long length = 0; /* squelch bogus compiler warning */
+ unsigned int i;
+
+ status = _cairo_cff_font_create (font_subset, &font, subset_name);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_cff_font_generate (font, &data, &length);
+ if (unlikely (status))
+ goto fail1;
+
+ cff_subset->ps_name = strdup (font->ps_name);
+ if (unlikely (cff_subset->ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ if (font->font_name) {
+ cff_subset->family_name_utf8 = strdup (font->font_name);
+ if (cff_subset->family_name_utf8 == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ } else {
+ cff_subset->family_name_utf8 = NULL;
+ }
+
+ cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
+ if (unlikely (cff_subset->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+ cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em;
+
+ cff_subset->x_min = (double)font->x_min/font->units_per_em;
+ cff_subset->y_min = (double)font->y_min/font->units_per_em;
+ cff_subset->x_max = (double)font->x_max/font->units_per_em;
+ cff_subset->y_max = (double)font->y_max/font->units_per_em;
+ cff_subset->ascent = (double)font->ascent/font->units_per_em;
+ cff_subset->descent = (double)font->descent/font->units_per_em;
+
+ cff_subset->data = malloc (length);
+ if (unlikely (cff_subset->data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail4;
+ }
+
+ memcpy (cff_subset->data, data, length);
+ cff_subset->data_length = length;
+
+ cairo_cff_font_destroy (font);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ fail4:
+ free (cff_subset->widths);
+ fail3:
+ free (cff_subset->family_name_utf8);
+ fail2:
+ free (cff_subset->ps_name);
+ fail1:
+ cairo_cff_font_destroy (font);
+
+ return status;
+}
+
+void
+_cairo_cff_subset_fini (cairo_cff_subset_t *subset)
+{
+ free (subset->ps_name);
+ free (subset->family_name_utf8);
+ free (subset->widths);
+ free (subset->data);
+}
+
+cairo_bool_t
+_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font)
+{
+ const cairo_scaled_font_backend_t *backend;
+ cairo_int_status_t status;
+ unsigned char *data;
+ unsigned long data_length;
+ unsigned char *current_ptr;
+ unsigned char *data_end;
+ cff_header_t *header;
+ cff_index_element_t *element;
+ cairo_hash_table_t *top_dict;
+ cairo_array_t index;
+ int size;
+ cairo_bool_t is_cid = FALSE;
+
+ backend = scaled_font->backend;
+ data = NULL;
+ data_length = 0;
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ /* Try to load an OpenType/CFF font */
+ if (backend->load_truetype_table &&
+ (status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
+ 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS)
+ {
+ data = malloc (data_length);
+ if (unlikely (data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return FALSE;
+ }
+
+ status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
+ 0, data, &data_length);
+ if (unlikely (status))
+ goto fail1;
+ }
+ /* Try to load a CFF font */
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
+ backend->load_type1_data &&
+ (status = backend->load_type1_data (scaled_font,
+ 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS)
+ {
+ data = malloc (data_length);
+ if (unlikely (data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return FALSE;
+ }
+
+ status = backend->load_type1_data (scaled_font, 0, data, &data_length);
+ if (unlikely (status))
+ goto fail1;
+ }
+ if (status)
+ goto fail1;
+
+ /* Check if it looks like a CFF font */
+ if (!check_fontdata_is_cff (data, data_length))
+ goto fail1;
+
+ data_end = data + data_length;
+
+ /* skip header */
+ if (data_length < sizeof (cff_header_t))
+ goto fail1;
+
+ header = (cff_header_t *) data;
+ current_ptr = data + header->header_size;
+
+ /* skip name */
+ cff_index_init (&index);
+ status = cff_index_read (&index, &current_ptr, data_end);
+ cff_index_fini (&index);
+
+ if (status)
+ goto fail1;
+
+ /* read top dict */
+ cff_index_init (&index);
+ status = cff_index_read (&index, &current_ptr, data_end);
+ if (unlikely (status))
+ goto fail2;
+
+ status = cff_dict_init (&top_dict);
+ if (unlikely (status))
+ goto fail2;
+
+ element = _cairo_array_index (&index, 0);
+ if (element == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ goto fail3;
+ }
+
+ status = cff_dict_read (top_dict, element->data, element->length);
+ if (unlikely (status))
+ goto fail3;
+
+ /* check for ROS operator indicating a CID font */
+ if (cff_dict_get_operands (top_dict, ROS_OP, &size) != NULL)
+ is_cid = TRUE;
+
+fail3:
+ cff_dict_fini (top_dict);
+
+fail2:
+ cff_index_fini (&index);
+
+fail1:
+ free (data);
+
+ return is_cid;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_cff_font_t **font_return,
+ const char *subset_name)
+{
+ cairo_status_t status;
+ cairo_cff_font_t *font;
+
+ font = malloc (sizeof (cairo_cff_font_t));
+ if (unlikely (font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->backend = NULL;
+ font->scaled_font_subset = scaled_font_subset;
+
+ _cairo_array_init (&font->output, sizeof (char));
+ status = _cairo_array_grow_by (&font->output, 4096);
+ if (unlikely (status))
+ goto fail1;
+
+ font->subset_font_name = strdup (subset_name);
+ if (unlikely (font->subset_font_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ font->ps_name = strdup (subset_name);
+ if (unlikely (font->ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ font->font_name = NULL;
+
+ font->x_min = 0;
+ font->y_min = 0;
+ font->x_max = 0;
+ font->y_max = 0;
+ font->ascent = 0;
+ font->descent = 0;
+
+ font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
+ if (unlikely (font->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+
+ font->data_length = 0;
+ font->data = NULL;
+ font->data_end = NULL;
+
+ status = cff_dict_init (&font->top_dict);
+ if (unlikely (status))
+ goto fail4;
+
+ status = cff_dict_init (&font->private_dict);
+ if (unlikely (status))
+ goto fail5;
+
+ cff_index_init (&font->strings_index);
+ cff_index_init (&font->charstrings_index);
+ cff_index_init (&font->global_sub_index);
+ cff_index_init (&font->local_sub_index);
+ cff_index_init (&font->charstrings_subset_index);
+ cff_index_init (&font->strings_subset_index);
+ font->global_subs_used = NULL;
+ font->local_subs_used = NULL;
+ font->subset_subroutines = FALSE;
+ font->fdselect = NULL;
+ font->fd_dict = NULL;
+ font->fd_private_dict = NULL;
+ font->fd_local_sub_index = NULL;
+ font->fdselect_subset = NULL;
+ font->fd_subset_map = NULL;
+ font->private_dict_offset = NULL;
+
+ *font_return = font;
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail5:
+ _cairo_hash_table_destroy (font->top_dict);
+fail4:
+ free (font->widths);
+fail3:
+ free (font->font_name);
+ free (font->ps_name);
+fail2:
+ free (font->subset_font_name);
+fail1:
+ _cairo_array_fini (&font->output);
+ free (font);
+ return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_fallback_generate (cairo_cff_font_t *font,
+ cairo_type2_charstrings_t *type2_subset,
+ const char **data,
+ unsigned long *length)
+{
+ cairo_int_status_t status;
+ cff_header_t header;
+ cairo_array_t *charstring;
+ unsigned char buf[40];
+ unsigned char *end_buf, *end_buf2;
+ unsigned int i;
+ int sid;
+
+ /* Create header */
+ header.major = 1;
+ header.minor = 0;
+ header.header_size = 4;
+ header.offset_size = 4;
+ font->header = &header;
+
+ /* Create Top Dict */
+ font->is_cid = FALSE;
+
+ snprintf((char*)buf, sizeof(buf), "CairoFont-%u-%u",
+ font->scaled_font_subset->font_id,
+ font->scaled_font_subset->subset_id);
+ sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+ status = cff_index_append_copy (&font->strings_subset_index,
+ (unsigned char *)buf,
+ strlen((char*)buf));
+ if (unlikely (status))
+ return status;
+
+ end_buf = encode_integer (buf, sid);
+ status = cff_dict_set_operands (font->top_dict, FULLNAME_OP,
+ buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ status = cff_dict_set_operands (font->top_dict, FAMILYNAME_OP,
+ buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ end_buf = encode_integer (buf, type2_subset->x_min);
+ end_buf = encode_integer (end_buf, type2_subset->y_min);
+ end_buf = encode_integer (end_buf, type2_subset->x_max);
+ end_buf = encode_integer (end_buf, type2_subset->y_max);
+ status = cff_dict_set_operands (font->top_dict,
+ FONTBBOX_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ end_buf = encode_integer_max (buf, 0);
+ status = cff_dict_set_operands (font->top_dict,
+ CHARSTRINGS_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+
+ if (font->scaled_font_subset->is_latin) {
+ status = cff_dict_set_operands (font->top_dict,
+ ENCODING_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ /* Private has two operands - size and offset */
+ end_buf2 = encode_integer_max (end_buf, 0);
+ cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf2 - buf);
+
+ } else {
+ status = cff_dict_set_operands (font->top_dict,
+ FDSELECT_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ status = cff_dict_set_operands (font->top_dict,
+ FDARRAY_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cff_dict_set_operands (font->top_dict,
+ CHARSET_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ if (!font->scaled_font_subset->is_latin) {
+ status = cairo_cff_font_set_ros_strings (font);
+ if (unlikely (status))
+ return status;
+
+ /* Create CID FD dictionary */
+ status = cairo_cff_font_create_cid_fontdict (font);
+ if (unlikely (status))
+ return status;
+ } else {
+ font->private_dict_offset = malloc (sizeof (int));
+ if (unlikely (font->private_dict_offset == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* Create charstrings */
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ charstring = _cairo_array_index(&type2_subset->charstrings, i);
+ if (charstring == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ status = cff_index_append (&font->charstrings_subset_index,
+ _cairo_array_index (charstring, 0),
+ _cairo_array_num_elements (charstring));
+
+ if (unlikely (status))
+ return status;
+ }
+
+ if (font->scaled_font_subset->is_latin)
+ status = cairo_cff_font_add_euro_charset_string (font);
+
+ status = cairo_cff_font_write_subset (font);
+ if (unlikely (status))
+ return status;
+
+ *data = _cairo_array_index (&font->output, 0);
+ *length = _cairo_array_num_elements (&font->output);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
+ const char *subset_name,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
+ cairo_status_t status;
+ const char *data = NULL; /* squelch bogus compiler warning */
+ unsigned long length = 0; /* squelch bogus compiler warning */
+ unsigned int i;
+ cairo_type2_charstrings_t type2_subset;
+
+ status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_type2_charstrings_init (&type2_subset, font_subset);
+ if (unlikely (status))
+ goto fail1;
+
+ status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length);
+ if (unlikely (status))
+ goto fail2;
+
+ cff_subset->family_name_utf8 = NULL;
+ cff_subset->ps_name = strdup (font->ps_name);
+ if (unlikely (cff_subset->ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+
+ cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
+ if (unlikely (cff_subset->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+ cff_subset->widths[i] = (double)type2_subset.widths[i]/1000;
+
+ cff_subset->x_min = (double)type2_subset.x_min/1000;
+ cff_subset->y_min = (double)type2_subset.y_min/1000;
+ cff_subset->x_max = (double)type2_subset.x_max/1000;
+ cff_subset->y_max = (double)type2_subset.y_max/1000;
+ cff_subset->ascent = (double)type2_subset.y_max/1000;
+ cff_subset->descent = (double)type2_subset.y_min/1000;
+
+ cff_subset->data = malloc (length);
+ if (unlikely (cff_subset->data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail4;
+ }
+
+ memcpy (cff_subset->data, data, length);
+ cff_subset->data_length = length;
+
+ _cairo_type2_charstrings_fini (&type2_subset);
+ cairo_cff_font_destroy (font);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ fail4:
+ _cairo_type2_charstrings_fini (&type2_subset);
+ free (cff_subset->widths);
+ fail3:
+ _cairo_type2_charstrings_fini (&type2_subset);
+ 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/src/cairo-clip-boxes.c b/src/cairo-clip-boxes.c
new file mode 100755
index 000000000..eae0919af
--- /dev/null
+++ b/src/cairo-clip-boxes.c
@@ -0,0 +1,597 @@
+/* -*- 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-box-inline.h"
+#include "cairo-clip-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static inline int
+pot (int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+static cairo_bool_t
+_cairo_clip_contains_rectangle_box (const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rect,
+ const cairo_box_t *box)
+{
+ int i;
+
+ /* clip == NULL means no clip, so the clip contains everything */
+ if (clip == NULL)
+ return TRUE;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return FALSE;
+
+ /* If we have a non-trivial path, just say no */
+ if (clip->path)
+ return FALSE;
+
+ if (! _cairo_rectangle_contains_rectangle (&clip->extents, rect))
+ return FALSE;
+
+ if (clip->num_boxes == 0)
+ return TRUE;
+
+ /* Check for a clip-box that wholly contains the rectangle */
+ for (i = 0; i < clip->num_boxes; i++) {
+ if (box->p1.x >= clip->boxes[i].p1.x &&
+ box->p1.y >= clip->boxes[i].p1.y &&
+ box->p2.x <= clip->boxes[i].p2.x &&
+ box->p2.y <= clip->boxes[i].p2.y)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+cairo_bool_t
+_cairo_clip_contains_box (const cairo_clip_t *clip,
+ const cairo_box_t *box)
+{
+ cairo_rectangle_int_t rect;
+
+ _cairo_box_round_to_rectangle (box, &rect);
+ return _cairo_clip_contains_rectangle_box(clip, &rect, box);
+}
+
+cairo_bool_t
+_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rect)
+{
+ cairo_box_t box;
+
+ 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 _cairo_clip_contains_rectangle_box (clip, rect, &box);
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias)
+{
+ cairo_status_t status;
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS && boxes.num_boxes))
+ clip = _cairo_clip_intersect_boxes (clip, &boxes);
+ else
+ clip = _cairo_clip_set_all_clipped (clip);
+ _cairo_boxes_fini (&boxes);
+
+ return clip;
+}
+
+static cairo_clip_t *
+_cairo_clip_intersect_rectangle_box (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *r,
+ const cairo_box_t *box)
+{
+ cairo_box_t extents_box;
+ cairo_bool_t changed = FALSE;
+ int i, j;
+
+ if (clip == NULL) {
+ clip = _cairo_clip_create ();
+ if (clip == NULL)
+ return _cairo_clip_set_all_clipped (clip);
+ }
+
+ if (clip->num_boxes == 0) {
+ clip->boxes = &clip->embedded_box;
+ clip->boxes[0] = *box;
+ clip->num_boxes = 1;
+ if (clip->path == NULL) {
+ clip->extents = *r;
+ } else {
+ if (! _cairo_rectangle_intersect (&clip->extents, r))
+ clip = _cairo_clip_set_all_clipped (clip);
+ }
+ if (clip->path == NULL)
+ clip->is_region = _cairo_box_is_pixel_aligned (box);
+ return clip;
+ }
+
+ /* Does the new box wholly subsume the clip? Perform a cheap check
+ * for the common condition of a single clip rectangle.
+ */
+ if (clip->num_boxes == 1 &&
+ clip->boxes[0].p1.x >= box->p1.x &&
+ clip->boxes[0].p1.y >= box->p1.y &&
+ clip->boxes[0].p2.x <= box->p2.x &&
+ clip->boxes[0].p2.y <= box->p2.y)
+ {
+ return clip;
+ }
+
+ for (i = j = 0; i < clip->num_boxes; i++) {
+ cairo_box_t *b = &clip->boxes[j];
+
+ if (j != i)
+ *b = clip->boxes[i];
+
+ if (box->p1.x > b->p1.x)
+ b->p1.x = box->p1.x, changed = TRUE;
+ if (box->p2.x < b->p2.x)
+ b->p2.x = box->p2.x, changed = TRUE;
+
+ if (box->p1.y > b->p1.y)
+ b->p1.y = box->p1.y, changed = TRUE;
+ if (box->p2.y < b->p2.y)
+ b->p2.y = box->p2.y, changed = TRUE;
+
+ j += b->p2.x > b->p1.x && b->p2.y > b->p1.y;
+ }
+ clip->num_boxes = j;
+
+ if (clip->num_boxes == 0)
+ return _cairo_clip_set_all_clipped (clip);
+
+ if (! changed)
+ return clip;
+
+ extents_box = clip->boxes[0];
+ for (i = 1; i < clip->num_boxes; i++) {
+ if (clip->boxes[i].p1.x < extents_box.p1.x)
+ extents_box.p1.x = clip->boxes[i].p1.x;
+
+ if (clip->boxes[i].p1.y < extents_box.p1.y)
+ extents_box.p1.y = clip->boxes[i].p1.y;
+
+ if (clip->boxes[i].p2.x > extents_box.p2.x)
+ extents_box.p2.x = clip->boxes[i].p2.x;
+
+ if (clip->boxes[i].p2.y > extents_box.p2.y)
+ extents_box.p2.y = clip->boxes[i].p2.y;
+ }
+
+ if (clip->path == NULL) {
+ _cairo_box_round_to_rectangle (&extents_box, &clip->extents);
+ } else {
+ cairo_rectangle_int_t extents_rect;
+
+ _cairo_box_round_to_rectangle (&extents_box, &extents_rect);
+ if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect))
+ return _cairo_clip_set_all_clipped (clip);
+ }
+
+ if (clip->region) {
+ cairo_region_destroy (clip->region);
+ clip->region = NULL;
+ }
+
+ clip->is_region = FALSE;
+ return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_box (cairo_clip_t *clip,
+ const cairo_box_t *box)
+{
+ cairo_rectangle_int_t r;
+
+ _cairo_box_round_to_rectangle (box, &r);
+ if (r.width == 0 || r.height == 0)
+ return _cairo_clip_set_all_clipped (clip);
+
+ return _cairo_clip_intersect_rectangle_box (clip, &r, box);
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_boxes (cairo_clip_t *clip,
+ const cairo_boxes_t *boxes)
+{
+ cairo_boxes_t clip_boxes;
+ cairo_box_t limits;
+ cairo_rectangle_int_t extents;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ if (boxes->num_boxes == 0)
+ return _cairo_clip_set_all_clipped (clip);
+
+ if (boxes->num_boxes == 1)
+ return _cairo_clip_intersect_box (clip, boxes->chunks.base);
+
+ if (clip == NULL)
+ clip = _cairo_clip_create ();
+
+ if (clip == NULL)
+ return NULL;
+
+ if (clip->num_boxes) {
+ _cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes);
+ if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) {
+ clip = _cairo_clip_set_all_clipped (clip);
+ goto out;
+ }
+
+ if (clip->boxes != &clip->embedded_box)
+ free (clip->boxes);
+
+ clip->boxes = NULL;
+ boxes = &clip_boxes;
+ }
+
+ if (boxes->num_boxes == 0) {
+ clip = _cairo_clip_set_all_clipped (clip);
+ goto out;
+ } else if (boxes->num_boxes == 1) {
+ clip->boxes = &clip->embedded_box;
+ clip->boxes[0] = boxes->chunks.base[0];
+ clip->num_boxes = 1;
+ } else {
+ clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
+ }
+ _cairo_boxes_extents (boxes, &limits);
+
+ _cairo_box_round_to_rectangle (&limits, &extents);
+ if (clip->path == NULL)
+ clip->extents = extents;
+ else if (! _cairo_rectangle_intersect (&clip->extents, &extents))
+ clip = _cairo_clip_set_all_clipped (clip);
+
+ if (clip->region) {
+ cairo_region_destroy (clip->region);
+ clip->region = NULL;
+ }
+ clip->is_region = FALSE;
+
+out:
+ if (boxes == &clip_boxes)
+ _cairo_boxes_fini (&clip_boxes);
+
+ return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *r)
+{
+ cairo_box_t box;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ if (r->width == 0 || r->height == 0)
+ return _cairo_clip_set_all_clipped (clip);
+
+ box.p1.x = _cairo_fixed_from_int (r->x);
+ box.p1.y = _cairo_fixed_from_int (r->y);
+ box.p2.x = _cairo_fixed_from_int (r->x + r->width);
+ box.p2.y = _cairo_fixed_from_int (r->y + r->height);
+
+ return _cairo_clip_intersect_rectangle_box (clip, r, &box);
+}
+
+struct reduce {
+ cairo_clip_t *clip;
+ cairo_box_t limit;
+ cairo_box_t extents;
+ cairo_bool_t inside;
+
+ cairo_point_t current_point;
+ cairo_point_t last_move_to;
+};
+
+static void
+_add_clipped_edge (struct reduce *r,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ int y1, int y2)
+{
+ cairo_fixed_t x;
+
+ x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y1);
+ if (x < r->extents.p1.x)
+ r->extents.p1.x = x;
+
+ x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y2);
+ if (x > r->extents.p2.x)
+ r->extents.p2.x = x;
+
+ if (y1 < r->extents.p1.y)
+ r->extents.p1.y = y1;
+
+ if (y2 > r->extents.p2.y)
+ r->extents.p2.y = y2;
+
+ r->inside = TRUE;
+}
+
+static void
+_add_edge (struct reduce *r,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ int top, bottom;
+ int top_y, bot_y;
+ int n;
+
+ if (p1->y < p2->y) {
+ top = p1->y;
+ bottom = p2->y;
+ } else {
+ top = p2->y;
+ bottom = p1->y;
+ }
+
+ if (bottom < r->limit.p1.y || top > r->limit.p2.y)
+ return;
+
+ if (p1->x > p2->x) {
+ const cairo_point_t *t = p1;
+ p1 = p2;
+ p2 = t;
+ }
+
+ if (p2->x <= r->limit.p1.x || p1->x >= r->limit.p2.x)
+ return;
+
+ for (n = 0; n < r->clip->num_boxes; n++) {
+ const cairo_box_t *limits = &r->clip->boxes[n];
+
+ if (bottom < limits->p1.y || top > limits->p2.y)
+ continue;
+
+ if (p2->x <= limits->p1.x || p1->x >= limits->p2.x)
+ continue;
+
+ if (p1->x >= limits->p1.x && p2->x <= limits->p1.x) {
+ top_y = top;
+ bot_y = bottom;
+ } else {
+ int p1_y, p2_y;
+
+ p1_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p1.x);
+ p2_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p2.x);
+ if (p1_y < p2_y) {
+ top_y = p1_y;
+ bot_y = p2_y;
+ } else {
+ top_y = p2_y;
+ bot_y = p1_y;
+ }
+
+ if (top_y < top)
+ top_y = top;
+ if (bot_y > bottom)
+ bot_y = bottom;
+ }
+
+ if (top_y < limits->p1.y)
+ top_y = limits->p1.y;
+
+ if (bot_y > limits->p2.y)
+ bot_y = limits->p2.y;
+ if (bot_y > top_y)
+ _add_clipped_edge (r, p1, p2, top_y, bot_y);
+ }
+}
+
+static cairo_status_t
+_reduce_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct reduce *r = closure;
+
+ _add_edge (r, &r->current_point, point);
+ r->current_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_reduce_close (void *closure)
+{
+ struct reduce *r = closure;
+
+ return _reduce_line_to (r, &r->last_move_to);
+}
+
+static cairo_status_t
+_reduce_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct reduce *r = closure;
+ cairo_status_t status;
+
+ /* close current subpath */
+ status = _reduce_close (closure);
+
+ /* make sure that the closure represents a degenerate path */
+ r->current_point = *point;
+ r->last_move_to = *point;
+
+ return status;
+}
+
+static cairo_clip_t *
+_cairo_clip_reduce_to_boxes (cairo_clip_t *clip)
+{
+ struct reduce r;
+ cairo_clip_path_t *clip_path;
+ cairo_status_t status;
+
+ return clip;
+ if (clip->path == NULL)
+ return clip;
+
+ r.clip = clip;
+ r.extents.p1.x = r.extents.p1.y = INT_MAX;
+ r.extents.p2.x = r.extents.p2.y = INT_MIN;
+ r.inside = FALSE;
+
+ r.limit.p1.x = _cairo_fixed_from_int (clip->extents.x);
+ r.limit.p1.y = _cairo_fixed_from_int (clip->extents.y);
+ r.limit.p2.x = _cairo_fixed_from_int (clip->extents.x + clip->extents.width);
+ r.limit.p2.y = _cairo_fixed_from_int (clip->extents.y + clip->extents.height);
+
+ clip_path = clip->path;
+ do {
+ r.current_point.x = 0;
+ r.current_point.y = 0;
+ r.last_move_to = r.current_point;
+
+ status = _cairo_path_fixed_interpret_flat (&clip_path->path,
+ _reduce_move_to,
+ _reduce_line_to,
+ _reduce_close,
+ &r,
+ clip_path->tolerance);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ _reduce_close (&r);
+ } while ((clip_path = clip_path->prev));
+
+ if (! r.inside) {
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ }
+
+ return _cairo_clip_intersect_box (clip, &r.extents);
+}
+
+cairo_clip_t *
+_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *r)
+{
+ cairo_clip_t *copy;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return (cairo_clip_t *) clip;
+
+ if (_cairo_clip_contains_rectangle (clip, r))
+ return _cairo_clip_intersect_rectangle (NULL, r);
+
+ copy = _cairo_clip_copy_intersect_rectangle (clip, r);
+ if (_cairo_clip_is_all_clipped (copy))
+ return copy;
+
+ return _cairo_clip_reduce_to_boxes (copy);
+}
+
+cairo_clip_t *
+_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
+ cairo_composite_rectangles_t *extents)
+{
+ const cairo_rectangle_int_t *r;
+
+ r = extents->is_bounded ? &extents->bounded : &extents->unbounded;
+ return _cairo_clip_reduce_to_rectangle (clip, r);
+}
+
+cairo_clip_t *
+_cairo_clip_from_boxes (const cairo_boxes_t *boxes)
+{
+ cairo_box_t extents;
+ cairo_clip_t *clip = _cairo_clip_create ();
+ if (clip == NULL)
+ return _cairo_clip_set_all_clipped (clip);
+
+ /* XXX cow-boxes? */
+ if(boxes->num_boxes == 1) {
+ clip->boxes = &clip->embedded_box;
+ clip->boxes[0] = boxes->chunks.base[0];
+ clip->num_boxes = 1;
+ } else {
+ clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
+ if (clip->boxes == NULL)
+ return _cairo_clip_set_all_clipped (clip);
+ }
+
+ _cairo_boxes_extents (boxes, &extents);
+ _cairo_box_round_to_rectangle (&extents, &clip->extents);
+
+ return clip;
+}
diff --git a/src/cairo-clip-inline.h b/src/cairo-clip-inline.h
new file mode 100755
index 000000000..a9f232692
--- /dev/null
+++ b/src/cairo-clip-inline.h
@@ -0,0 +1,83 @@
+/* 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>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CLIP_INLINE_H
+#define CAIRO_CLIP_INLINE_H
+
+#include "cairo-clip-private.h"
+
+static inline cairo_bool_t _cairo_clip_is_all_clipped(const cairo_clip_t *clip)
+{
+ return clip == &__cairo_clip_all;
+}
+
+static inline cairo_clip_t *
+_cairo_clip_set_all_clipped (cairo_clip_t *clip)
+{
+ _cairo_clip_destroy (clip);
+ return (cairo_clip_t *) &__cairo_clip_all;
+}
+
+static inline cairo_clip_t *
+_cairo_clip_copy_intersect_rectangle (const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *r)
+{
+ return _cairo_clip_intersect_rectangle (_cairo_clip_copy (clip), r);
+}
+
+static inline cairo_clip_t *
+_cairo_clip_copy_intersect_clip (const cairo_clip_t *clip,
+ const cairo_clip_t *other)
+{
+ return _cairo_clip_intersect_clip (_cairo_clip_copy (clip), other);
+}
+
+static inline void
+_cairo_clip_steal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes)
+{
+ _cairo_boxes_init_for_array (boxes, clip->boxes, clip->num_boxes);
+ clip->boxes = NULL;
+ clip->num_boxes = 0;
+}
+
+static inline void
+_cairo_clip_unsteal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes)
+{
+ clip->boxes = boxes->chunks.base;
+ clip->num_boxes = boxes->num_boxes;
+}
+
+#endif /* CAIRO_CLIP_INLINE_H */
diff --git a/src/cairo-clip-polygon.c b/src/cairo-clip-polygon.c
new file mode 100755
index 000000000..f40faefba
--- /dev/null
+++ b/src/cairo-clip-polygon.c
@@ -0,0 +1,156 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-clip-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static cairo_bool_t
+can_convert_to_polygon (const cairo_clip_t *clip)
+{
+ cairo_clip_path_t *clip_path = clip->path;
+ cairo_antialias_t antialias = clip_path->antialias;
+
+ while ((clip_path = clip_path->prev) != NULL) {
+ if (clip_path->antialias != antialias)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+cairo_int_status_t
+_cairo_clip_get_polygon (const cairo_clip_t *clip,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t *fill_rule,
+ cairo_antialias_t *antialias)
+{
+ cairo_status_t status;
+ cairo_clip_path_t *clip_path;
+
+ if (_cairo_clip_is_all_clipped (clip)) {
+ _cairo_polygon_init (polygon, NULL, 0);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ /* If there is no clip, we need an infinite polygon */
+ assert (clip && (clip->path || clip->num_boxes));
+
+ if (clip->path == NULL) {
+ *fill_rule = CAIRO_FILL_RULE_WINDING;
+ *antialias = CAIRO_ANTIALIAS_DEFAULT;
+ return _cairo_polygon_init_box_array (polygon,
+ clip->boxes,
+ clip->num_boxes);
+ }
+
+ /* check that residual is all of the same type/tolerance */
+ if (! can_convert_to_polygon (clip))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (clip->num_boxes < 2)
+ _cairo_polygon_init_with_clip (polygon, clip);
+ else
+ _cairo_polygon_init_with_clip (polygon, NULL);
+
+ clip_path = clip->path;
+ *fill_rule = clip_path->fill_rule;
+ *antialias = clip_path->antialias;
+
+ status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+ clip_path->tolerance,
+ polygon);
+ if (unlikely (status))
+ goto err;
+
+ if (clip->num_boxes > 1) {
+ status = _cairo_polygon_intersect_with_boxes (polygon, fill_rule,
+ clip->boxes, clip->num_boxes);
+ if (unlikely (status))
+ goto err;
+ }
+
+ polygon->limits = NULL;
+ polygon->num_limits = 0;
+
+ while ((clip_path = clip_path->prev) != NULL) {
+ cairo_polygon_t next;
+
+ _cairo_polygon_init (&next, NULL, 0);
+ status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+ clip_path->tolerance,
+ &next);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = _cairo_polygon_intersect (polygon, *fill_rule,
+ &next, clip_path->fill_rule);
+ _cairo_polygon_fini (&next);
+ if (unlikely (status))
+ goto err;
+
+ *fill_rule = CAIRO_FILL_RULE_WINDING;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+err:
+ _cairo_polygon_fini (polygon);
+ return status;
+}
+
+cairo_bool_t
+_cairo_clip_is_polygon (const cairo_clip_t *clip)
+{
+ if (_cairo_clip_is_all_clipped (clip))
+ return TRUE;
+
+ /* If there is no clip, we need an infinite polygon */
+ if (clip == NULL)
+ return FALSE;
+
+ if (clip->path == NULL)
+ return TRUE;
+
+ /* check that residual is all of the same type/tolerance */
+ return can_convert_to_polygon (clip);
+}
diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
new file mode 100755
index 000000000..c2ed66419
--- /dev/null
+++ b/src/cairo-clip-private.h
@@ -0,0 +1,201 @@
+/* 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>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CLIP_PRIVATE_H
+#define CAIRO_CLIP_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-reference-count-private.h"
+
+extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil;
+
+struct _cairo_clip_path {
+ cairo_reference_count_t ref_count;
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ double tolerance;
+ cairo_antialias_t antialias;
+ cairo_clip_path_t *prev;
+};
+
+struct _cairo_clip {
+ cairo_rectangle_int_t extents;
+ cairo_clip_path_t *path;
+
+ cairo_box_t *boxes;
+ int num_boxes;
+
+ cairo_region_t *region;
+ cairo_bool_t is_region;
+
+ cairo_box_t embedded_box;
+};
+
+cairo_private cairo_clip_t *
+_cairo_clip_create (void);
+
+cairo_private cairo_clip_path_t *
+_cairo_clip_path_reference (cairo_clip_path_t *clip_path);
+
+cairo_private void
+_cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
+
+cairo_private void
+_cairo_clip_destroy (cairo_clip_t *clip);
+
+cairo_private extern const cairo_clip_t __cairo_clip_all;
+
+cairo_private cairo_clip_t *
+_cairo_clip_copy (const cairo_clip_t *clip);
+
+cairo_private cairo_clip_t *
+_cairo_clip_copy_region (const cairo_clip_t *clip);
+
+cairo_private cairo_clip_t *
+_cairo_clip_copy_path (const cairo_clip_t *clip);
+
+cairo_private cairo_clip_t *
+_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty);
+
+cairo_private cairo_clip_t *
+_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m);
+
+cairo_private cairo_clip_t *
+_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty);
+
+cairo_private cairo_bool_t
+_cairo_clip_equal (const cairo_clip_t *clip_a,
+ const cairo_clip_t *clip_b);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rectangle);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_clip (cairo_clip_t *clip,
+ const cairo_clip_t *other);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_box (cairo_clip_t *clip,
+ const cairo_box_t *box);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_boxes (cairo_clip_t *clip,
+ const cairo_boxes_t *boxes);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_path (cairo_clip_t *clip,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias);
+
+cairo_private const cairo_rectangle_int_t *
+_cairo_clip_get_extents (const cairo_clip_t *clip);
+
+cairo_private const cairo_rectangle_t *
+_cairo_clip_get_exact_extents (const cairo_clip_t *clip);
+
+cairo_private cairo_surface_t *
+_cairo_clip_get_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty);
+
+cairo_private cairo_surface_t *
+_cairo_clip_get_image (const cairo_clip_t *clip,
+ cairo_surface_t *target,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_clip_combine_with_surface (const cairo_clip_t *clip,
+ cairo_surface_t *dst,
+ int dst_x, int dst_y);
+
+cairo_private cairo_clip_t *
+_cairo_clip_from_boxes (const cairo_boxes_t *boxes);
+
+cairo_private cairo_region_t *
+_cairo_clip_get_region (const cairo_clip_t *clip);
+
+cairo_private cairo_bool_t
+_cairo_clip_is_region (const cairo_clip_t *clip);
+
+cairo_private cairo_clip_t *
+_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *r);
+
+cairo_private cairo_clip_t *
+_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
+ cairo_composite_rectangles_t *extents);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rect);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_box (const cairo_clip_t *clip,
+ const cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_extents (const cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents);
+
+cairo_private cairo_rectangle_list_t*
+_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate);
+
+cairo_private cairo_rectangle_list_t *
+_cairo_rectangle_list_create_in_error (cairo_status_t status);
+
+cairo_private cairo_bool_t
+_cairo_clip_is_polygon (const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_clip_get_polygon (const cairo_clip_t *clip,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t *fill_rule,
+ cairo_antialias_t *antialias);
+
+#endif /* CAIRO_CLIP_PRIVATE_H */
diff --git a/src/cairo-clip-region.c b/src/cairo-clip-region.c
new file mode 100755
index 000000000..e3f4891e3
--- /dev/null
+++ b/src/cairo-clip-region.c
@@ -0,0 +1,123 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static void
+_cairo_clip_extract_region (cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+ cairo_rectangle_int_t *r = stack_rects;
+ cairo_bool_t is_region;
+ int i;
+
+ if (clip->num_boxes == 0)
+ return;
+
+ if (clip->num_boxes > ARRAY_LENGTH (stack_rects)) {
+ r = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_rectangle_int_t));
+ if (r == NULL){
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ is_region = clip->path == NULL;
+ for (i = 0; i < clip->num_boxes; i++) {
+ cairo_box_t *b = &clip->boxes[i];
+ if (is_region)
+ is_region =
+ _cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y);
+ r[i].x = _cairo_fixed_integer_floor (b->p1.x);
+ r[i].y = _cairo_fixed_integer_floor (b->p1.y);
+ r[i].width = _cairo_fixed_integer_ceil (b->p2.x) - r[i].x;
+ r[i].height = _cairo_fixed_integer_ceil (b->p2.y) - r[i].y;
+ }
+ clip->is_region = is_region;
+
+ clip->region = cairo_region_create_rectangles (r, i);
+
+ if (r != stack_rects)
+ free (r);
+}
+
+cairo_region_t *
+_cairo_clip_get_region (const cairo_clip_t *clip)
+{
+ if (clip == NULL)
+ return NULL;
+
+ if (clip->region == NULL)
+ _cairo_clip_extract_region ((cairo_clip_t *) clip);
+
+ return clip->region;
+}
+
+cairo_bool_t
+_cairo_clip_is_region (const cairo_clip_t *clip)
+{
+ if (clip == NULL)
+ return TRUE;
+
+ if (clip->is_region)
+ return TRUE;
+
+ /* XXX Geometric reduction? */
+
+ if (clip->path)
+ return FALSE;
+
+ if (clip->num_boxes == 0)
+ return TRUE;
+
+ if (clip->region == NULL)
+ _cairo_clip_extract_region ((cairo_clip_t *) clip);
+
+ return clip->is_region;
+}
diff --git a/src/cairo-clip-surface.c b/src/cairo-clip-surface.c
new file mode 100755
index 000000000..f1c13a4ae
--- /dev/null
+++ b/src/cairo-clip-surface.c
@@ -0,0 +1,247 @@
+/* -*- 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-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+cairo_status_t
+_cairo_clip_combine_with_surface (const cairo_clip_t *clip,
+ cairo_surface_t *dst,
+ int dst_x, int dst_y)
+{
+ cairo_clip_path_t *copy_path;
+ cairo_clip_path_t *clip_path;
+ cairo_clip_t *copy;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ copy = _cairo_clip_copy_with_translation (clip, -dst_x, -dst_y);
+ if (copy == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ copy_path = copy->path;
+ copy->path = NULL;
+
+ if (copy->boxes) {
+ status = _cairo_surface_paint (dst,
+ CAIRO_OPERATOR_IN,
+ &_cairo_pattern_white.base,
+ copy);
+ }
+
+ clip = NULL;
+ if (_cairo_clip_is_region (copy))
+ clip = copy;
+ clip_path = copy_path;
+ while (status == CAIRO_STATUS_SUCCESS && clip_path) {
+ status = _cairo_surface_fill (dst,
+ CAIRO_OPERATOR_IN,
+ &_cairo_pattern_white.base,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ clip);
+ clip_path = clip_path->prev;
+ }
+
+ copy->path = copy_path;
+ _cairo_clip_destroy (copy);
+ return status;
+}
+
+static cairo_status_t
+_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
+ const cairo_box_t *box,
+ cairo_fixed_t fx,
+ cairo_fixed_t fy)
+{
+ cairo_status_t status;
+
+ status = _cairo_path_fixed_move_to (path, box->p1.x + fx, box->p1.y + fy);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p1.y + fy);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p2.y + fy);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p1.x + fx, box->p2.y + fy);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_path_fixed_close_path (path);
+}
+
+cairo_surface_t *
+_cairo_clip_get_surface (const cairo_clip_t *clip,
+ cairo_surface_t *target,
+ int *tx, int *ty)
+{
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ cairo_clip_t *copy, *region;
+ cairo_clip_path_t *copy_path, *clip_path;
+
+ if (clip->num_boxes) {
+ cairo_path_fixed_t path;
+ int i;
+
+ surface = _cairo_surface_create_similar_solid (target,
+ CAIRO_CONTENT_ALPHA,
+ clip->extents.width,
+ clip->extents.height,
+ CAIRO_COLOR_TRANSPARENT);
+ if (unlikely (surface->status))
+ return surface;
+
+ _cairo_path_fixed_init (&path);
+ status = CAIRO_STATUS_SUCCESS;
+ for (i = 0; status == CAIRO_STATUS_SUCCESS && i < clip->num_boxes; i++) {
+ status = _cairo_path_fixed_add_box (&path, &clip->boxes[i],
+ -_cairo_fixed_from_int (clip->extents.x),
+ -_cairo_fixed_from_int (clip->extents.y));
+ }
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_surface_fill (surface,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ &path,
+ CAIRO_FILL_RULE_WINDING,
+ 1.,
+ CAIRO_ANTIALIAS_DEFAULT,
+ NULL);
+ _cairo_path_fixed_fini (&path);
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+ } else {
+ surface = _cairo_surface_create_similar_solid (target,
+ CAIRO_CONTENT_ALPHA,
+ clip->extents.width,
+ clip->extents.height,
+ CAIRO_COLOR_WHITE);
+ if (unlikely (surface->status))
+ return surface;
+ }
+
+ copy = _cairo_clip_copy_with_translation (clip,
+ -clip->extents.x,
+ -clip->extents.y);
+ if (copy == NULL) {
+ cairo_surface_destroy (surface);
+ status = CAIRO_STATUS_NULL_POINTER;
+ return _cairo_surface_create_in_error (status);
+ }
+ copy_path = copy->path;
+ copy->path = NULL;
+
+ region = copy;
+ if (! _cairo_clip_is_region (copy))
+ region = _cairo_clip_copy_region (copy);
+
+ status = CAIRO_STATUS_SUCCESS;
+ clip_path = copy_path;
+ while (status == CAIRO_STATUS_SUCCESS && clip_path) {
+ status = _cairo_surface_fill (surface,
+ CAIRO_OPERATOR_IN,
+ &_cairo_pattern_white.base,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ region);
+ clip_path = clip_path->prev;
+ }
+
+ copy->path = copy_path;
+ _cairo_clip_destroy (copy);
+ if (region != copy)
+ _cairo_clip_destroy (region);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ *tx = clip->extents.x;
+ *ty = clip->extents.y;
+ return surface;
+}
+
+cairo_surface_t *
+_cairo_clip_get_image (const cairo_clip_t *clip,
+ cairo_surface_t *target,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ surface = cairo_surface_create_similar_image (target,
+ CAIRO_FORMAT_A8,
+ extents->width,
+ extents->height);
+ if (unlikely (surface->status))
+ return surface;
+
+ status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE,
+ &_cairo_pattern_white.base, NULL);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = _cairo_clip_combine_with_surface (clip, surface,
+ extents->x, extents->y);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ surface = _cairo_surface_create_in_error (status);
+ }
+
+ return surface;
+}
diff --git a/src/cairo-clip-tor-scan-converter.c b/src/cairo-clip-tor-scan-converter.c
new file mode 100755
index 000000000..e32a5a9d9
--- /dev/null
+++ b/src/cairo-clip-tor-scan-converter.c
@@ -0,0 +1,1845 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008 M Joonas Pihlaja
+ * Copyright (c) 2007 David Turner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ * 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>
+#include <setjmp.h>
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_Y 15
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as
+ * pixel scaled numbers. These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<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;
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/*-------------------------------------------------------------------------
+ * 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;
+
+ jmp_buf *jmp;
+
+ /* Free list of previously allocated chunks. All have >= default
+ * capacity. */
+ struct _pool_chunk *first_free;
+
+ /* The default capacity of a chunk. */
+ size_t default_capacity;
+
+ /* Header for the sentinel chunk. Directly following the pool
+ * struct should be some space for embedded elements from which
+ * the sentinel chunk allocates from. */
+ struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+ /* Next in y-bucket or active list. */
+ struct edge *next;
+
+ /* Current x coordinate while the edge is on the active
+ * list. Initialised to the x coordinate of the top of the
+ * edge. The quotient is in grid_scaled_x_t units and the
+ * remainder is mod dy in grid_scaled_y_t units.*/
+ struct quorem x;
+
+ /* Advance of the current x when moving down a subsample line. */
+ struct quorem dxdy;
+
+ /* Advance of the current x when moving down a full pixel
+ * row. Only initialised when the height of the edge is large
+ * enough that there's a chance the edge could be stepped by a
+ * full row's worth of subsample rows at a time. */
+ struct quorem dxdy_full;
+
+ /* The clipped y of the top of the edge. */
+ grid_scaled_y_t ytop;
+
+ /* y2-y1 after orienting the edge downwards. */
+ grid_scaled_y_t dy;
+
+ /* Number of subsample rows remaining to scan convert of this
+ * edge. */
+ grid_scaled_y_t height_left;
+
+ /* Original sign of the edge: +1 for downwards, -1 for upwards
+ * edges. */
+ int dir;
+ int vertical;
+ int clip;
+};
+
+/* Number of subsample rows per y-bucket. Must be GRID_Y. */
+#define EDGE_Y_BUCKET_HEIGHT GRID_Y
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+ /* The vertical clip extents. */
+ grid_scaled_y_t ymin, ymax;
+
+ /* Array of edges all starting in the same bucket. An edge is put
+ * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+ * it is added to the polygon. */
+ struct edge **y_buckets;
+ struct edge *y_buckets_embedded[64];
+
+ struct {
+ struct pool base[1];
+ struct edge embedded[32];
+ } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel. It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one. The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * | | |
+ * | | |
+ * |_______________________|_______________________|
+ * | \...................|.......................|\
+ * | \..................|.......................| |
+ * | \.................|.......................| |
+ * | \....covered.....|.......................| |
+ * | \....area.......|.......................| } covered height
+ * | \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * | area \............|.......................| |
+ * |___________\...........|.......................|/
+ * | | |
+ * | | |
+ * | | |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead. The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge. As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign. When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+ struct cell *next;
+ int x;
+ grid_area_t uncovered_area;
+ grid_scaled_y_t covered_height;
+ grid_scaled_y_t clipped_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x. It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+ /* Sentinel nodes */
+ struct cell head, tail;
+
+ /* Cursor state for iterating through the cell list. */
+ struct cell *cursor;
+
+ /* Cells in the cell list are owned by the cell list and are
+ * allocated from this pool. */
+ struct {
+ struct pool base[1];
+ struct cell embedded[32];
+ } cell_pool;
+};
+
+struct cell_pair {
+ struct cell *cell1;
+ struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+ /* Leftmost edge on the current scan line. */
+ struct edge *head;
+
+ /* A lower bound on the height of the active edges is used to
+ * estimate how soon some active edge ends. We can't advance the
+ * scan conversion by a full pixel row if an edge ends somewhere
+ * within it. */
+ grid_scaled_y_t min_height;
+};
+
+struct glitter_scan_converter {
+ struct polygon polygon[1];
+ struct active_list active[1];
+ struct cell_list coverages[1];
+
+ /* Clip box. */
+ grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+ struct quorem qr;
+ qr.quo = a/b;
+ qr.rem = a%b;
+ if ((a^b)<0 && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+ struct quorem qr;
+ long long xa = (long long)x*a;
+ qr.quo = xa/b;
+ qr.rem = xa%b;
+ if ((xa>=0) != (b>=0) && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+ struct _pool_chunk *p,
+ struct _pool_chunk *prev_chunk,
+ size_t capacity)
+{
+ p->prev_chunk = prev_chunk;
+ p->size = 0;
+ p->capacity = capacity;
+ return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+ struct _pool_chunk *p;
+
+ p = malloc(size + sizeof(struct _pool_chunk));
+ if (unlikely (NULL == p))
+ longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+ jmp_buf *jmp,
+ size_t default_capacity,
+ size_t embedded_capacity)
+{
+ pool->jmp = jmp;
+ pool->current = pool->sentinel;
+ pool->first_free = NULL;
+ pool->default_capacity = default_capacity;
+ _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+ struct _pool_chunk *p = pool->current;
+ do {
+ while (NULL != p) {
+ struct _pool_chunk *prev = p->prev_chunk;
+ if (p != pool->sentinel)
+ free(p);
+ p = prev;
+ }
+ p = pool->first_free;
+ pool->first_free = NULL;
+ } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+ struct pool *pool,
+ size_t size)
+{
+ struct _pool_chunk *chunk;
+ void *obj;
+ size_t capacity;
+
+ /* If the allocation is smaller than the default chunk size then
+ * try getting a chunk off the free list. Force alloc of a new
+ * chunk for large requests. */
+ capacity = size;
+ chunk = NULL;
+ if (size < pool->default_capacity) {
+ capacity = pool->default_capacity;
+ chunk = pool->first_free;
+ if (chunk) {
+ pool->first_free = chunk->prev_chunk;
+ _pool_chunk_init(chunk, pool->current, chunk->capacity);
+ }
+ }
+
+ if (NULL == chunk)
+ chunk = _pool_chunk_create (pool, capacity);
+ pool->current = chunk;
+
+ obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+}
+
+/* Allocate size bytes from the pool. The first allocated address
+ * returned from a pool is aligned to sizeof(void*). Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated. Returns the address of a new memory area or %NULL on
+ * allocation failures. The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+ struct _pool_chunk *chunk = pool->current;
+
+ if (size <= chunk->capacity - chunk->size) {
+ void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+ } else {
+ return _pool_alloc_from_new_chunk(pool, size);
+ }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+ /* Transfer all used chunks to the chunk free list. */
+ struct _pool_chunk *chunk = pool->current;
+ if (chunk != pool->sentinel) {
+ while (chunk->prev_chunk != pool->sentinel) {
+ chunk = chunk->prev_chunk;
+ }
+ chunk->prev_chunk = pool->first_free;
+ pool->first_free = pool->current;
+ }
+ /* Reset the sentinel as the current chunk. */
+ pool->current = pool->sentinel;
+ pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning. After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+ cells->cursor = &cells->head;
+}
+
+/* Rewind the cell list if its cursor has been advanced past x. */
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+ if (tail->x > x)
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+ pool_init(cells->cell_pool.base, jmp,
+ 256*sizeof(struct cell),
+ sizeof(cells->cell_pool.embedded));
+ cells->tail.next = NULL;
+ cells->tail.x = INT_MAX;
+ cells->head.x = INT_MIN;
+ cells->head.next = &cells->tail;
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+ pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list. This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+ cell_list_rewind (cells);
+ cells->head.next = &cells->tail;
+ pool_reset (cells->cell_pool.base);
+}
+
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+ struct cell *tail,
+ int x)
+{
+ struct cell *cell;
+
+ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+ cell->next = tail->next;
+ tail->next = cell;
+ cell->x = x;
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->clipped_height = 0;
+ return cell;
+}
+
+/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
+ * needed to be allocated but couldn't be. Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+
+ while (1) {
+ UNROLL3({
+ if (tail->next->x > x)
+ break;
+ tail = tail->next;
+ });
+ }
+
+ if (tail->x != x)
+ tail = cell_list_alloc (cells, tail, x);
+ return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2. This is exactly equivalent
+ * to
+ *
+ * pair.cell1 = cell_list_find(cells, x1);
+ * pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+ struct cell_pair pair;
+
+ pair.cell1 = cells->cursor;
+ while (1) {
+ UNROLL3({
+ if (pair.cell1->next->x > x1)
+ break;
+ pair.cell1 = pair.cell1->next;
+ });
+ }
+ if (pair.cell1->x != x1) {
+ struct cell *cell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ cell->x = x1;
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->clipped_height = 0;
+ cell->next = pair.cell1->next;
+ pair.cell1->next = cell;
+ pair.cell1 = cell;
+ }
+
+ pair.cell2 = pair.cell1;
+ while (1) {
+ UNROLL3({
+ if (pair.cell2->next->x > x2)
+ break;
+ pair.cell2 = pair.cell2->next;
+ });
+ }
+ if (pair.cell2->x != x2) {
+ struct cell *cell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->clipped_height = 0;
+ cell->x = x2;
+ cell->next = pair.cell2->next;
+ pair.cell2->next = cell;
+ pair.cell2 = cell;
+ }
+
+ cells->cursor = pair.cell2;
+ return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+ grid_scaled_x_t x1,
+ grid_scaled_x_t x2)
+{
+ int ix1, fx1;
+ int ix2, fx2;
+
+ GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+ if (ix1 != ix2) {
+ struct cell_pair p;
+ p = cell_list_find_pair(cells, ix1, ix2);
+ p.cell1->uncovered_area += 2*fx1;
+ ++p.cell1->covered_height;
+ p.cell2->uncovered_area -= 2*fx2;
+ --p.cell2->covered_height;
+ } else {
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->uncovered_area += 2*(fx1-fx2);
+ }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change. In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.) */
+static void
+cell_list_render_edge(
+ struct cell_list *cells,
+ struct edge *edge,
+ int sign)
+{
+ grid_scaled_y_t y1, y2, dy;
+ grid_scaled_x_t dx;
+ int ix1, ix2;
+ grid_scaled_x_t fx1, fx2;
+
+ struct quorem x1 = edge->x;
+ struct quorem x2 = x1;
+
+ if (! edge->vertical) {
+ x2.quo += edge->dxdy_full.quo;
+ x2.rem += edge->dxdy_full.rem;
+ if (x2.rem >= 0) {
+ ++x2.quo;
+ x2.rem -= edge->dy;
+ }
+
+ edge->x = x2;
+ }
+
+ GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+ /* Edge is entirely within a column? */
+ if (ix1 == ix2) {
+ /* We always know that ix1 is >= the cell list cursor in this
+ * case due to the no-intersections precondition. */
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->covered_height += sign*GRID_Y;
+ cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+ return;
+ }
+
+ /* Orient the edge left-to-right. */
+ dx = x2.quo - x1.quo;
+ if (dx >= 0) {
+ y1 = 0;
+ y2 = GRID_Y;
+ } else {
+ int tmp;
+ tmp = ix1; ix1 = ix2; ix2 = tmp;
+ tmp = fx1; fx1 = fx2; fx2 = tmp;
+ dx = -dx;
+ sign = -sign;
+ y1 = GRID_Y;
+ y2 = 0;
+ }
+ dy = y2 - y1;
+
+ /* Add coverage for all pixels [ix1,ix2] on this row crossed
+ * by the edge. */
+ {
+ struct cell_pair pair;
+ struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+ /* When rendering a previous edge on the active list we may
+ * advance the cell list cursor past the leftmost pixel of the
+ * current edge even though the two edges don't intersect.
+ * e.g. consider two edges going down and rightwards:
+ *
+ * --\_+---\_+-----+-----+----
+ * \_ \_ | |
+ * | \_ | \_ | |
+ * | \_| \_| |
+ * | \_ \_ |
+ * ----+-----+-\---+-\---+----
+ *
+ * The left edge touches cells past the starting cell of the
+ * right edge. Fortunately such cases are rare.
+ *
+ * The rewinding is never necessary if the current edge stays
+ * within a single column because we've checked before calling
+ * this function that the active list order won't change. */
+ cell_list_maybe_rewind(cells, ix1);
+
+ pair = cell_list_find_pair(cells, ix1, ix1+1);
+ pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+ pair.cell1->covered_height += sign*y.quo;
+ y.quo += y1;
+
+ if (ix1+1 < ix2) {
+ struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+ struct cell *cell = pair.cell2;
+
+ ++ix1;
+ do {
+ grid_scaled_y_t y_skip = dydx_full.quo;
+ y.rem += dydx_full.rem;
+ if (y.rem >= dx) {
+ ++y_skip;
+ y.rem -= dx;
+ }
+
+ y.quo += y_skip;
+
+ y_skip *= sign;
+ cell->uncovered_area += y_skip*GRID_X;
+ cell->covered_height += y_skip;
+
+ ++ix1;
+ cell = cell_list_find(cells, ix1);
+ } while (ix1 != ix2);
+
+ pair.cell2 = cell;
+ }
+ pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+ pair.cell2->covered_height += sign*(y2 - y.quo);
+ }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+ polygon->ymin = polygon->ymax = 0;
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ pool_init (polygon->edge_pool.base, jmp,
+ 8192 - sizeof (struct _pool_chunk),
+ sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static cairo_status_t
+polygon_reset (struct polygon *polygon,
+ grid_scaled_y_t ymin,
+ grid_scaled_y_t ymax)
+{
+ unsigned h = ymax - ymin;
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
+ ymin);
+
+ pool_reset(polygon->edge_pool.base);
+
+ if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
+ goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+ polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+ sizeof (struct edge *));
+ if (unlikely (NULL == polygon->y_buckets))
+ goto bail_no_mem;
+ }
+ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
+ return CAIRO_STATUS_SUCCESS;
+
+ bail_no_mem:
+ polygon->ymin = 0;
+ polygon->ymax = 0;
+ return CAIRO_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(
+ struct polygon *polygon,
+ struct edge *e)
+{
+ unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+ struct edge **ptail = &polygon->y_buckets[ix];
+ e->next = *ptail;
+ *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+ const cairo_edge_t *edge,
+ int clip)
+{
+ struct edge *e;
+ grid_scaled_x_t dx;
+ grid_scaled_y_t dy;
+ grid_scaled_y_t ytop, ybot;
+ grid_scaled_y_t ymin = polygon->ymin;
+ grid_scaled_y_t ymax = polygon->ymax;
+
+ assert (edge->bottom > edge->top);
+
+ if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+ return;
+
+ e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+ e->dir = edge->dir;
+ e->clip = clip;
+
+ ytop = edge->top >= ymin ? edge->top : ymin;
+ ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+ e->ytop = ytop;
+ e->height_left = ybot - ytop;
+
+ if (dx == 0) {
+ e->vertical = TRUE;
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ e->dxdy.quo = 0;
+ e->dxdy.rem = 0;
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_divrem (dx, dy);
+ if (ytop == edge->line.p1.y) {
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ } else {
+ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+
+ if (e->height_left >= GRID_Y) {
+ e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+ } else {
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+ e->x.rem -= dy; /* Bias the remainder for faster
+ * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+ active->head = NULL;
+ active->min_height = 0;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+ active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ * - head_a: The head of the first list.
+ * - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+ struct edge *head, **next;
+ int32_t x;
+
+ if (head_a == NULL)
+ return head_b;
+
+ next = &head;
+ if (head_a->x.quo <= head_b->x.quo) {
+ head = head_a;
+ } else {
+ head = head_b;
+ goto start_with_b;
+ }
+
+ do {
+ x = head_b->x.quo;
+ while (head_a != NULL && head_a->x.quo <= x) {
+ next = &head_a->next;
+ head_a = head_a->next;
+ }
+
+ *next = head_b;
+ if (head_a == NULL)
+ return head;
+
+start_with_b:
+ x = head_a->x.quo;
+ while (head_b != NULL && head_b->x.quo <= x) {
+ next = &head_b->next;
+ head_b = head_b->next;
+ }
+
+ *next = head_a;
+ if (head_b == NULL)
+ return head;
+ } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ * - list: The list to be sorted; list cannot be NULL.
+ * - limit: Recursion limit.
+ * Output:
+ * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ * input list; if the input list has fewer elements, head_out be a sorted list
+ * containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge *list,
+ unsigned int level,
+ struct edge **head_out)
+{
+ struct edge *head_other, *remaining;
+ unsigned int i;
+
+ head_other = list->next;
+
+ /* Single element list -> return */
+ if (head_other == NULL) {
+ *head_out = list;
+ return NULL;
+ }
+
+ /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
+ * - Initialize remaining to be the list containing the elements after the second in the input list.
+ * - Initialize *head_out to be the sorted list containing the first two element.
+ */
+ remaining = head_other->next;
+ if (list->x.quo <= head_other->x.quo) {
+ *head_out = list;
+ /* list->next = head_other; */ /* The input list is already like this. */
+ head_other->next = NULL;
+ } else {
+ *head_out = head_other;
+ head_other->next = list;
+ list->next = NULL;
+ }
+
+ for (i = 0; i < level && remaining; i++) {
+ /* Extract a sorted list of the same size as *head_out
+ * (2^(i+1) elements) from the list of remaining elements. */
+ remaining = sort_edges (remaining, i, &head_other);
+ *head_out = merge_sorted_edges (*head_out, head_other);
+ }
+
+ /* *head_out now contains (at most) 2^(level+1) elements. */
+
+ return remaining;
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+active_list_can_step_full_row (struct active_list *active)
+{
+ const struct edge *e;
+ int prev_x = INT_MIN;
+
+ /* Recomputes the minimum height of all edges on the active
+ * list if we have been dropping edges. */
+ if (active->min_height <= 0) {
+ int min_height = INT_MAX;
+
+ e = active->head;
+ while (NULL != e) {
+ if (e->height_left < min_height)
+ min_height = e->height_left;
+ e = e->next;
+ }
+
+ active->min_height = min_height;
+ }
+
+ if (active->min_height < GRID_Y)
+ return 0;
+
+ /* Check for intersections as no edges end during the next row. */
+ e = active->head;
+ while (NULL != e) {
+ struct quorem x = e->x;
+
+ if (! e->vertical) {
+ x.quo += e->dxdy_full.quo;
+ x.rem += e->dxdy_full.rem;
+ if (x.rem >= 0)
+ ++x.quo;
+ }
+
+ if (x.quo <= prev_x)
+ return 0;
+
+ prev_x = x.quo;
+ e = e->next;
+ }
+
+ return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_polygon(struct active_list *active,
+ struct edge **ptail,
+ grid_scaled_y_t y,
+ struct polygon *polygon)
+{
+ /* Split off the edges on the current subrow and merge them into
+ * the active list. */
+ int min_height = active->min_height;
+ struct edge *subrow_edges = NULL;
+ struct edge *tail = *ptail;
+
+ do {
+ struct edge *next = tail->next;
+
+ if (y == tail->ytop) {
+ tail->next = subrow_edges;
+ subrow_edges = tail;
+
+ if (tail->height_left < min_height)
+ min_height = tail->height_left;
+
+ *ptail = next;
+ } else
+ ptail = &tail->next;
+
+ tail = next;
+ } while (tail);
+
+ if (subrow_edges) {
+ sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
+ active->head = merge_sorted_edges (active->head, subrow_edges);
+ active->min_height = min_height;
+ }
+}
+
+/* Advance the edges on the active list by one subsample row by
+ * updating their x positions. Drop edges from the list that end. */
+inline static void
+active_list_substep_edges(struct active_list *active)
+{
+ struct edge **cursor = &active->head;
+ grid_scaled_x_t prev_x = INT_MIN;
+ struct edge *unsorted = NULL;
+ struct edge *edge = *cursor;
+
+ do {
+ UNROLL3({
+ struct edge *next;
+
+ if (NULL == edge)
+ break;
+
+ next = edge->next;
+ if (--edge->height_left) {
+ edge->x.quo += edge->dxdy.quo;
+ edge->x.rem += edge->dxdy.rem;
+ if (edge->x.rem >= 0) {
+ ++edge->x.quo;
+ edge->x.rem -= edge->dy;
+ }
+
+ if (edge->x.quo < prev_x) {
+ *cursor = next;
+ edge->next = unsorted;
+ unsorted = edge;
+ } else {
+ prev_x = edge->x.quo;
+ cursor = &edge->next;
+ }
+ } else {
+ *cursor = next;
+ }
+ edge = next;
+ })
+ } while (1);
+
+ if (unsorted) {
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ active->head = merge_sorted_edges (active->head, unsorted);
+ }
+}
+
+inline static void
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int winding = 0;
+ int xstart;
+ int xend;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+ winding = edge->dir;
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge) {
+ ASSERT_NOT_REACHED;
+ return;
+ }
+
+ winding += edge->dir;
+ if (0 == winding) {
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+ }
+ }
+
+ xend = edge->x.quo;
+ cell_list_add_subspan (coverages, xstart, xend);
+
+ edge = edge->next;
+ }
+}
+
+static void
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int xstart;
+ int xend;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge) {
+ ASSERT_NOT_REACHED;
+ return;
+ }
+
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+
+ edge = edge->next;
+ }
+
+ xend = edge->x.quo;
+ cell_list_add_subspan (coverages, xstart, xend);
+
+ edge = edge->next;
+ }
+}
+
+static void
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+ int winding = left_edge->dir;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge) {
+ cell_list_render_edge (coverages, left_edge, +1);
+ return;
+ }
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ winding += right_edge->dir;
+ if (0 == winding) {
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ cell_list_render_edge (coverages, left_edge, +1);
+ cell_list_render_edge (coverages, right_edge, -1);
+
+ left_edge = *cursor;
+ }
+}
+
+static void
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge) {
+ cell_list_render_edge (coverages, left_edge, +1);
+ return;
+ }
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ cell_list_render_edge (coverages, left_edge, +1);
+ cell_list_render_edge (coverages, right_edge, -1);
+
+ left_edge = *cursor;
+ }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+ polygon_init(converter->polygon, jmp);
+ active_list_init(converter->active);
+ cell_list_init(converter->coverages, jmp);
+ converter->ymin=0;
+ converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+{
+ polygon_fini(converter->polygon);
+ cell_list_fini(converter->coverages);
+ converter->ymin=0;
+ converter->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+ /* Clamp to max/min representable scaled number. */
+ if (i >= 0) {
+ if (i >= INT_MAX/scale)
+ i = INT_MAX/scale;
+ }
+ else {
+ if (i <= INT_MIN/scale)
+ i = INT_MIN/scale;
+ }
+ return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+static cairo_status_t
+glitter_scan_converter_reset(glitter_scan_converter_t *converter,
+ int ymin, int ymax)
+{
+ cairo_status_t status;
+
+ converter->ymin = 0;
+ converter->ymax = 0;
+
+ ymin = int_to_grid_scaled_y(ymin);
+ ymax = int_to_grid_scaled_y(ymax);
+
+ active_list_reset(converter->active);
+ cell_list_reset(converter->coverages);
+ status = polygon_reset(converter->polygon, ymin, ymax);
+ if (status)
+ return status;
+
+ converter->ymin = ymin;
+ converter->ymax = ymax;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ * These macros convert an input coordinate in the client's
+ * device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
+ long long tmp__ = (long long)(grid_scale) * (in); \
+ tmp__ >>= GLITTER_INPUT_BITS; \
+ (out) = tmp__; \
+} while (0)
+
+static void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge,
+ int clip)
+{
+ cairo_edge_t e;
+
+ INPUT_TO_GRID_Y (edge->top, e.top);
+ INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+ if (e.top >= e.bottom)
+ return;
+
+ /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+ INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+ if (e.line.p1.y == e.line.p2.y)
+ return;
+
+ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+ INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+ e.dir = edge->dir;
+
+ polygon_add_edge (converter->polygon, &e, clip);
+}
+
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+ struct edge *e;
+
+ for (e = active->head; e != NULL; e = e->next) {
+ if (! e->vertical)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+ struct edge **cursor = &active->head;
+ struct edge *edge;
+
+ for (edge = *cursor; edge != NULL; edge = *cursor) {
+ edge->height_left -= GRID_Y * count;
+ if (edge->height_left)
+ cursor = &edge->next;
+ else
+ *cursor = edge->next;
+ }
+}
+
+static cairo_status_t
+blit_coverages (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ struct pool *span_pool,
+ int y, int height)
+{
+ struct cell *cell = cells->head.next;
+ int prev_x = -1;
+ int cover = 0, last_cover = 0;
+ int clip = 0;
+ cairo_half_open_span_t *spans;
+ unsigned num_spans;
+
+ assert (cell != &cells->tail);
+
+ /* Count number of cells remaining. */
+ {
+ struct cell *next = cell;
+ num_spans = 2;
+ while (next->next) {
+ next = next->next;
+ ++num_spans;
+ }
+ num_spans = 2*num_spans;
+ }
+
+ /* Allocate enough spans for the row. */
+ pool_reset (span_pool);
+ spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
+ num_spans = 0;
+
+ /* Form the spans from the coverages and areas. */
+ for (; cell->next; cell = cell->next) {
+ int x = cell->x;
+ int area;
+
+ if (x > prev_x && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ spans[num_spans].inverse = 0;
+ last_cover = cover;
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ clip += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ if (area != last_cover) {
+ spans[num_spans].x = x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+ spans[num_spans].inverse = 0;
+ last_cover = area;
+ ++num_spans;
+ }
+
+ prev_x = x+1;
+ }
+
+ /* Dump them into the renderer. */
+ return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+static void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+ int nonzero_fill,
+ cairo_span_renderer_t *span_renderer,
+ struct pool *span_pool)
+{
+ int i, j;
+ int ymax_i = converter->ymax / GRID_Y;
+ int ymin_i = converter->ymin / GRID_Y;
+ int h = ymax_i - ymin_i;
+ struct polygon *polygon = converter->polygon;
+ struct cell_list *coverages = converter->coverages;
+ struct active_list *active = converter->active;
+
+ /* Render each pixel row. */
+ for (i = 0; i < h; i = j) {
+ int do_full_step = 0;
+
+ j = i + 1;
+
+ /* Determine if we can ignore this row or use the full pixel
+ * stepper. */
+ if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+ if (! active->head) {
+ for (; j < h && ! polygon->y_buckets[j]; j++)
+ ;
+ continue;
+ }
+
+ do_full_step = active_list_can_step_full_row (active);
+ }
+
+ if (do_full_step) {
+ /* Step by a full pixel row's worth. */
+ if (nonzero_fill)
+ apply_nonzero_fill_rule_and_step_edges (active, coverages);
+ else
+ apply_evenodd_fill_rule_and_step_edges (active, coverages);
+
+ if (active_list_is_vertical (active)) {
+ while (j < h &&
+ polygon->y_buckets[j] == NULL &&
+ active->min_height >= 2*GRID_Y)
+ {
+ active->min_height -= GRID_Y;
+ j++;
+ }
+ if (j != i + 1)
+ step_edges (active, j - (i + 1));
+ }
+ } else {
+ grid_scaled_y_t suby;
+
+ /* Subsample this row. */
+ for (suby = 0; suby < GRID_Y; suby++) {
+ grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+
+ if (polygon->y_buckets[i]) {
+ active_list_merge_edges_from_polygon (active,
+ &polygon->y_buckets[i], y,
+ polygon);
+ }
+
+ if (nonzero_fill)
+ apply_nonzero_fill_rule_for_subrow (active, coverages);
+ else
+ apply_evenodd_fill_rule_for_subrow (active, coverages);
+
+ active_list_substep_edges(active);
+ }
+ }
+
+ blit_coverages (coverages, span_renderer, span_pool, i+ymin_i, j -i);
+ cell_list_reset (coverages);
+
+ if (! active->head)
+ active->min_height = INT_MAX;
+ else
+ active->min_height -= GRID_Y;
+ }
+}
+
+struct _cairo_clip_tor_scan_converter {
+ cairo_scan_converter_t base;
+
+ glitter_scan_converter_t converter[1];
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+
+ cairo_fill_rule_t clip_fill_rule;
+ cairo_antialias_t clip_antialias;
+
+ jmp_buf jmp;
+
+ struct {
+ struct pool base[1];
+ cairo_half_open_span_t embedded[32];
+ } span_pool;
+};
+
+typedef struct _cairo_clip_tor_scan_converter cairo_clip_tor_scan_converter_t;
+
+static void
+_cairo_clip_tor_scan_converter_destroy (void *converter)
+{
+ cairo_clip_tor_scan_converter_t *self = converter;
+ if (self == NULL) {
+ return;
+ }
+ _glitter_scan_converter_fini (self->converter);
+ pool_fini (self->span_pool.base);
+ free(self);
+}
+
+static cairo_status_t
+_cairo_clip_tor_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_clip_tor_scan_converter_t *self = converter;
+ cairo_status_t status;
+
+ if ((status = setjmp (self->jmp)))
+ return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+ glitter_scan_converter_render (self->converter,
+ self->fill_rule == CAIRO_FILL_RULE_WINDING,
+ renderer,
+ self->span_pool.base);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias)
+{
+ cairo_clip_tor_scan_converter_t *self;
+ cairo_polygon_t clipper;
+ cairo_status_t status;
+ int i;
+
+ self = calloc (1, sizeof(struct _cairo_clip_tor_scan_converter));
+ if (unlikely (self == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail_nomem;
+ }
+
+ self->base.destroy = _cairo_clip_tor_scan_converter_destroy;
+ self->base.generate = _cairo_clip_tor_scan_converter_generate;
+
+ pool_init (self->span_pool.base, &self->jmp,
+ 250 * sizeof(self->span_pool.embedded[0]),
+ sizeof(self->span_pool.embedded));
+
+ _glitter_scan_converter_init (self->converter, &self->jmp);
+ status = glitter_scan_converter_reset (self->converter,
+ clip->extents.y,
+ clip->extents.y + clip->extents.height);
+ if (unlikely (status))
+ goto bail;
+
+ self->fill_rule = fill_rule;
+ self->antialias = antialias;
+
+ for (i = 0; i < polygon->num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter,
+ &polygon->edges[i],
+ FALSE);
+
+ status = _cairo_clip_get_polygon (clip,
+ &clipper,
+ &self->clip_fill_rule,
+ &self->clip_antialias);
+ if (unlikely (status))
+ goto bail;
+
+ for (i = 0; i < clipper.num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter,
+ &clipper.edges[i],
+ TRUE);
+ _cairo_polygon_fini (&clipper);
+
+ return &self->base;
+
+ bail:
+ self->base.destroy(&self->base);
+ bail_nomem:
+ return _cairo_scan_converter_create_in_error (status);
+}
+
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
new file mode 100755
index 000000000..dcafaf229
--- /dev/null
+++ b/src/cairo-clip.c
@@ -0,0 +1,846 @@
+/* -*- 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-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static freed_pool_t clip_path_pool;
+static freed_pool_t clip_pool;
+
+const cairo_clip_t __cairo_clip_all;
+
+static cairo_clip_path_t *
+_cairo_clip_path_create (cairo_clip_t *clip)
+{
+ cairo_clip_path_t *clip_path;
+
+ clip_path = _freed_pool_get (&clip_path_pool);
+ if (unlikely (clip_path == NULL)) {
+ clip_path = malloc (sizeof (cairo_clip_path_t));
+ if (unlikely (clip_path == NULL))
+ return NULL;
+ }
+
+ CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
+
+ clip_path->prev = clip->path;
+ clip->path = clip_path;
+
+ return clip_path;
+}
+
+cairo_clip_path_t *
+_cairo_clip_path_reference (cairo_clip_path_t *clip_path)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
+
+ _cairo_reference_count_inc (&clip_path->ref_count);
+
+ return clip_path;
+}
+
+void
+_cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
+ return;
+
+ _cairo_path_fixed_fini (&clip_path->path);
+
+ if (clip_path->prev != NULL)
+ _cairo_clip_path_destroy (clip_path->prev);
+
+ _freed_pool_put (&clip_path_pool, clip_path);
+}
+
+cairo_clip_t *
+_cairo_clip_create (void)
+{
+ cairo_clip_t *clip;
+
+ clip = _freed_pool_get (&clip_pool);
+ if (unlikely (clip == NULL)) {
+ clip = malloc (sizeof (cairo_clip_t));
+ if (unlikely (clip == NULL))
+ return NULL;
+ }
+
+ clip->extents = _cairo_unbounded_rectangle;
+
+ clip->path = NULL;
+ clip->boxes = NULL;
+ clip->num_boxes = 0;
+ clip->region = NULL;
+ clip->is_region = FALSE;
+
+ return clip;
+}
+
+void
+_cairo_clip_destroy (cairo_clip_t *clip)
+{
+ if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+ return;
+
+ if (clip->path != NULL)
+ _cairo_clip_path_destroy (clip->path);
+
+ if (clip->boxes != &clip->embedded_box)
+ free (clip->boxes);
+ cairo_region_destroy (clip->region);
+
+ _freed_pool_put (&clip_pool, clip);
+}
+
+cairo_clip_t *
+_cairo_clip_copy (const cairo_clip_t *clip)
+{
+ cairo_clip_t *copy;
+
+ if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+ return (cairo_clip_t *) clip;
+
+ copy = _cairo_clip_create ();
+ if (copy == NULL)
+ return NULL;
+
+ if (clip->path)
+ copy->path = _cairo_clip_path_reference (clip->path);
+
+ if (clip->num_boxes) {
+ if (clip->num_boxes == 1) {
+ copy->boxes = &copy->embedded_box;
+ } else {
+ copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
+ if (unlikely (copy->boxes == NULL))
+ return _cairo_clip_set_all_clipped (copy);
+ }
+
+ memcpy (copy->boxes, clip->boxes,
+ clip->num_boxes * sizeof (cairo_box_t));
+ copy->num_boxes = clip->num_boxes;
+ }
+
+ copy->extents = clip->extents;
+ copy->region = cairo_region_reference (clip->region);
+ copy->is_region = clip->is_region;
+
+ return copy;
+}
+
+cairo_clip_t *
+_cairo_clip_copy_path (const cairo_clip_t *clip)
+{
+ cairo_clip_t *copy;
+
+ if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+ return (cairo_clip_t *) clip;
+
+ assert (clip->num_boxes);
+
+ copy = _cairo_clip_create ();
+ if (copy == NULL)
+ return NULL;
+
+ copy->extents = clip->extents;
+ if (clip->path)
+ copy->path = _cairo_clip_path_reference (clip->path);
+
+ return copy;
+}
+
+cairo_clip_t *
+_cairo_clip_copy_region (const cairo_clip_t *clip)
+{
+ cairo_clip_t *copy;
+ int i;
+
+ if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+ return (cairo_clip_t *) clip;
+
+ assert (clip->num_boxes);
+
+ copy = _cairo_clip_create ();
+ if (copy == NULL)
+ return NULL;
+
+ copy->extents = clip->extents;
+
+ if (clip->num_boxes == 1) {
+ copy->boxes = &copy->embedded_box;
+ } else {
+ copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
+ if (unlikely (copy->boxes == NULL))
+ return _cairo_clip_set_all_clipped (copy);
+ }
+
+ for (i = 0; i < clip->num_boxes; i++) {
+ copy->boxes[i].p1.x = _cairo_fixed_floor (clip->boxes[i].p1.x);
+ copy->boxes[i].p1.y = _cairo_fixed_floor (clip->boxes[i].p1.y);
+ copy->boxes[i].p2.x = _cairo_fixed_ceil (clip->boxes[i].p2.x);
+ copy->boxes[i].p2.y = _cairo_fixed_ceil (clip->boxes[i].p2.y);
+ }
+ copy->num_boxes = clip->num_boxes;
+
+ copy->region = cairo_region_reference (clip->region);
+ copy->is_region = TRUE;
+
+ return copy;
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_path (cairo_clip_t *clip,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_clip_path_t *clip_path;
+ cairo_status_t status;
+ cairo_rectangle_int_t extents;
+ cairo_box_t box;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ /* catch the empty clip path */
+ if (_cairo_path_fixed_fill_is_empty (path))
+ return _cairo_clip_set_all_clipped (clip);
+
+ if (_cairo_path_fixed_is_box (path, &box)) {
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ box.p1.x = _cairo_fixed_round_down (box.p1.x);
+ box.p1.y = _cairo_fixed_round_down (box.p1.y);
+ box.p2.x = _cairo_fixed_round_down (box.p2.x);
+ box.p2.y = _cairo_fixed_round_down (box.p2.y);
+ }
+
+ return _cairo_clip_intersect_box (clip, &box);
+ }
+ if (_cairo_path_fixed_fill_is_rectilinear (path))
+ return _cairo_clip_intersect_rectilinear_path (clip, path,
+ fill_rule, antialias);
+
+ _cairo_path_fixed_approximate_clip_extents (path, &extents);
+ if (extents.width == 0 || extents.height == 0)
+ return _cairo_clip_set_all_clipped (clip);
+
+ clip = _cairo_clip_intersect_rectangle (clip, &extents);
+ if (_cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_clip_set_all_clipped (clip);
+
+ status = _cairo_path_fixed_init_copy (&clip_path->path, path);
+ if (unlikely (status))
+ return _cairo_clip_set_all_clipped (clip);
+
+ clip_path->fill_rule = fill_rule;
+ clip_path->tolerance = tolerance;
+ clip_path->antialias = antialias;
+
+ if (clip->region) {
+ cairo_region_destroy (clip->region);
+ clip->region = NULL;
+ }
+
+ clip->is_region = FALSE;
+ return clip;
+}
+
+static cairo_clip_t *
+_cairo_clip_intersect_clip_path (cairo_clip_t *clip,
+ const cairo_clip_path_t *clip_path)
+{
+ if (clip_path->prev)
+ clip = _cairo_clip_intersect_clip_path (clip, clip_path->prev);
+
+ return _cairo_clip_intersect_path (clip,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias);
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_clip (cairo_clip_t *clip,
+ const cairo_clip_t *other)
+{
+ if (_cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ if (other == NULL)
+ return clip;
+
+ if (clip == NULL)
+ return _cairo_clip_copy (other);
+
+ if (_cairo_clip_is_all_clipped (other))
+ return _cairo_clip_set_all_clipped (clip);
+
+ if (! _cairo_rectangle_intersect (&clip->extents, &other->extents))
+ return _cairo_clip_set_all_clipped (clip);
+
+ if (other->num_boxes) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_for_array (&boxes, other->boxes, other->num_boxes);
+ clip = _cairo_clip_intersect_boxes (clip, &boxes);
+ }
+
+ if (! _cairo_clip_is_all_clipped (clip)) {
+ if (other->path) {
+ if (clip->path == NULL)
+ clip->path = _cairo_clip_path_reference (other->path);
+ else
+ clip = _cairo_clip_intersect_clip_path (clip, other->path);
+ }
+ }
+
+ if (clip->region) {
+ cairo_region_destroy (clip->region);
+ clip->region = NULL;
+ }
+ clip->is_region = FALSE;
+
+ return clip;
+}
+
+cairo_bool_t
+_cairo_clip_equal (const cairo_clip_t *clip_a,
+ const cairo_clip_t *clip_b)
+{
+ const cairo_clip_path_t *cp_a, *cp_b;
+
+ /* are both all-clipped or no-clip? */
+ if (clip_a == clip_b)
+ return TRUE;
+
+ /* or just one of them? */
+ if (clip_a == NULL || clip_b == NULL ||
+ _cairo_clip_is_all_clipped (clip_a) ||
+ _cairo_clip_is_all_clipped (clip_b))
+ {
+ return FALSE;
+ }
+
+ /* We have a pair of normal clips, check their contents */
+
+ if (clip_a->num_boxes != clip_b->num_boxes)
+ return FALSE;
+
+ if (memcmp (clip_a->boxes, clip_b->boxes,
+ sizeof (cairo_box_t) * clip_a->num_boxes))
+ return FALSE;
+
+ cp_a = clip_a->path;
+ cp_b = clip_b->path;
+ while (cp_a && cp_b) {
+ if (cp_a == cp_b)
+ return TRUE;
+
+ /* XXX compare reduced polygons? */
+
+ if (cp_a->antialias != cp_b->antialias)
+ return FALSE;
+
+ if (cp_a->tolerance != cp_b->tolerance)
+ return FALSE;
+
+ if (cp_a->fill_rule != cp_b->fill_rule)
+ return FALSE;
+
+ if (! _cairo_path_fixed_equal (&cp_a->path,
+ &cp_b->path))
+ return FALSE;
+
+ cp_a = cp_a->prev;
+ cp_b = cp_b->prev;
+ }
+
+ return cp_a == NULL && cp_b == NULL;
+}
+
+static cairo_clip_t *
+_cairo_clip_path_copy_with_translation (cairo_clip_t *clip,
+ cairo_clip_path_t *other_path,
+ int fx, int fy)
+{
+ cairo_status_t status;
+ cairo_clip_path_t *clip_path;
+
+ if (other_path->prev != NULL)
+ clip = _cairo_clip_path_copy_with_translation (clip, other_path->prev,
+ fx, fy);
+ if (_cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_clip_set_all_clipped (clip);
+
+ status = _cairo_path_fixed_init_copy (&clip_path->path,
+ &other_path->path);
+ if (unlikely (status))
+ return _cairo_clip_set_all_clipped (clip);
+
+ _cairo_path_fixed_translate (&clip_path->path, fx, fy);
+
+ clip_path->fill_rule = other_path->fill_rule;
+ clip_path->tolerance = other_path->tolerance;
+ clip_path->antialias = other_path->antialias;
+
+ return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty)
+{
+ int fx, fy, i;
+ cairo_clip_path_t *clip_path;
+
+ if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ if (tx == 0 && ty == 0)
+ return clip;
+
+ fx = _cairo_fixed_from_int (tx);
+ fy = _cairo_fixed_from_int (ty);
+
+ for (i = 0; i < clip->num_boxes; i++) {
+ clip->boxes[i].p1.x += fx;
+ clip->boxes[i].p2.x += fx;
+ clip->boxes[i].p1.y += fy;
+ clip->boxes[i].p2.y += fy;
+ }
+
+ clip->extents.x += tx;
+ clip->extents.y += ty;
+
+ if (clip->path == NULL)
+ return clip;
+
+ clip_path = clip->path;
+ clip->path = NULL;
+ clip = _cairo_clip_path_copy_with_translation (clip, clip_path, fx, fy);
+ _cairo_clip_path_destroy (clip_path);
+
+ return clip;
+}
+
+static cairo_status_t
+_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
+ const cairo_box_t *box)
+{
+ cairo_status_t status;
+
+ status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_path_fixed_close_path (path);
+}
+
+static cairo_status_t
+_cairo_path_fixed_init_from_boxes (cairo_path_fixed_t *path,
+ const cairo_boxes_t *boxes)
+{
+ cairo_status_t status;
+ const struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ _cairo_path_fixed_init (path);
+ if (boxes->num_boxes == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_path_fixed_add_box (path, &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_path_fixed_fini (path);
+ return status;
+ }
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_clip_t *
+_cairo_clip_intersect_clip_path_transformed (cairo_clip_t *clip,
+ const cairo_clip_path_t *clip_path,
+ const cairo_matrix_t *m)
+{
+ cairo_path_fixed_t path;
+
+ if (clip_path->prev)
+ clip = _cairo_clip_intersect_clip_path_transformed (clip,
+ clip_path->prev,
+ m);
+
+ if (_cairo_path_fixed_init_copy (&path, &clip_path->path))
+ return _cairo_clip_set_all_clipped (clip);
+
+ _cairo_path_fixed_transform (&path, m);
+
+ clip = _cairo_clip_intersect_path (clip,
+ &path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias);
+ _cairo_path_fixed_fini (&path);
+
+ return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m)
+{
+ cairo_clip_t *copy;
+
+ if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+ return clip;
+
+ if (_cairo_matrix_is_translation (m))
+ return _cairo_clip_translate (clip, m->x0, m->y0);
+
+ copy = _cairo_clip_create ();
+
+ if (clip->num_boxes) {
+ cairo_path_fixed_t path;
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes);
+ _cairo_path_fixed_init_from_boxes (&path, &boxes);
+ _cairo_path_fixed_transform (&path, m);
+
+ copy = _cairo_clip_intersect_path (copy, &path,
+ CAIRO_FILL_RULE_WINDING,
+ 0.1,
+ CAIRO_ANTIALIAS_DEFAULT);
+
+ _cairo_path_fixed_fini (&path);
+ }
+
+ if (clip->path)
+ copy = _cairo_clip_intersect_clip_path_transformed (copy, clip->path,m);
+
+ _cairo_clip_destroy (clip);
+ return copy;
+}
+
+cairo_clip_t *
+_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty)
+{
+ cairo_clip_t *copy;
+ int fx, fy, i;
+
+ if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+ return (cairo_clip_t *)clip;
+
+ if (tx == 0 && ty == 0)
+ return _cairo_clip_copy (clip);
+
+ copy = _cairo_clip_create ();
+ if (copy == NULL)
+ return _cairo_clip_set_all_clipped (copy);
+
+ fx = _cairo_fixed_from_int (tx);
+ fy = _cairo_fixed_from_int (ty);
+
+ if (clip->num_boxes) {
+ if (clip->num_boxes == 1) {
+ copy->boxes = &copy->embedded_box;
+ } else {
+ copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
+ if (unlikely (copy->boxes == NULL))
+ return _cairo_clip_set_all_clipped (copy);
+ }
+
+ for (i = 0; i < clip->num_boxes; i++) {
+ copy->boxes[i].p1.x = clip->boxes[i].p1.x + fx;
+ copy->boxes[i].p2.x = clip->boxes[i].p2.x + fx;
+ copy->boxes[i].p1.y = clip->boxes[i].p1.y + fy;
+ copy->boxes[i].p2.y = clip->boxes[i].p2.y + fy;
+ }
+ copy->num_boxes = clip->num_boxes;
+ }
+
+ copy->extents = clip->extents;
+ copy->extents.x += tx;
+ copy->extents.y += ty;
+
+ if (clip->path == NULL)
+ return copy;
+
+ return _cairo_clip_path_copy_with_translation (copy, clip->path, fx, fy);
+}
+
+cairo_bool_t
+_cairo_clip_contains_extents (const cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ const cairo_rectangle_int_t *rect;
+
+ rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
+ return _cairo_clip_contains_rectangle (clip, rect);
+}
+
+void
+_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip)
+{
+ int i;
+
+ if (clip == NULL) {
+ fprintf (stream, "no clip\n");
+ return;
+ }
+
+ if (_cairo_clip_is_all_clipped (clip)) {
+ fprintf (stream, "clip: all-clipped\n");
+ return;
+ }
+
+ fprintf (stream, "clip:\n");
+ fprintf (stream, " extents: (%d, %d) x (%d, %d), is-region? %d",
+ clip->extents.x, clip->extents.y,
+ clip->extents.width, clip->extents.height,
+ clip->is_region);
+
+ fprintf (stream, " num_boxes = %d\n", clip->num_boxes);
+ for (i = 0; i < clip->num_boxes; i++) {
+ fprintf (stream, " [%d] = (%f, %f), (%f, %f)\n", i,
+ _cairo_fixed_to_double (clip->boxes[i].p1.x),
+ _cairo_fixed_to_double (clip->boxes[i].p1.y),
+ _cairo_fixed_to_double (clip->boxes[i].p2.x),
+ _cairo_fixed_to_double (clip->boxes[i].p2.y));
+ }
+
+ if (clip->path) {
+ cairo_clip_path_t *clip_path = clip->path;
+ do {
+ fprintf (stream, "path: aa=%d, tolerance=%f, rule=%d: ",
+ clip_path->antialias,
+ clip_path->tolerance,
+ clip_path->fill_rule);
+ _cairo_debug_print_path (stream, &clip_path->path);
+ fprintf (stream, "\n");
+ } while ((clip_path = clip_path->prev) != NULL);
+ }
+}
+
+const cairo_rectangle_int_t *
+_cairo_clip_get_extents (const cairo_clip_t *clip)
+{
+ if (clip == NULL)
+ return &_cairo_unbounded_rectangle;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return &_cairo_empty_rectangle;
+
+ return &clip->extents;
+}
+
+const cairo_rectangle_list_t _cairo_rectangles_nil =
+ { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
+static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
+ { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
+
+static cairo_bool_t
+_cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
+ cairo_rectangle_int_t *clip_rect,
+ cairo_rectangle_t *user_rect)
+{
+ cairo_bool_t is_tight;
+
+ double x1 = clip_rect->x;
+ double y1 = clip_rect->y;
+ double x2 = clip_rect->x + (int) clip_rect->width;
+ double y2 = clip_rect->y + (int) clip_rect->height;
+
+ _cairo_gstate_backend_to_user_rectangle (gstate,
+ &x1, &y1, &x2, &y2,
+ &is_tight);
+
+ user_rect->x = x1;
+ user_rect->y = y1;
+ user_rect->width = x2 - x1;
+ user_rect->height = y2 - y1;
+
+ return is_tight;
+}
+
+cairo_rectangle_list_t *
+_cairo_rectangle_list_create_in_error (cairo_status_t status)
+{
+ cairo_rectangle_list_t *list;
+
+ if (status == CAIRO_STATUS_NO_MEMORY)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
+
+ list = malloc (sizeof (*list));
+ if (unlikely (list == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ }
+
+ list->status = status;
+ list->rectangles = NULL;
+ list->num_rectangles = 0;
+
+ return list;
+}
+
+cairo_rectangle_list_t *
+_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
+{
+#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S))
+
+ cairo_rectangle_list_t *list;
+ cairo_rectangle_t *rectangles = NULL;
+ cairo_region_t *region = NULL;
+ int n_rects = 0;
+ int i;
+
+ if (clip == NULL)
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+
+ if (_cairo_clip_is_all_clipped (clip))
+ goto DONE;
+
+ if (! _cairo_clip_is_region (clip))
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+
+ region = _cairo_clip_get_region (clip);
+ if (region == NULL)
+ return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
+
+ n_rects = cairo_region_num_rectangles (region);
+ if (n_rects) {
+ rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
+ if (unlikely (rectangles == NULL)) {
+ return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < n_rects; ++i) {
+ cairo_rectangle_int_t clip_rect;
+
+ cairo_region_get_rectangle (region, i, &clip_rect);
+
+ if (! _cairo_clip_int_rect_to_user (gstate,
+ &clip_rect,
+ &rectangles[i]))
+ {
+ free (rectangles);
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+ }
+ }
+ }
+
+ DONE:
+ list = malloc (sizeof (cairo_rectangle_list_t));
+ if (unlikely (list == NULL)) {
+ free (rectangles);
+ return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ list->status = CAIRO_STATUS_SUCCESS;
+ list->rectangles = rectangles;
+ list->num_rectangles = n_rects;
+ return list;
+
+#undef ERROR_LIST
+}
+
+/**
+ * cairo_rectangle_list_destroy:
+ * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangle_list()
+ *
+ * Unconditionally frees @rectangle_list and all associated
+ * references. After this call, the @rectangle_list pointer must not
+ * be dereferenced.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
+{
+ if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
+ rectangle_list == &_cairo_rectangles_not_representable)
+ return;
+
+ free (rectangle_list->rectangles);
+ free (rectangle_list);
+}
+
+void
+_cairo_clip_reset_static_data (void)
+{
+ _freed_pool_reset (&clip_path_pool);
+ _freed_pool_reset (&clip_pool);
+}
diff --git a/src/cairo-cogl-context-private.h b/src/cairo-cogl-context-private.h
new file mode 100755
index 000000000..0a7185ef1
--- /dev/null
+++ b/src/cairo-cogl-context-private.h
@@ -0,0 +1,52 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_CONTEXT_PRIVATE_H
+#define CAIRO_COGL_CONTEXT_PRIVATE_H
+
+#include "cairo-default-context-private.h"
+#include "cairo-cogl-private.h"
+
+typedef struct _cairo_cogl_context {
+ cairo_default_context_t base;
+
+ cairo_cogl_device_t *dev;
+ int path_ctm_age;
+ cairo_path_fixed_t user_path;
+
+ cairo_bool_t path_is_rectangle;
+ double x, y, width, height;
+} cairo_cogl_context_t;
+
+cairo_t *
+_cairo_cogl_context_create (void *target);
+
+#endif /* CAIRO_COGL_CONTEXT_PRIVATE_H */
diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
new file mode 100755
index 000000000..0116b0a5a
--- /dev/null
+++ b/src/cairo-cogl-context.c
@@ -0,0 +1,822 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+/* so long as we can verify that the ctm doesn't change multiple times
+ * during the construction of a path we can build a shadow
+ * #cairo_path_fixed_t in user coordinates that we can use to create a
+ * hash value for caching tessellations of that path.
+ *
+ * We need to hook into all the points where the ctm can be changed
+ * so we can bump a cr->path_ctm_age counter.
+ *
+ * We need to hook into all the points where the path can be modified
+ * so we can catch the start of a path and reset the cr->path_ctm_age
+ * counter at that point.
+ *
+ * When a draw operation is hit we can then check that the
+ * path_ctm_age == 0 and if so we create a hash of the path.
+ *
+ * We use this hash to lookup a #cairo_cogl_path_meta_t struct which
+ * may contain tessellated triangles for the path or may just contain
+ * a count of how many times the path has been re-seen (we only cache
+ * tessellated triangles if there is evidence that the path is being
+ * used multiple times because there is a cost involved in allocating
+ * a separate buffer for the triangles).
+ */
+
+#include "cairoint.h"
+
+#include "cairo-cogl-context-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-path-fixed-private.h"
+
+#include <glib.h>
+
+static freed_pool_t context_pool;
+
+void
+_cairo_cogl_context_reset_static_data (void)
+{
+ _freed_pool_reset (&context_pool);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rectangle_real (cairo_cogl_context_t *cr,
+ double x, double y,
+ double width, double height)
+{
+ cairo_status_t status;
+ status = cr->dev->backend_parent.rectangle (cr, x, y, width, height);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_cogl_path_fixed_rectangle (&cr->user_path,
+ _cairo_fixed_from_double (x),
+ _cairo_fixed_from_double (y),
+ _cairo_fixed_from_double (width),
+ _cairo_fixed_from_double (height));
+}
+
+/* The idea here is that we have a simplified way of tracking rectangle paths
+ * because rectangles are perhaps the most common shape drawn with cairo.
+ *
+ * Basically we have a speculative store for a rectangle path that doesn't
+ * need to use the #cairo_path_fixed_t api to describe a rectangle in terms of
+ * (move_to,rel_line_to,rel_line_to,_rel_line_to,close) because if you profile
+ * heavy rectangle drawing with Cairo that process can be overly expensive.
+ *
+ * If the user asks to add more than just a rectangle to their current path
+ * then we "flush" any speculative rectangle stored into the current path
+ * before continuing to append their operations.
+ *
+ * In addition to the speculative store cairo-cogl also has a fast-path
+ * fill_rectangle drawing operation that further aims to minimize the cost
+ * of drawing rectangles.
+ */
+static cairo_status_t
+_flush_cr_rectangle (cairo_cogl_context_t *cr)
+{
+ if (!cr->path_is_rectangle)
+ return CAIRO_STATUS_SUCCESS;
+
+ cr->path_is_rectangle = FALSE;
+ return _cairo_cogl_context_rectangle_real (cr, cr->x, cr->y, cr->width, cr->height);
+}
+
+static cairo_status_t
+_cairo_cogl_context_restore (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ cr->path_ctm_age++;
+ return cr->dev->backend_parent.restore (abstract_cr);
+}
+
+static cairo_status_t
+_cairo_cogl_context_translate (void *abstract_cr, double tx, double ty)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ cr->path_ctm_age++;
+ return cr->dev->backend_parent.translate (abstract_cr, tx, ty);
+}
+
+static cairo_status_t
+_cairo_cogl_context_scale (void *abstract_cr, double sx, double sy)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ cr->path_ctm_age++;
+ return cr->dev->backend_parent.scale (abstract_cr, sx, sy);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rotate (void *abstract_cr, double theta)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ cr->path_ctm_age++;
+ return cr->dev->backend_parent.rotate (abstract_cr, theta);
+}
+
+static cairo_status_t
+_cairo_cogl_context_transform (void *abstract_cr, const cairo_matrix_t *matrix)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ cr->path_ctm_age++;
+ return cr->dev->backend_parent.transform (abstract_cr, matrix);
+}
+
+static cairo_status_t
+_cairo_cogl_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ cr->path_ctm_age++;
+ return cr->dev->backend_parent.set_matrix (abstract_cr, matrix);
+}
+
+static cairo_status_t
+_cairo_cogl_context_set_identity_matrix (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ cr->path_ctm_age++;
+ return cr->dev->backend_parent.set_identity_matrix (abstract_cr);
+}
+
+static cairo_status_t
+_cairo_cogl_context_new_path (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.new_path (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+ _cairo_path_fixed_fini (&cr->user_path);
+ _cairo_path_fixed_init (&cr->user_path);
+ cr->path_is_rectangle = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_new_sub_path (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.new_sub_path (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+ _cairo_path_fixed_new_sub_path (&cr->user_path);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_move_to (void *abstract_cr, double x, double y)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_fixed_t x_fixed, y_fixed;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.move_to (abstract_cr, x, y);
+ if (unlikely (status))
+ return status;
+
+ x_fixed = _cairo_fixed_from_double (x);
+ y_fixed = _cairo_fixed_from_double (y);
+
+ return _cairo_path_fixed_move_to (&cr->user_path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_line_to (void *abstract_cr, double x, double y)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_fixed_t x_fixed, y_fixed;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.line_to (abstract_cr, x, y);
+ if (unlikely (status))
+ return status;
+
+ x_fixed = _cairo_fixed_from_double (x);
+ y_fixed = _cairo_fixed_from_double (y);
+
+ if (cr->user_path.buf.base.num_ops == 0)
+ cr->path_ctm_age = 0;
+
+ return _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_curve_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ 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 (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.curve_to (abstract_cr, x1, y1, x2, y2, x3, y3);
+ if (unlikely (status))
+ return status;
+
+ 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);
+
+ if (cr->user_path.buf.base.num_ops == 0)
+ cr->path_ctm_age = 0;
+
+ return _cairo_path_fixed_curve_to (&cr->user_path,
+ x1_fixed, y1_fixed,
+ x2_fixed, y2_fixed,
+ x3_fixed, y3_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_arc (void *abstract_cr,
+ double xc, double yc, double radius,
+ double angle1, double angle2,
+ cairo_bool_t forward)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.arc (abstract_cr, xc, yc, radius, angle1, angle2, forward);
+ if (unlikely (status))
+ return status;
+
+ if (cr->user_path.buf.base.num_ops == 0)
+ cr->path_ctm_age = 0;
+
+ /* Do nothing, successfully, if radius is <= 0 */
+ if (radius <= 0.0) {
+ cairo_fixed_t x_fixed, y_fixed;
+
+ x_fixed = _cairo_fixed_from_double (xc);
+ y_fixed = _cairo_fixed_from_double (yc);
+ status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_cogl_context_line_to (cr,
+ xc + radius * cos (angle1),
+ yc + radius * sin (angle1));
+
+ if (unlikely (status))
+ return status;
+
+ if (forward)
+ _cairo_arc_path (&cr->base.base, xc, yc, radius, angle1, angle2);
+ else
+ _cairo_arc_path_negative (&cr->base.base, xc, yc, radius, angle1, angle2);
+
+ return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */
+}
+
+static cairo_status_t
+_cairo_cogl_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_fixed_t dx_fixed, dy_fixed;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.rel_move_to (abstract_cr, dx, dy);
+ if (unlikely (status))
+ return status;
+
+ dx_fixed = _cairo_fixed_from_double (dx);
+ dy_fixed = _cairo_fixed_from_double (dy);
+
+ return _cairo_path_fixed_rel_move_to (&cr->user_path, dx_fixed, dy_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_fixed_t dx_fixed, dy_fixed;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.rel_line_to (abstract_cr, dx, dy);
+ if (unlikely (status))
+ return status;
+
+ dx_fixed = _cairo_fixed_from_double (dx);
+ dy_fixed = _cairo_fixed_from_double (dy);
+
+ if (cr->user_path.buf.base.num_ops == 0)
+ cr->path_ctm_age = 0;
+
+ return _cairo_path_fixed_rel_line_to (&cr->user_path, dx_fixed, dy_fixed);
+}
+
+
+static cairo_status_t
+_cairo_cogl_context_rel_curve_to (void *abstract_cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_fixed_t dx1_fixed, dy1_fixed;
+ cairo_fixed_t dx2_fixed, dy2_fixed;
+ cairo_fixed_t dx3_fixed, dy3_fixed;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.rel_curve_to (abstract_cr, dx1, dy1, dx2, dy2, dx3, dy3);
+ if (unlikely (status))
+ return status;
+
+ 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);
+
+ if (cr->user_path.buf.base.num_ops == 0)
+ cr->path_ctm_age = 0;
+
+ return _cairo_path_fixed_rel_curve_to (&cr->user_path,
+ dx1_fixed, dy1_fixed,
+ dx2_fixed, dy2_fixed,
+ dx3_fixed, dy3_fixed);
+}
+
+#if 0
+static cairo_status_t
+_cairo_cogl_context_arc_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double radius)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.arc_to (abstract_cr, x1, y1, x2, y2, radius);
+ if (unlikely (status))
+ return status;
+#warning "FIXME"
+}
+
+static cairo_status_t
+_cairo_cogl_rel_arc_to (void *cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double radius)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.rel_arc_to (abstract_cr, dx1, dy2, dx2, dy2, radius);
+ if (unlikely (status))
+ return status;
+#warning "FIXME"
+}
+#endif
+
+static cairo_status_t
+_cairo_cogl_context_close_path (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ if (cr->path_is_rectangle) {
+ status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = cr->dev->backend_parent.close_path (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+ if (cr->user_path.buf.base.num_ops == 0)
+ cr->path_ctm_age = 0;
+
+ return _cairo_path_fixed_close_path (&cr->user_path);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rectangle (void *abstract_cr,
+ double x, double y,
+ double width, double height)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ if (cr->user_path.buf.base.num_ops == 0) {
+ cr->path_ctm_age = 0;
+
+#if 1
+ /* XXX: Since drawing rectangles is so common we have a
+ * fast-path for drawing a single rectangle. */
+ cr->x = x;
+ cr->y = y;
+ cr->width = width;
+ cr->height = height;
+ cr->path_is_rectangle = TRUE;
+ return CAIRO_STATUS_SUCCESS;
+#endif
+ }
+
+ if (cr->path_is_rectangle) {
+ cairo_status_t status = _flush_cr_rectangle (cr);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_cogl_context_rectangle_real (cr, x, y, width, height);
+}
+
+/* Since the surface backend drawing operator functions don't get
+ * passed the current #cairo_t context we don't have a good way
+ * to get our user-coordinates path into our surface operator
+ * functions.
+ *
+ * For now we use this function to set side band data on the surface
+ * itself.
+ */
+static void
+_cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface,
+ cairo_cogl_context_t *cr)
+{
+
+ if (cr->path_ctm_age <= 1) {
+ surface->user_path = &cr->user_path;
+ surface->ctm = &cr->base.gstate->ctm;
+ surface->ctm_inverse = &cr->base.gstate->ctm_inverse;
+ surface->path_is_rectangle = cr->path_is_rectangle;
+ if (surface->path_is_rectangle) {
+ surface->path_rectangle_x = cr->x;
+ surface->path_rectangle_y = cr->y;
+ surface->path_rectangle_width = cr->width;
+ surface->path_rectangle_height = cr->height;
+ }
+ } else {
+ surface->user_path = NULL;
+ surface->path_is_rectangle = FALSE;
+ }
+}
+
+static cairo_status_t
+_cairo_cogl_context_fill (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+ if (cr->path_is_rectangle) {
+ status = _cairo_cogl_surface_fill_rectangle (cr->base.gstate->target,
+ cr->base.gstate->op,
+ cr->base.gstate->source,
+ cr->x,
+ cr->y,
+ cr->width,
+ cr->height,
+ &cr->base.gstate->ctm,
+ cr->base.gstate->clip);
+ if (status == CAIRO_STATUS_SUCCESS)
+ goto DONE;
+ _flush_cr_rectangle (cr);
+ }
+
+ _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+ status = cr->dev->backend_parent.fill (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+DONE:
+ _cairo_path_fixed_fini (&cr->user_path);
+ _cairo_path_fixed_init (&cr->user_path);
+ cr->path_is_rectangle = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_fill_preserve (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+ _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+ status = cr->dev->backend_parent.fill_preserve (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_stroke (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+ _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+ status = cr->dev->backend_parent.stroke (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+ _cairo_path_fixed_fini (&cr->user_path);
+ _cairo_path_fixed_init (&cr->user_path);
+ cr->path_is_rectangle = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_stroke_preserve (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+ _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+ status = cr->dev->backend_parent.stroke_preserve (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_clip (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->dev->backend_parent.clip (abstract_cr);
+ if (unlikely (status))
+ return status;
+
+ _cairo_path_fixed_fini (&cr->user_path);
+ _cairo_path_fixed_init (&cr->user_path);
+ cr->path_is_rectangle = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cogl_context_destroy (void *abstract_cr)
+{
+ cairo_cogl_context_t *cr = abstract_cr;
+
+ _cairo_default_context_fini (&cr->base);
+
+ _cairo_path_fixed_fini (&cr->user_path);
+
+ /* mark the context as invalid to protect against misuse */
+ cr->base.base.status = CAIRO_STATUS_NULL_POINTER;
+ _freed_pool_put (&context_pool, cr);
+}
+
+/* We want to hook into the frontend of the path construction APIs so
+ * we can build up a path description in user coordinates instead of
+ * backend coordinates so that we can recognize user coordinate
+ * rectangles and so we can hash a user path independent of its
+ * transform. (With some care to catch unusual cases where the ctm
+ * changes mid-path) */
+cairo_t *
+_cairo_cogl_context_create (void *target)
+{
+ cairo_cogl_surface_t *surface = target;
+ cairo_cogl_context_t *cr;
+ cairo_status_t status;
+
+ cr = _freed_pool_get (&context_pool);
+ if (unlikely (cr == NULL)) {
+ cr = malloc (sizeof (cairo_cogl_context_t));
+ if (unlikely (cr == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ status = _cairo_default_context_init (&cr->base, target);
+ if (unlikely (status)) {
+ _freed_pool_put (&context_pool, cr);
+ return _cairo_create_in_error (status);
+ }
+
+ cr->dev = (cairo_cogl_device_t *)surface->base.device;
+
+ if (unlikely (cr->dev->backend_vtable_initialized == FALSE)) {
+ cairo_backend_t *backend = &cr->dev->backend;
+ memcpy (backend, cr->base.base.backend, sizeof (cairo_backend_t));
+ memcpy (&cr->dev->backend_parent, cr->base.base.backend, sizeof (cairo_backend_t));
+
+ backend->destroy = _cairo_cogl_context_destroy;
+
+ backend->restore = _cairo_cogl_context_restore;
+ backend->translate = _cairo_cogl_context_translate;
+ backend->scale = _cairo_cogl_context_scale;
+ backend->rotate = _cairo_cogl_context_rotate;
+ backend->transform = _cairo_cogl_context_transform;
+ backend->set_matrix = _cairo_cogl_context_set_matrix;
+ backend->set_identity_matrix = _cairo_cogl_context_set_identity_matrix;
+
+ backend->new_path = _cairo_cogl_context_new_path;
+ backend->new_sub_path = _cairo_cogl_context_new_sub_path;
+ backend->move_to = _cairo_cogl_context_move_to;
+ backend->rel_move_to = _cairo_cogl_context_rel_move_to;
+ backend->line_to = _cairo_cogl_context_line_to;
+ backend->rel_line_to = _cairo_cogl_context_rel_line_to;
+ backend->curve_to = _cairo_cogl_context_curve_to;
+ backend->rel_curve_to = _cairo_cogl_context_rel_curve_to;
+#if 0
+ backend->arc_to = _cairo_cogl_context_arc_to;
+ backend->rel_arc_to = _cairo_cogl_context_rel_arc_to;
+#endif
+ backend->close_path = _cairo_cogl_context_close_path;
+ //backend->arc = _cairo_cogl_context_arc;
+ backend->rectangle = _cairo_cogl_context_rectangle;
+
+ /* Try to automatically catch if any new path APIs are added that mean
+ * we may need to overload more functions... */
+ assert (((char *)&backend->path_extents - (char *)&backend->device_to_user_distance)
+ == (sizeof (void *) * 14));
+
+ backend->fill = _cairo_cogl_context_fill;
+ backend->fill_preserve = _cairo_cogl_context_fill_preserve;
+ backend->stroke = _cairo_cogl_context_stroke;
+ backend->stroke_preserve = _cairo_cogl_context_stroke_preserve;
+ backend->clip = _cairo_cogl_context_clip;
+
+ cr->dev->backend_vtable_initialized = TRUE;
+ }
+
+ cr->base.base.backend = &cr->dev->backend;
+
+ _cairo_path_fixed_init (&cr->user_path);
+ cr->path_is_rectangle = FALSE;
+
+ return &cr->base.base;
+}
diff --git a/src/cairo-cogl-gradient-private.h b/src/cairo-cogl-gradient-private.h
new file mode 100755
index 000000000..fa684d2c6
--- /dev/null
+++ b/src/cairo-cogl-gradient-private.h
@@ -0,0 +1,89 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_GRADIENT_PRIVATE_H
+#define CAIRO_COGL_GRADIENT_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-pattern-private.h"
+
+#include <cogl/cogl2-experimental.h>
+
+#define CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE (1024 * 1024)
+
+typedef enum _cairo_cogl_gradient_compatibility {
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD = 1<<0,
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT = 1<<1,
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT = 1<<2,
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE = 1<<3
+} cairo_cogl_gradient_compatibility_t;
+#define CAIRO_COGL_GRADIENT_CAN_EXTEND_ALL (CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD |\
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT|\
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT|\
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE)
+
+typedef struct _cairo_cogl_linear_texture_entry {
+ cairo_cogl_gradient_compatibility_t compatibility;
+ CoglTexture *texture;
+ float translate_x;
+ float scale_x;
+} cairo_cogl_linear_texture_entry_t;
+
+typedef struct _cairo_cogl_linear_gradient {
+ cairo_cache_entry_t cache_entry;
+ cairo_reference_count_t ref_count;
+ GList *textures;
+ int n_stops;
+ const cairo_gradient_stop_t *stops;
+ cairo_gradient_stop_t stops_embedded[1];
+} cairo_cogl_linear_gradient_t;
+
+cairo_int_status_t
+_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *context,
+ cairo_extend_t extend_mode,
+ int n_stops,
+ const cairo_gradient_stop_t *stops,
+ cairo_cogl_linear_gradient_t **gradient_out);
+
+cairo_cogl_linear_texture_entry_t *
+_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
+ cairo_extend_t extend_mode);
+
+cairo_cogl_linear_gradient_t *
+_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient);
+
+void
+_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient);
+
+cairo_bool_t
+_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b);
+
+#endif /* CAIRO_COGL_GRADIENT_PRIVATE_H */
diff --git a/src/cairo-cogl-gradient.c b/src/cairo-cogl-gradient.c
new file mode 100755
index 000000000..f8c800416
--- /dev/null
+++ b/src/cairo-cogl-gradient.c
@@ -0,0 +1,642 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+//#include "cairoint.h"
+
+#include "cairo-cogl-private.h"
+#include "cairo-cogl-gradient-private.h"
+#include "cairo-image-surface-private.h"
+
+#include <cogl/cogl2-experimental.h>
+#include <glib.h>
+
+#define DUMP_GRADIENTS_TO_PNG
+
+static unsigned long
+_cairo_cogl_linear_gradient_hash (unsigned int n_stops,
+ const cairo_gradient_stop_t *stops)
+{
+ return _cairo_hash_bytes (n_stops, stops,
+ sizeof (cairo_gradient_stop_t) * n_stops);
+}
+
+static cairo_cogl_linear_gradient_t *
+_cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t *ctx,
+ unsigned long hash,
+ unsigned int n_stops,
+ const cairo_gradient_stop_t *stops)
+{
+ cairo_cogl_linear_gradient_t lookup;
+
+ lookup.cache_entry.hash = hash,
+ lookup.n_stops = n_stops;
+ lookup.stops = stops;
+
+ return _cairo_cache_lookup (&ctx->linear_cache, &lookup.cache_entry);
+}
+
+cairo_bool_t
+_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b)
+{
+ const cairo_cogl_linear_gradient_t *a = key_a;
+ const cairo_cogl_linear_gradient_t *b = key_b;
+
+ if (a->n_stops != b->n_stops)
+ return FALSE;
+
+ return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
+}
+
+cairo_cogl_linear_gradient_t *
+_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+ _cairo_reference_count_inc (&gradient->ref_count);
+
+ return gradient;
+}
+
+void
+_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient)
+{
+ GList *l;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
+ return;
+
+ for (l = gradient->textures; l; l = l->next) {
+ cairo_cogl_linear_texture_entry_t *entry = l->data;
+ cogl_object_unref (entry->texture);
+ free (entry);
+ }
+ g_list_free (gradient->textures);
+
+ free (gradient);
+}
+
+static int
+_cairo_cogl_util_next_p2 (int a)
+{
+ int rval = 1;
+
+ while (rval < a)
+ rval <<= 1;
+
+ return rval;
+}
+
+static float
+get_max_color_component_range (const cairo_color_stop_t *color0, const cairo_color_stop_t *color1)
+{
+ float range;
+ float max = 0;
+
+ range = fabs (color0->red - color1->red);
+ max = MAX (range, max);
+ range = fabs (color0->green - color1->green);
+ max = MAX (range, max);
+ range = fabs (color0->blue - color1->blue);
+ max = MAX (range, max);
+ range = fabs (color0->alpha - color1->alpha);
+ max = MAX (range, max);
+
+ return max;
+}
+
+static int
+_cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t extend,
+ unsigned int n_stops,
+ const cairo_gradient_stop_t *stops)
+{
+ unsigned int n;
+ float max_texels_per_unit_offset = 0;
+ float total_offset_range;
+
+ /* Find the stop pair demanding the most precision because we are
+ * interpolating the largest color-component range.
+ *
+ * From that we can define the relative sizes of all the other
+ * stop pairs within our texture and thus the overall size.
+ *
+ * To determine the maximum number of texels for a given gap we
+ * look at the range of colors we are expected to interpolate (so
+ * long as the stop offsets are not degenerate) and we simply
+ * assume we want one texel for each unique color value possible
+ * for a one byte-per-component representation.
+ * XXX: maybe this is overkill and just allowing 128 levels
+ * instead of 256 would be enough and then we'd rely on the
+ * bilinear filtering to give the full range.
+ *
+ * XXX: potentially we could try and map offsets to pixels to come
+ * up with a more precise mapping, but we are aiming to cache
+ * the gradients so we can't make assumptions about how it will be
+ * scaled in the future.
+ */
+ for (n = 1; n < n_stops; n++) {
+ float color_range;
+ float offset_range;
+ float texels;
+ float texels_per_unit_offset;
+
+ /* note: degenerate stops don't need to be represented in the
+ * texture but we want to be sure that solid gaps get at least
+ * one texel and all other gaps get at least 2 texels.
+ */
+
+ if (stops[n].offset == stops[n-1].offset)
+ continue;
+
+ color_range = get_max_color_component_range (&stops[n].color, &stops[n-1].color);
+ if (color_range == 0)
+ texels = 1;
+ else
+ texels = MAX (2, 256.0f * color_range);
+
+ /* So how many texels would we need to map over the full [0,1]
+ * gradient range so this gap would have enough texels? ... */
+ offset_range = stops[n].offset - stops[n - 1].offset;
+ texels_per_unit_offset = texels / offset_range;
+
+ if (texels_per_unit_offset > max_texels_per_unit_offset)
+ max_texels_per_unit_offset = texels_per_unit_offset;
+ }
+
+ total_offset_range = fabs (stops[n_stops - 1].offset - stops[0].offset);
+ return max_texels_per_unit_offset * total_offset_range;
+}
+
+/* Aim to create gradient textures without an alpha component so we can avoid
+ * needing to use blending... */
+static CoglPixelFormat
+_cairo_cogl_linear_gradient_format_for_stops (cairo_extend_t extend,
+ unsigned int n_stops,
+ const cairo_gradient_stop_t *stops)
+{
+ unsigned int n;
+
+ /* We have to add extra transparent texels to the end of the gradient to
+ * handle CAIRO_EXTEND_NONE... */
+ if (extend == CAIRO_EXTEND_NONE)
+ return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+
+ for (n = 1; n < n_stops; n++) {
+ if (stops[n].color.alpha != 1.0)
+ return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+ }
+
+ return COGL_PIXEL_FORMAT_BGR_888;
+}
+
+static cairo_cogl_gradient_compatibility_t
+_cairo_cogl_compatibility_from_extend_mode (cairo_extend_t extend_mode)
+{
+ switch (extend_mode)
+ {
+ case CAIRO_EXTEND_NONE:
+ return CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE;
+ case CAIRO_EXTEND_PAD:
+ return CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD;
+ case CAIRO_EXTEND_REPEAT:
+ return CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
+ case CAIRO_EXTEND_REFLECT:
+ return CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
+ }
+
+ assert (0); /* not reached */
+ return CAIRO_EXTEND_NONE;
+}
+
+cairo_cogl_linear_texture_entry_t *
+_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
+ cairo_extend_t extend_mode)
+{
+ GList *l;
+ cairo_cogl_gradient_compatibility_t compatibility =
+ _cairo_cogl_compatibility_from_extend_mode (extend_mode);
+ for (l = gradient->textures; l; l = l->next) {
+ cairo_cogl_linear_texture_entry_t *entry = l->data;
+ if (entry->compatibility & compatibility)
+ return entry;
+ }
+ return NULL;
+}
+
+static void
+color_stop_lerp (const cairo_color_stop_t *c0,
+ const cairo_color_stop_t *c1,
+ float factor,
+ cairo_color_stop_t *dest)
+{
+ /* NB: we always ignore the short members in this file so we don't need to
+ * worry about initializing them here. */
+ dest->red = c0->red * (1.0f-factor) + c1->red * factor;
+ dest->green = c0->green * (1.0f-factor) + c1->green * factor;
+ dest->blue = c0->blue * (1.0f-factor) + c1->blue * factor;
+ dest->alpha = c0->alpha * (1.0f-factor) + c1->alpha * factor;
+}
+
+static size_t
+_cairo_cogl_linear_gradient_size (cairo_cogl_linear_gradient_t *gradient)
+{
+ GList *l;
+ size_t size = 0;
+ for (l = gradient->textures; l; l = l->next) {
+ cairo_cogl_linear_texture_entry_t *entry = l->data;
+ size += cogl_texture_get_width (entry->texture) * 4;
+ }
+ return size;
+}
+
+static void
+emit_stop (CoglVertexP2C4 **position,
+ float left,
+ float right,
+ const cairo_color_stop_t *left_color,
+ const cairo_color_stop_t *right_color)
+{
+ CoglVertexP2C4 *p = *position;
+
+ guint8 lr = left_color->red * 255;
+ guint8 lg = left_color->green * 255;
+ guint8 lb = left_color->blue * 255;
+ guint8 la = left_color->alpha * 255;
+
+ guint8 rr = right_color->red * 255;
+ guint8 rg = right_color->green * 255;
+ guint8 rb = right_color->blue * 255;
+ guint8 ra = right_color->alpha * 255;
+
+ p[0].x = left;
+ p[0].y = 0;
+ p[0].r = lr; p[0].g = lg; p[0].b = lb; p[0].a = la;
+ p[1].x = left;
+ p[1].y = 1;
+ p[1].r = lr; p[1].g = lg; p[1].b = lb; p[1].a = la;
+ p[2].x = right;
+ p[2].y = 1;
+ p[2].r = rr; p[2].g = rg; p[2].b = rb; p[2].a = ra;
+
+ p[3].x = left;
+ p[3].y = 0;
+ p[3].r = lr; p[3].g = lg; p[3].b = lb; p[3].a = la;
+ p[4].x = right;
+ p[4].y = 1;
+ p[4].r = rr; p[4].g = rg; p[4].b = rb; p[4].a = ra;
+ p[5].x = right;
+ p[5].y = 0;
+ p[5].r = rr; p[5].g = rg; p[5].b = rb; p[5].a = ra;
+
+ *position = &p[6];
+}
+
+#ifdef DUMP_GRADIENTS_TO_PNG
+static void
+dump_gradient_to_png (CoglTexture *texture)
+{
+ cairo_image_surface_t *image = (cairo_image_surface_t *)
+ cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ cogl_texture_get_width (texture),
+ cogl_texture_get_height (texture));
+ CoglPixelFormat format;
+ static int gradient_id = 0;
+ char *gradient_name;
+
+ if (image->base.status)
+ return;
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+#else
+ format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+#endif
+ cogl_texture_get_data (texture,
+ format,
+ 0,
+ image->data);
+ gradient_name = g_strdup_printf ("./gradient%d.png", gradient_id++);
+ g_print ("writing gradient: %s\n", gradient_name);
+ cairo_surface_write_to_png ((cairo_surface_t *)image, gradient_name);
+ g_free (gradient_name);
+}
+#endif
+
+cairo_int_status_t
+_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
+ cairo_extend_t extend_mode,
+ int n_stops,
+ const cairo_gradient_stop_t *stops,
+ cairo_cogl_linear_gradient_t **gradient_out)
+{
+ unsigned long hash;
+ cairo_cogl_linear_gradient_t *gradient;
+ cairo_cogl_linear_texture_entry_t *entry;
+ cairo_gradient_stop_t *internal_stops;
+ int stop_offset;
+ int n_internal_stops;
+ int n;
+ cairo_cogl_gradient_compatibility_t compatibilities;
+ int width;
+ int left_padding = 0;
+ cairo_color_stop_t left_padding_color;
+ int right_padding = 0;
+ cairo_color_stop_t right_padding_color;
+ CoglPixelFormat format;
+ CoglTexture2D *tex;
+ GError *error = NULL;
+ int un_padded_width;
+ CoglHandle offscreen;
+ cairo_int_status_t status;
+ int n_quads;
+ int n_vertices;
+ float prev;
+ float right;
+ CoglVertexP2C4 *vertices;
+ CoglVertexP2C4 *p;
+ CoglPrimitive *prim;
+
+ hash = _cairo_cogl_linear_gradient_hash (n_stops, stops);
+
+ gradient = _cairo_cogl_linear_gradient_lookup (device, hash, n_stops, stops);
+ if (gradient) {
+ cairo_cogl_linear_texture_entry_t *entry =
+ _cairo_cogl_linear_gradient_texture_for_extend (gradient, extend_mode);
+ if (entry) {
+ *gradient_out = _cairo_cogl_linear_gradient_reference (gradient);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+ }
+
+ if (!gradient) {
+ gradient = malloc (sizeof (cairo_cogl_linear_gradient_t) +
+ sizeof (cairo_gradient_stop_t) * (n_stops - 1));
+ if (!gradient)
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
+ /* NB: we update the cache_entry size at the end before
+ * [re]adding it to the cache. */
+ gradient->cache_entry.hash = hash;
+ gradient->textures = NULL;
+ gradient->n_stops = n_stops;
+ gradient->stops = gradient->stops_embedded;
+ memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops);
+ } else
+ _cairo_cogl_linear_gradient_reference (gradient);
+
+ entry = malloc (sizeof (cairo_cogl_linear_texture_entry_t));
+ if (!entry) {
+ status = CAIRO_INT_STATUS_NO_MEMORY;
+ goto BAIL;
+ }
+
+ compatibilities = _cairo_cogl_compatibility_from_extend_mode (extend_mode);
+
+ n_internal_stops = n_stops;
+ stop_offset = 0;
+
+ /* We really need stops covering the full [0,1] range for repeat/reflect
+ * if we want to use sampler REPEAT/MIRROR wrap modes so we may need
+ * to add some extra stops... */
+ if (extend_mode == CAIRO_EXTEND_REPEAT || extend_mode == CAIRO_EXTEND_REFLECT)
+ {
+ /* If we don't need any extra stops then actually the texture
+ * will be shareable for repeat and reflect... */
+ compatibilities = (CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT |
+ CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT);
+
+ if (stops[0].offset != 0) {
+ n_internal_stops++;
+ stop_offset++;
+ }
+
+ if (stops[n_stops - 1].offset != 1)
+ n_internal_stops++;
+ }
+
+ internal_stops = alloca (n_internal_stops * sizeof (cairo_gradient_stop_t));
+ memcpy (&internal_stops[stop_offset], stops, sizeof (cairo_gradient_stop_t) * n_stops);
+
+ /* cairo_color_stop_t values are all unpremultiplied but we need to
+ * interpolate premultiplied colors so we premultiply all the double
+ * components now. (skipping any extra stops added for repeat/reflect)
+ *
+ * Anothing thing to note is that by premultiplying the colors
+ * early we'll also reduce the range of colors to interpolate
+ * which can result in smaller gradient textures.
+ */
+ for (n = stop_offset; n < n_stops; n++) {
+ cairo_color_stop_t *color = &internal_stops[n].color;
+ color->red *= color->alpha;
+ color->green *= color->alpha;
+ color->blue *= color->alpha;
+ }
+
+ if (n_internal_stops != n_stops)
+ {
+ if (extend_mode == CAIRO_EXTEND_REPEAT) {
+ compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
+ if (stops[0].offset != 0) {
+ /* what's the wrap-around distance between the user's end-stops? */
+ double dx = (1.0 - stops[n_stops - 1].offset) + stops[0].offset;
+ internal_stops[0].offset = 0;
+ color_stop_lerp (&stops[0].color,
+ &stops[n_stops - 1].color,
+ stops[0].offset / dx,
+ &internal_stops[0].color);
+ }
+ if (stops[n_stops - 1].offset != 1) {
+ internal_stops[n_internal_stops - 1].offset = 1;
+ internal_stops[n_internal_stops - 1].color = internal_stops[0].color;
+ }
+ } else if (extend_mode == CAIRO_EXTEND_REFLECT) {
+ compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
+ if (stops[0].offset != 0) {
+ internal_stops[0].offset = 0;
+ internal_stops[0].color = stops[n_stops - 1].color;
+ }
+ if (stops[n_stops - 1].offset != 1) {
+ internal_stops[n_internal_stops - 1].offset = 1;
+ internal_stops[n_internal_stops - 1].color = stops[0].color;
+ }
+ }
+ }
+
+ stops = internal_stops;
+ n_stops = n_internal_stops;
+
+ width = _cairo_cogl_linear_gradient_width_for_stops (extend_mode, n_stops, stops);
+
+ if (extend_mode == CAIRO_EXTEND_PAD) {
+
+ /* Here we need to guarantee that the edge texels of our
+ * texture correspond to the desired padding color so we
+ * can use CLAMP_TO_EDGE.
+ *
+ * For short stop-gaps and especially for degenerate stops
+ * it's possible that without special consideration the
+ * user's end stop colors would not be present in our final
+ * texture.
+ *
+ * To handle this we forcibly add two extra padding texels
+ * at the edges which extend beyond the [0,1] range of the
+ * gradient itself and we will later report a translate and
+ * scale transform to compensate for this.
+ */
+
+ /* XXX: If we consider generating a mipmap for our 1d texture
+ * at some point then we also need to consider how much
+ * padding to add to be sure lower mipmap levels still have
+ * the desired edge color (as opposed to a linear blend with
+ * other colors of the gradient).
+ */
+
+ left_padding = 1;
+ left_padding_color = stops[0].color;
+ right_padding = 1;
+ right_padding_color = stops[n_stops - 1].color;
+ } else if (extend_mode == CAIRO_EXTEND_NONE) {
+ /* We handle EXTEND_NONE by adding two extra, transparent, texels at
+ * the ends of the texture and use CLAMP_TO_EDGE.
+ *
+ * We add a scale and translate transform so to account for our texels
+ * extending beyond the [0,1] range. */
+
+ left_padding = 1;
+ left_padding_color.red = 0;
+ left_padding_color.green = 0;
+ left_padding_color.blue = 0;
+ left_padding_color.alpha = 0;
+ right_padding = 1;
+ right_padding_color = left_padding_color;
+ }
+
+ /* If we still have stops that don't cover the full [0,1] range
+ * then we need to define a texture-coordinate scale + translate
+ * transform to account for that... */
+ if (stops[n_stops - 1].offset - stops[0].offset < 1) {
+ float range = stops[n_stops - 1].offset - stops[0].offset;
+ entry->scale_x = 1.0 / range;
+ entry->translate_x = -(stops[0].offset * entry->scale_x);
+ }
+
+ width += left_padding + right_padding;
+
+ width = _cairo_cogl_util_next_p2 (width);
+ width = MIN (4096, width); /* lets not go too stupidly big! */
+ format = _cairo_cogl_linear_gradient_format_for_stops (extend_mode, n_stops, stops);
+
+ do {
+ tex = cogl_texture_2d_new_with_size (device->cogl_context,
+ width,
+ 1,
+ format,
+ &error);
+ if (!tex)
+ g_error_free (error);
+ } while (tex == NULL && width >> 1);
+
+ if (!tex) {
+ status = CAIRO_INT_STATUS_NO_MEMORY;
+ goto BAIL;
+ }
+
+ entry->texture = COGL_TEXTURE (tex);
+ entry->compatibility = compatibilities;
+
+ un_padded_width = width - left_padding - right_padding;
+
+ /* XXX: only when we know the final texture width can we calculate the
+ * scale and translate factors needed to account for padding... */
+ if (un_padded_width != width)
+ entry->scale_x *= (float)un_padded_width / (float)width;
+ if (left_padding)
+ entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding;
+
+ offscreen = cogl_offscreen_new_to_texture (tex);
+ cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
+ cogl_ortho (0, width, 1, 0, -1, 100);
+ cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
+ COGL_BUFFER_BIT_COLOR,
+ 0, 0, 0, 0);
+
+ n_quads = n_stops - 1 + !!left_padding + !!right_padding;
+ n_vertices = 6 * n_quads;
+ vertices = alloca (sizeof (CoglVertexP2C4) * n_vertices);
+ p = vertices;
+ if (left_padding)
+ emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color);
+ prev = (float)left_padding;
+ for (n = 1; n < n_stops; n++) {
+ right = (float)left_padding + (float)un_padded_width * stops[n].offset;
+ emit_stop (&p, prev, right, &stops[n-1].color, &stops[n].color);
+ prev = right;
+ }
+ if (right_padding)
+ emit_stop (&p, prev, width, &right_padding_color, &right_padding_color);
+
+ prim = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES,
+ n_vertices,
+ vertices);
+ /* Just use this as the simplest way to setup a default pipeline... */
+ cogl_set_source_color4f (0, 0, 0, 0);
+ cogl_primitive_draw (prim);
+ cogl_object_unref (prim);
+
+ cogl_pop_framebuffer ();
+ cogl_object_unref (offscreen);
+
+ gradient->textures = g_list_prepend (gradient->textures, entry);
+ gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient);
+
+#ifdef DUMP_GRADIENTS_TO_PNG
+ dump_gradient_to_png (COGL_TEXTURE (tex));
+#endif
+
+#warning "FIXME:"
+ /* XXX: it seems the documentation of _cairo_cache_insert isn't true - it
+ * doesn't handle re-adding the same entry gracefully - the cache will
+ * just keep on growing and then it will start randomly evicting things
+ * pointlessly */
+ /* we ignore errors here and just return an uncached gradient */
+ if (likely (! _cairo_cache_insert (&device->linear_cache, &gradient->cache_entry)))
+ _cairo_cogl_linear_gradient_reference (gradient);
+
+ *gradient_out = gradient;
+ return CAIRO_INT_STATUS_SUCCESS;
+
+BAIL:
+ free (entry);
+ if (gradient)
+ _cairo_cogl_linear_gradient_destroy (gradient);
+ return status;
+}
diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
new file mode 100755
index 000000000..13fe5a8dc
--- /dev/null
+++ b/src/cairo-cogl-private.h
@@ -0,0 +1,164 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_PRIVATE_H
+#define CAIRO_COGL_PRIVATE_H
+
+#include "cairo-device-private.h"
+#include "cairo-cache-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-surface-private.h"
+
+#include <cogl/cogl2-experimental.h>
+
+typedef enum _cairo_cogl_template_type {
+ CAIRO_COGL_TEMPLATE_TYPE_SOLID,
+ CAIRO_COGL_TEMPLATE_TYPE_TEXTURE,
+ CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID,
+ CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE,
+ CAIRO_COGL_TEMPLATE_TYPE_COUNT
+} cairo_cogl_template_type;
+
+typedef struct _cairo_cogl_device {
+ cairo_device_t base;
+
+ cairo_bool_t backend_vtable_initialized;
+ cairo_backend_t backend;
+
+ /* We save a copy of all the original backend methods that we override so
+ * we can chain up...
+ */
+ cairo_backend_t backend_parent;
+
+ CoglContext *cogl_context;
+
+ CoglTexture *dummy_texture;
+
+ /* This is a sparsely filled set of templates because we don't support
+ * the full range of operators that cairo has. All entries corresponding
+ * to unsupported operators are NULL.
+ *
+ * The CAIRO_OPERATOR_ADD is the operator enum with the highest value that
+ * we support so we at least cap the size of the array by that.
+ *
+ * For each operator we have a template for when we have a solid source
+ * and another for each texture format that could be used as a source.
+ */
+ CoglPipeline *template_pipelines[CAIRO_OPERATOR_ADD + 1][CAIRO_COGL_TEMPLATE_TYPE_COUNT];
+
+ CoglMatrix identity;
+
+ /* Caches 1d linear gradient textures */
+ cairo_cache_t linear_cache;
+
+ cairo_cache_t path_fill_staging_cache;
+ cairo_cache_t path_fill_prim_cache;
+ cairo_cache_t path_stroke_staging_cache;
+ cairo_cache_t path_stroke_prim_cache;
+} cairo_cogl_device_t;
+
+typedef struct _cairo_cogl_clip_primitives {
+ cairo_t *clip;
+ CoglPrimitive **primitives;
+} cairo_cogl_clip_primitives_t;
+
+typedef struct _cairo_cogl_surface {
+ cairo_surface_t base;
+
+ CoglPixelFormat cogl_format;
+ cairo_bool_t ignore_alpha;
+
+ /* We currently have 3 basic kinds of Cogl surfaces:
+ * 1) A light surface simply wrapping a CoglTexture
+ * 2) A CoglOffscreen framebuffer that implicitly also wraps a CoglTexture
+ * 3) A CoglOnscreen framebuffer which could potentially be mapped to
+ * a CoglTexture (e.g. via tfp on X11) but we don't currently do
+ * that.
+ */
+
+ CoglTexture *texture;
+ CoglFramebuffer *framebuffer;
+
+ int width;
+ int height;
+
+ GQueue *journal;
+
+ CoglAttributeBuffer *buffer_stack;
+ size_t buffer_stack_size;
+ size_t buffer_stack_offset;
+ guint8 *buffer_stack_pointer;
+
+ cairo_clip_t *last_clip;
+
+ /* A small fifo of recently used cairo_clip_ts paired with CoglPrimitives
+ * that can be used to mask the stencil buffer. */
+ GList *clips_fifo;
+
+ int n_clip_updates_per_frame;
+
+ /* Since the surface backend drawing operator functions don't get
+ * passed the current cairo_t context we don't have a good way
+ * to get our user-coordinates path into our surface_fill function.
+ *
+ * For now we use our _cairo_cogl_context_fill() wrapper to set this
+ * side band data on the surface...
+ */
+ cairo_path_fixed_t *user_path;
+ cairo_matrix_t *ctm;
+ cairo_matrix_t *ctm_inverse;
+ cairo_bool_t path_is_rectangle;
+ double path_rectangle_x;
+ double path_rectangle_y;
+ double path_rectangle_width;
+ double path_rectangle_height;
+} cairo_cogl_surface_t;
+
+cairo_status_t
+_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y,
+ cairo_fixed_t width,
+ cairo_fixed_t height);
+
+cairo_int_status_t
+_cairo_cogl_surface_fill_rectangle (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ double x,
+ double y,
+ double width,
+ double height,
+ cairo_matrix_t *ctm,
+ const cairo_clip_t *clip);
+
+#endif /* CAIRO_COGL_PRIVATE_H */
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
new file mode 100755
index 000000000..27c3676de
--- /dev/null
+++ b/src/cairo-cogl-surface.c
@@ -0,0 +1,2805 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+#include "cairoint.h"
+
+#include "cairo-cache-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-fixed-private.h"
+#include "cairo-device-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-cogl-private.h"
+#include "cairo-cogl-gradient-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-cogl-context-private.h"
+#include "cairo-cogl-utils-private.h"
+#include "cairo-box-inline.h"
+#include "cairo-surface-subsurface-inline.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-surface-offset-private.h"
+
+#include "cairo-cogl.h"
+
+#include <cogl/cogl2-experimental.h>
+#include <glib.h>
+
+#define CAIRO_COGL_DEBUG 0
+//#define FILL_WITH_COGL_PATH
+//#define USE_CAIRO_PATH_FLATTENER
+#define ENABLE_PATH_CACHE
+//#define DISABLE_BATCHING
+#define USE_COGL_RECTANGLE_API
+#define ENABLE_RECTANGLES_FASTPATH
+
+#if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE)
+#define NEED_COGL_CONTEXT
+#endif
+
+#if CAIRO_COGL_DEBUG && __GNUC__
+#define UNSUPPORTED(reason) ({ \
+ g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \
+ CAIRO_INT_STATUS_UNSUPPORTED; \
+})
+#else
+#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
+#endif
+
+#define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024)
+
+typedef struct _cairo_cogl_texture_attributes {
+ /* nabbed from cairo_surface_attributes_t... */
+ cairo_matrix_t matrix;
+ cairo_extend_t extend;
+ cairo_filter_t filter;
+ cairo_bool_t has_component_alpha;
+
+ CoglPipelineWrapMode s_wrap;
+ CoglPipelineWrapMode t_wrap;
+} cairo_cogl_texture_attributes_t;
+
+typedef enum _cairo_cogl_journal_entry_type {
+ CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE,
+ CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE,
+ CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH,
+ CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP
+} cairo_cogl_journal_entry_type_t;
+
+typedef struct _cairo_cogl_journal_entry {
+ cairo_cogl_journal_entry_type_t type;
+} cairo_cogl_journal_entry_t;
+
+typedef struct _cairo_cogl_journal_clip_entry {
+ cairo_cogl_journal_entry_t base;
+ cairo_clip_t *clip;
+} cairo_cogl_journal_clip_entry_t;
+
+typedef struct _cairo_cogl_journal_rect_entry {
+ cairo_cogl_journal_entry_t base;
+ CoglPipeline *pipeline;
+ float x;
+ float y;
+ float width;
+ float height;
+ int n_layers;
+ cairo_matrix_t ctm;
+} cairo_cogl_journal_rect_entry_t;
+
+typedef struct _cairo_cogl_journal_prim_entry {
+ cairo_cogl_journal_entry_t base;
+ CoglPipeline *pipeline;
+ CoglPrimitive *primitive;
+ gboolean has_transform;
+ cairo_matrix_t transform;
+} cairo_cogl_journal_prim_entry_t;
+
+typedef struct _cairo_cogl_journal_path_entry {
+ cairo_cogl_journal_entry_t base;
+ CoglPipeline *pipeline;
+ CoglPath *path;
+} cairo_cogl_journal_path_entry_t;
+
+typedef struct _cairo_cogl_path_fill_meta {
+ cairo_cache_entry_t cache_entry;
+ cairo_reference_count_t ref_count;
+ int counter;
+ cairo_path_fixed_t *user_path;
+ cairo_matrix_t ctm_inverse;
+
+ /* TODO */
+#if 0
+ /* A cached path tessellation should be re-usable with different rotations
+ * and translations but not for different scales.
+ *
+ * one idea is to track the diagonal lenghts of a unit rectangle
+ * transformed through the original ctm use to tesselate the geometry
+ * so we can check what the lengths are for any new ctm to know if
+ * this geometry is compatible.
+ */
+#endif
+
+ CoglPrimitive *prim;
+} cairo_cogl_path_fill_meta_t;
+
+typedef struct _cairo_cogl_path_stroke_meta {
+ cairo_cache_entry_t cache_entry;
+ cairo_reference_count_t ref_count;
+ int counter;
+ cairo_path_fixed_t *user_path;
+ cairo_matrix_t ctm_inverse;
+ cairo_stroke_style_t style;
+ double tolerance;
+
+ /* TODO */
+#if 0
+ /* A cached path tessellation should be re-usable with different rotations
+ * and translations but not for different scales.
+ *
+ * one idea is to track the diagonal lenghts of a unit rectangle
+ * transformed through the original ctm use to tesselate the geometry
+ * so we can check what the lengths are for any new ctm to know if
+ * this geometry is compatible.
+ */
+#endif
+
+ CoglPrimitive *prim;
+} cairo_cogl_path_stroke_meta_t;
+
+static cairo_surface_t *
+_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
+ cairo_bool_t ignore_alpha,
+ CoglFramebuffer *framebuffer,
+ CoglTexture *texture);
+
+static cairo_int_status_t
+_cairo_cogl_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+static void
+_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface);
+
+cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend;
+
+slim_hidden_proto (cairo_cogl_device_create);
+slim_hidden_proto (cairo_cogl_surface_create);
+slim_hidden_proto (cairo_cogl_surface_get_framebuffer);
+slim_hidden_proto (cairo_cogl_surface_get_texture);
+slim_hidden_proto (cairo_cogl_surface_end_frame);
+
+static cairo_cogl_device_t *
+to_device (cairo_device_t *device)
+{
+ return (cairo_cogl_device_t *)device;
+}
+
+/* moves trap points such that they become the actual corners of the trapezoid */
+static void
+_sanitize_trap (cairo_trapezoid_t *t)
+{
+ cairo_trapezoid_t s = *t;
+
+#define FIX(lr, tb, p) \
+ if (t->lr.p.y != t->tb) { \
+ t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \
+ t->lr.p.y = s.tb; \
+ }
+ FIX (left, top, p1);
+ FIX (left, bottom, p2);
+ FIX (right, top, p1);
+ FIX (right, bottom, p2);
+}
+
+static cairo_status_t
+_cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
+{
+ GError *error = NULL;
+
+ if (surface->framebuffer)
+ return CAIRO_STATUS_SUCCESS;
+
+ surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (surface->texture));
+ if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
+ g_error_free (error);
+ cogl_object_unref (surface->framebuffer);
+ surface->framebuffer = NULL;
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+
+ cogl_push_framebuffer (surface->framebuffer);
+ cogl_ortho (0, surface->width,
+ surface->height, 0,
+ -1, 100);
+ cogl_pop_framebuffer ();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_cogl_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_cogl_surface_t *reference_surface = abstract_surface;
+ cairo_cogl_surface_t *surface;
+ CoglTexture *texture;
+ cairo_status_t status;
+
+ texture = cogl_texture_new_with_size (width, height,
+ COGL_TEXTURE_NO_SLICING,
+ (content & CAIRO_CONTENT_COLOR) ?
+ COGL_PIXEL_FORMAT_BGRA_8888_PRE :
+ COGL_PIXEL_FORMAT_A_8);
+ if (!texture)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface = (cairo_cogl_surface_t *)
+ _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
+ (content & CAIRO_CONTENT_ALPHA) == 0,
+ NULL,
+ texture);
+ if (unlikely (surface->base.status))
+ return &surface->base;
+
+ status = _cairo_cogl_surface_ensure_framebuffer (surface);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &surface->base;
+}
+
+static cairo_bool_t
+_cairo_cogl_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_cogl_surface_t *surface = abstract_surface;
+
+ extents->x = 0;
+ extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+
+ return TRUE;
+}
+
+static void
+_cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
+{
+ GList *l;
+
+ for (l = surface->journal->head; l; l = l->next) {
+ cairo_cogl_journal_entry_t *entry = l->data;
+
+ if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE) {
+ cairo_cogl_journal_prim_entry_t *prim_entry =
+ (cairo_cogl_journal_prim_entry_t *)entry;
+ cogl_object_unref (prim_entry->primitive);
+ } else if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH) {
+ cairo_cogl_journal_path_entry_t *path_entry =
+ (cairo_cogl_journal_path_entry_t *)entry;
+ cogl_object_unref (path_entry->path);
+ }
+ }
+
+ g_queue_free (surface->journal);
+ surface->journal = NULL;
+}
+
+#ifdef FILL_WITH_COGL_PATH
+static void
+_cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
+ CoglPipeline *pipeline,
+ CoglPath *path)
+{
+ cairo_cogl_journal_path_entry_t *entry;
+
+ if (unlikely (surface->journal == NULL))
+ surface->journal = g_queue_new ();
+
+ /* FIXME: Instead of a GList here we should stack allocate the journal
+ * entries so it would be cheaper to allocate and they can all be freed in
+ * one go after flushing! */
+ entry = g_slice_new (cairo_cogl_journal_path_entry_t);
+ entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH;
+
+ entry->pipeline = cogl_object_ref (pipeline);
+ entry->path = cogl_object_ref (path);
+
+ g_queue_push_tail (surface->journal, entry);
+
+#ifdef DISABLE_BATCHING
+ _cairo_cogl_journal_flush (surface);
+#endif
+}
+#endif /* FILL_WITH_COGL_PATH */
+
+static void
+_cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
+ CoglPipeline *pipeline,
+ CoglPrimitive *primitive,
+ cairo_matrix_t *transform)
+{
+ cairo_cogl_journal_prim_entry_t *entry;
+
+ if (unlikely (surface->journal == NULL))
+ surface->journal = g_queue_new ();
+
+ /* FIXME: Instead of a GList here we should stack allocate the journal
+ * entries so it would be cheaper to allocate and they can all be freed in
+ * one go after flushing! */
+ entry = g_slice_new (cairo_cogl_journal_prim_entry_t);
+ entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE;
+
+ entry->pipeline = cogl_object_ref (pipeline);
+
+ if (transform) {
+ entry->transform = *transform;
+ entry->has_transform = TRUE;
+ } else
+ entry->has_transform = FALSE;
+
+ entry->primitive = cogl_object_ref (primitive);
+
+ g_queue_push_tail (surface->journal, entry);
+
+#ifdef DISABLE_BATCHING
+ _cairo_cogl_journal_flush (surface);
+#endif
+}
+
+static void
+_cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
+ CoglPipeline *pipeline,
+ float x,
+ float y,
+ float width,
+ float height,
+ int n_layers,
+ cairo_matrix_t *ctm)
+{
+ cairo_cogl_journal_rect_entry_t *entry;
+
+ if (unlikely (surface->journal == NULL))
+ surface->journal = g_queue_new ();
+
+ /* FIXME: Instead of a GList here we should stack allocate the journal
+ * entries so it would be cheaper to allocate and they can all be freed in
+ * one go after flushing! */
+ entry = g_slice_new (cairo_cogl_journal_rect_entry_t);
+ entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE;
+
+ entry->pipeline = cogl_object_ref (pipeline);
+
+ entry->x = x;
+ entry->y = y;
+ entry->width = width;
+ entry->height = height;
+ entry->ctm = *ctm;
+
+ entry->n_layers = n_layers;
+
+ g_queue_push_tail (surface->journal, entry);
+
+#ifdef DISABLE_BATCHING
+ _cairo_cogl_journal_flush (surface);
+#endif
+}
+
+static void
+_cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
+ const cairo_clip_t *clip)
+{
+ cairo_cogl_journal_clip_entry_t *entry;
+
+ if (unlikely (surface->journal == NULL))
+ surface->journal = g_queue_new ();
+
+ /* FIXME: Instead of a GList here we should stack allocate the journal
+ * entries so it would be cheaper to allocate and they can all be freed in
+ * one go after flushing! */
+ entry = g_slice_new (cairo_cogl_journal_clip_entry_t);
+ entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP;
+ entry->clip = _cairo_clip_copy (clip);
+
+ g_queue_push_tail (surface->journal, entry);
+}
+
+static void
+_cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
+{
+ GList *l;
+
+ if (!surface->journal) {
+ assert (surface->last_clip == NULL);
+ return;
+ }
+
+ if (surface->buffer_stack && surface->buffer_stack_offset) {
+ cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
+ cogl_object_unref (surface->buffer_stack);
+ surface->buffer_stack = NULL;
+ }
+
+ for (l = surface->journal->head; l; l = l->next) {
+ cairo_cogl_journal_entry_t *entry = l->data;
+ gsize entry_size;
+
+ switch (entry->type)
+ {
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
+ cairo_cogl_journal_clip_entry_t *clip_entry =
+ (cairo_cogl_journal_clip_entry_t *)entry;
+ _cairo_clip_destroy (clip_entry->clip);
+ entry_size = sizeof (cairo_cogl_journal_clip_entry_t);
+ break;
+ }
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
+ cairo_cogl_journal_rect_entry_t *rect_entry =
+ (cairo_cogl_journal_rect_entry_t *)entry;
+ cogl_object_unref (rect_entry->pipeline);
+ entry_size = sizeof (cairo_cogl_journal_rect_entry_t);
+ break;
+ }
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
+ cairo_cogl_journal_prim_entry_t *prim_entry =
+ (cairo_cogl_journal_prim_entry_t *)entry;
+ cogl_object_unref (prim_entry->pipeline);
+ cogl_object_unref (prim_entry->primitive);
+ entry_size = sizeof (cairo_cogl_journal_prim_entry_t);
+ break;
+ }
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
+ cairo_cogl_journal_path_entry_t *path_entry =
+ (cairo_cogl_journal_path_entry_t *)entry;
+ cogl_object_unref (path_entry->pipeline);
+ cogl_object_unref (path_entry->path);
+ entry_size = sizeof (cairo_cogl_journal_path_entry_t);
+ break;
+ }
+ default:
+ assert (0); /* not reached! */
+ entry_size = 0; /* avoid compiler warning */
+ }
+ g_slice_free1 (entry_size, entry);
+ }
+
+ g_queue_clear (surface->journal);
+
+ if (surface->last_clip) {
+ _cairo_clip_destroy (surface->last_clip);
+ surface->last_clip = NULL;
+ }
+}
+
+static CoglAttributeBuffer *
+_cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface,
+ size_t size,
+ size_t *offset,
+ void **pointer)
+{
+ /* XXX: In the Cogl journal we found it more efficient to have a pool of
+ * buffers that we re-cycle but for now we simply thow away our stack
+ * buffer each time we flush. */
+ if (unlikely (surface->buffer_stack &&
+ (surface->buffer_stack_size - surface->buffer_stack_offset) < size)) {
+ cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
+ cogl_object_unref (surface->buffer_stack);
+ surface->buffer_stack = NULL;
+ surface->buffer_stack_size *= 2;
+ }
+
+ if (unlikely (surface->buffer_stack_size < size))
+ surface->buffer_stack_size = size * 2;
+
+ if (unlikely (surface->buffer_stack == NULL)) {
+ surface->buffer_stack = cogl_attribute_buffer_new (surface->buffer_stack_size, NULL);
+ surface->buffer_stack_pointer =
+ cogl_buffer_map (COGL_BUFFER (surface->buffer_stack),
+ COGL_BUFFER_ACCESS_WRITE,
+ COGL_BUFFER_MAP_HINT_DISCARD);
+ surface->buffer_stack_offset = 0;
+ }
+
+ *pointer = surface->buffer_stack_pointer + surface->buffer_stack_offset;
+ *offset = surface->buffer_stack_offset;
+
+ surface->buffer_stack_offset += size;
+ return cogl_object_ref (surface->buffer_stack);
+}
+
+
+static CoglAttributeBuffer *
+_cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
+ cairo_traps_t *traps,
+ size_t *offset,
+ gboolean one_shot)
+{
+ CoglAttributeBuffer *buffer;
+ int n_traps = traps->num_traps;
+ int i;
+ CoglVertexP2 *triangles;
+
+ if (one_shot) {
+ buffer = _cairo_cogl_surface_allocate_buffer_space (surface,
+ n_traps * sizeof (CoglVertexP2) * 6,
+ offset,
+ (void **)&triangles);
+ if (!buffer)
+ return NULL;
+ } else {
+ buffer = cogl_attribute_buffer_new (n_traps * sizeof (CoglVertexP2) * 6, NULL);
+ if (!buffer)
+ return NULL;
+ triangles = cogl_buffer_map (COGL_BUFFER (buffer),
+ COGL_BUFFER_ACCESS_WRITE,
+ COGL_BUFFER_MAP_HINT_DISCARD);
+ if (!triangles)
+ return NULL;
+ *offset = 0;
+ }
+
+ /* XXX: This is can be very expensive. I'm not sure a.t.m if it's
+ * predominantly the bandwidth required or the cost of the fixed_to_float
+ * conversions but either way we should try using an index buffer to
+ * reduce the amount we upload by 1/3 (offset by allocating and uploading
+ * indices though) sadly though my experience with the intel mesa drivers
+ * is that slow paths can easily be hit when starting to use indices.
+ */
+ for (i = 0; i < n_traps; i++)
+ {
+ CoglVertexP2 *p = &triangles[i * 6];
+ cairo_trapezoid_t *trap = &traps->traps[i];
+
+ p[0].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
+ p[0].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
+
+ p[1].x = _cairo_cogl_util_fixed_to_float (trap->left.p2.x);
+ p[1].y = _cairo_cogl_util_fixed_to_float (trap->left.p2.y);
+
+ p[2].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
+ p[2].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
+
+ p[3].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
+ p[3].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
+
+ p[4].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
+ p[4].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
+
+ p[5].x = _cairo_cogl_util_fixed_to_float (trap->right.p1.x);
+ p[5].y = _cairo_cogl_util_fixed_to_float (trap->right.p1.y);
+ }
+
+ if (!one_shot)
+ cogl_buffer_unmap (COGL_BUFFER (buffer));
+
+ return buffer;
+}
+
+/* Used for solid fills, in this case we just need a mesh made of
+ * a single (2-component) position attribute. */
+static CoglPrimitive *
+_cairo_cogl_traps_to_composite_prim_p2 (cairo_cogl_surface_t *surface,
+ cairo_traps_t *traps,
+ gboolean one_shot)
+{
+ size_t offset;
+ CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
+ CoglAttribute *pos = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (CoglVertexP2),
+ offset,
+ 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ CoglPrimitive *prim;
+
+ /* The attribute will have taken a reference on the buffer */
+ cogl_object_unref (buffer);
+
+ prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
+ traps->num_traps * 6, pos, NULL);
+
+ /* The primitive will now keep the attribute alive... */
+ cogl_object_unref (pos);
+
+ return prim;
+}
+
+/* Used for surface fills, in this case we need a mesh made of a single
+ * (2-component) position attribute + we also alias the same attribute as
+ * (2-component) texture coordinates */
+static CoglPrimitive *
+_cairo_cogl_traps_to_composite_prim_p2t2 (cairo_cogl_surface_t *surface,
+ cairo_traps_t *traps,
+ gboolean one_shot)
+{
+ size_t offset;
+ CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
+ CoglAttribute *pos = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (CoglVertexP2),
+ offset,
+ 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ CoglAttribute *tex_coords = cogl_attribute_new (buffer,
+ "cogl_tex_coord0_in",
+ sizeof (CoglVertexP2),
+ 0,
+ 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ CoglPrimitive *prim;
+
+ /* The attributes will have taken references on the buffer */
+ cogl_object_unref (buffer);
+
+ prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
+ traps->num_traps * 6, pos, tex_coords, NULL);
+
+ /* The primitive will now keep the attributes alive... */
+ cogl_object_unref (pos);
+ cogl_object_unref (tex_coords);
+
+ return prim;
+}
+
+static CoglPrimitive *
+_cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface,
+ cairo_traps_t *traps,
+ int n_layers,
+ gboolean one_shot)
+{
+ int n_traps = traps->num_traps;
+ int i;
+
+ /* XXX: Ideally we would skip tessellating to traps entirely since
+ * given their representation, conversion to triangles is quite expensive.
+ *
+ * This simplifies the conversion to triangles by making the end points of
+ * the two side lines actually just correspond to the corners of the
+ * traps.
+ */
+ for (i = 0; i < n_traps; i++)
+ _sanitize_trap (&traps->traps[i]);
+
+ if (n_layers == 0)
+ return _cairo_cogl_traps_to_composite_prim_p2 (surface, traps, one_shot);
+ else {
+ assert (n_layers == 1);
+ return _cairo_cogl_traps_to_composite_prim_p2t2 (surface, traps, one_shot);
+ }
+}
+
+static cairo_int_status_t
+_cairo_cogl_fill_to_primitive (cairo_cogl_surface_t *surface,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ int n_layers,
+ cairo_bool_t one_shot,
+ CoglPrimitive **primitive,
+ size_t *size)
+{
+ cairo_traps_t traps;
+ cairo_int_status_t status;
+
+ _cairo_traps_init (&traps);
+ status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (traps.num_traps == 0) {
+ status = CAIRO_INT_STATUS_NOTHING_TO_DO;
+ goto BAIL;
+ }
+
+ *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
+
+ *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
+ if (!*primitive) {
+ status = CAIRO_INT_STATUS_NO_MEMORY;
+ goto BAIL;
+ }
+
+BAIL:
+ _cairo_traps_fini (&traps);
+ return status;
+}
+
+static void
+_cairo_cogl_clip_push_box (const cairo_box_t *box)
+{
+ if (_cairo_box_is_pixel_aligned (box)) {
+ cairo_rectangle_int_t rect;
+ _cairo_box_round_to_rectangle (box, &rect);
+ cogl_clip_push_window_rectangle (rect.x, rect.y,
+ rect.width, rect.height);
+ } else {
+ double x1, y1, x2, y2;
+ _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
+ cogl_clip_push_rectangle (x1, y1, x2, y2);
+ }
+}
+
+static void
+_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
+{
+ GList *l;
+ int clip_stack_depth = 0;
+ int i;
+
+ if (!surface->journal)
+ return;
+
+ if (surface->buffer_stack && surface->buffer_stack_offset) {
+ cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
+ cogl_object_unref (surface->buffer_stack);
+ surface->buffer_stack = NULL;
+ }
+
+ cogl_set_framebuffer (surface->framebuffer);
+
+ cogl_push_matrix ();
+
+ for (l = surface->journal->head; l; l = l->next) {
+ cairo_cogl_journal_entry_t *entry = l->data;
+
+ switch (entry->type)
+ {
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
+ cairo_cogl_journal_clip_entry_t *clip_entry =
+ (cairo_cogl_journal_clip_entry_t *)entry;
+ cairo_clip_path_t *path;
+#if 0
+ cairo_bool_t checked_for_primitives = FALSE;
+ cairo_cogl_clip_primitives_t *clip_primitives;
+#endif
+
+ for (i = 0; i < clip_stack_depth; i++)
+ cogl_clip_pop ();
+ clip_stack_depth = 0;
+
+ for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) {
+ cairo_rectangle_int_t extents;
+ cairo_int_status_t status;
+ CoglPrimitive *prim;
+ size_t prim_size;
+
+ _cairo_path_fixed_approximate_clip_extents (&path->path, &extents);
+
+ /* TODO - maintain a fifo of the last 10 used clips with cached
+ * primitives to see if we can avoid tesselating the path and
+ * uploading the vertices...
+ */
+#if 0
+ if (!checked_for_primitives) {
+ clip_primitives = find_clip_primitives (clip);
+ checked_for_primitives = TRUE;
+ }
+ if (clip_primitives)
+ prim = clip_primitives->primitives[i];
+#endif
+ status = _cairo_cogl_fill_to_primitive (surface,
+ &path->path,
+ path->fill_rule,
+ path->tolerance,
+ 0,
+ TRUE,
+ &prim,
+ &prim_size);
+ if (unlikely (status)) {
+ g_warning ("Failed to get primitive for clip path while flushing journal");
+ continue;
+ }
+ clip_stack_depth++;
+ cogl_clip_push_primitive (prim,
+ extents.x, extents.y,
+ extents.x + extents.width,
+ extents.y + extents.height);
+ cogl_object_unref (prim);
+ }
+
+ for (i = 0; i < clip_entry->clip->num_boxes; i++) {
+ clip_stack_depth++;
+ _cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i]);
+ }
+
+ surface->n_clip_updates_per_frame++;
+ break;
+ }
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
+ cairo_cogl_journal_rect_entry_t *rect_entry =
+ (cairo_cogl_journal_rect_entry_t *)entry;
+ float tex_coords[8];
+ float x1 = rect_entry->x;
+ float y1 = rect_entry->y;
+ float x2 = rect_entry->x + rect_entry->width;
+ float y2 = rect_entry->y + rect_entry->height;
+ cairo_matrix_t *ctm = &rect_entry->ctm;
+ float ctmfv[16] = {
+ ctm->xx, ctm->yx, 0, 0,
+ ctm->xy, ctm->yy, 0, 0,
+ 0, 0, 1, 0,
+ ctm->x0, ctm->y0, 0, 1
+ };
+ CoglMatrix transform;
+
+ cogl_matrix_init_from_array (&transform, ctmfv);
+
+ if (rect_entry->n_layers) {
+ g_assert (rect_entry->n_layers <= 2);
+ tex_coords[0] = x1;
+ tex_coords[1] = y1;
+ tex_coords[2] = x2;
+ tex_coords[3] = y2;
+ if (rect_entry->n_layers > 1)
+ memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4);
+ }
+
+ cogl_set_source (rect_entry->pipeline);
+ cogl_push_matrix ();
+ cogl_transform (&transform);
+ cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2,
+ tex_coords, 4 * rect_entry->n_layers);
+ cogl_pop_matrix ();
+ break;
+ }
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
+ cairo_cogl_journal_prim_entry_t *prim_entry =
+ (cairo_cogl_journal_prim_entry_t *)entry;
+ CoglMatrix transform;
+
+ cogl_push_matrix ();
+ if (prim_entry->has_transform) {
+ cairo_matrix_t *ctm = &prim_entry->transform;
+ float ctmfv[16] = {
+ ctm->xx, ctm->yx, 0, 0,
+ ctm->xy, ctm->yy, 0, 0,
+ 0, 0, 1, 0,
+ ctm->x0, ctm->y0, 0, 1
+ };
+ cogl_matrix_init_from_array (&transform, ctmfv);
+ cogl_transform (&transform);
+ } else {
+ cogl_matrix_init_identity (&transform);
+ cogl_set_modelview_matrix (&transform);
+ }
+
+ cogl_set_source (prim_entry->pipeline);
+ cogl_primitive_draw (prim_entry->primitive);
+ cogl_pop_matrix ();
+ break;
+ }
+ case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
+ cairo_cogl_journal_path_entry_t *path_entry =
+ (cairo_cogl_journal_path_entry_t *)entry;
+
+ cogl_set_source (path_entry->pipeline);
+ cogl_path_fill (path_entry->path);
+ break;
+ }
+ default:
+ assert (0); /* not reached! */
+ }
+ }
+
+ cogl_pop_matrix ();
+
+ for (i = 0; i < clip_stack_depth; i++)
+ cogl_clip_pop ();
+
+ _cairo_cogl_journal_discard (surface);
+}
+
+static cairo_status_t
+_cairo_cogl_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_cogl_journal_flush (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_surface_finish (void *abstract_surface)
+{
+ cairo_cogl_surface_t *surface = abstract_surface;
+
+ if (surface->texture)
+ cogl_object_unref (surface->texture);
+
+ if (surface->framebuffer)
+ cogl_object_unref (surface->framebuffer);
+
+ if (surface->journal)
+ _cairo_cogl_journal_free (surface);
+
+ /*XXX wtf */
+ cairo_device_release (surface->base.device);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static CoglPixelFormat
+get_cogl_format_from_cairo_format (cairo_format_t cairo_format);
+
+/* XXX: We often use RGBA format for onscreen framebuffers so make sure
+ * to handle CAIRO_FORMAT_INVALID sensibly */
+static cairo_format_t
+get_cairo_format_from_cogl_format (CoglPixelFormat format)
+{
+ switch ((int)format)
+ {
+ case COGL_PIXEL_FORMAT_A_8:
+ return CAIRO_FORMAT_A8;
+ case COGL_PIXEL_FORMAT_RGB_565:
+ return CAIRO_FORMAT_RGB16_565;
+
+ case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
+ case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
+ case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
+ /* Note: this is ambiguous since CAIRO_FORMAT_RGB24
+ * would also map to the same CoglPixelFormat */
+ return CAIRO_FORMAT_ARGB32;
+
+ default:
+ g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n",
+ format,
+ format & COGL_A_BIT,
+ format & COGL_BGR_BIT,
+ format & COGL_PREMULT_BIT,
+ format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT));
+ return CAIRO_FORMAT_INVALID;
+ }
+}
+
+static CoglPixelFormat
+get_cogl_format_from_cairo_format (cairo_format_t cairo_format)
+{
+ switch (cairo_format)
+ {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+#else
+ return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+#endif
+ case CAIRO_FORMAT_A8:
+ return COGL_PIXEL_FORMAT_A_8;
+ case CAIRO_FORMAT_RGB16_565:
+ return COGL_PIXEL_FORMAT_RGB_565;
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_RGB30:
+ return 0;
+ }
+
+ g_warn_if_reached ();
+ return 0;
+}
+
+static cairo_status_t
+_cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t *surface,
+ cairo_rectangle_int_t *interest,
+ cairo_image_surface_t **image_out)
+{
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ cairo_format_t cairo_format;
+ CoglPixelFormat cogl_format;
+
+ /* TODO: Add cogl_texture_get_region() API so we don't have to ensure the
+ * surface is bound to an fbo to read back pixels */
+ status = _cairo_cogl_surface_ensure_framebuffer (surface);
+ if (unlikely (status))
+ return status;
+
+ cairo_format = get_cairo_format_from_cogl_format (surface->cogl_format);
+ if (cairo_format == CAIRO_FORMAT_INVALID) {
+ cairo_format = CAIRO_FORMAT_ARGB32;
+ cogl_format = get_cogl_format_from_cairo_format (cairo_format);
+ } else {
+ cogl_format = cogl_framebuffer_get_color_format (surface->framebuffer);
+ }
+
+ image = (cairo_image_surface_t *)
+ cairo_image_surface_create (cairo_format, surface->width, surface->height);
+ if (image->base.status)
+ return image->base.status;
+
+ /* TODO: Add cogl_framebuffer_read_pixels() API */
+ cogl_push_framebuffer (surface->framebuffer);
+ cogl_read_pixels (0, 0, surface->width, surface->height,
+ COGL_READ_PIXELS_COLOR_BUFFER,
+ cogl_format,
+ image->data);
+ cogl_pop_framebuffer ();
+
+ *image_out = image;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_cogl_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->texture) {
+ cairo_format_t format = get_cairo_format_from_cogl_format (surface->cogl_format);
+ cairo_image_surface_t *image = (cairo_image_surface_t *)
+ cairo_image_surface_create (format, surface->width, surface->height);
+ if (image->base.status)
+ return image->base.status;
+
+ cogl_texture_get_data (surface->texture,
+ cogl_texture_get_format (surface->texture),
+ 0,
+ image->data);
+
+ image->base.is_clear = FALSE;
+ *image_out = image;
+ } else {
+ cairo_rectangle_int_t extents = {
+ 0, 0, surface->width, surface->height
+ };
+ status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents,
+ image_out);
+ if (unlikely (status))
+ return status;
+ }
+
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cogl_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_cogl_surface_clear (cairo_cogl_surface_t *surface,
+ const cairo_color_t *color)
+{
+ /* Anything batched in the journal up until now is redundant... */
+ _cairo_cogl_journal_discard (surface);
+
+ /* XXX: we currently implicitly clear the depth and stencil buffer here
+ * but since we use the framebuffer_discard extension when available I
+ * suppose this doesn't matter too much.
+ *
+ * The main concern is that we want to avoid re-loading an external z
+ * buffer at the start of each frame, but also many gpu architectures have
+ * optimizations for how they handle the depth/stencil buffers and can get
+ * upset if they aren't cleared together at the start of the frame.
+ *
+ * FIXME: we need a way to assert that the clip stack currently isn't
+ * using the stencil buffer before clearing it here!
+ */
+ cogl_framebuffer_clear4f (surface->framebuffer,
+ COGL_BUFFER_BIT_COLOR |
+ COGL_BUFFER_BIT_DEPTH |
+ COGL_BUFFER_BIT_STENCIL,
+ color->red * color->alpha,
+ color->green * color->alpha,
+ color->blue * color->alpha,
+ color->alpha);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y,
+ cairo_fixed_t width,
+ cairo_fixed_t height)
+{
+ cairo_status_t status;
+
+ status = _cairo_path_fixed_move_to (path, x, y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_rel_line_to (path, width, 0);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_rel_line_to (path, 0, height);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_rel_line_to (path, -width, 0);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_close_path (path);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_cogl_surface_t *surface;
+ cairo_path_fixed_t path;
+ cairo_status_t status;
+ cairo_matrix_t identity;
+
+ if (clip == NULL) {
+ if (op == CAIRO_OPERATOR_CLEAR)
+ return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
+ else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ (op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) {
+ return _cairo_cogl_surface_clear (abstract_surface,
+ &((cairo_solid_pattern_t *) source)->color);
+ }
+ }
+
+ /* fallback to handling the paint in terms of a fill... */
+
+ surface = abstract_surface;
+
+ _cairo_path_fixed_init (&path);
+
+ status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0, surface->width, surface->height);
+ if (unlikely (status))
+ goto BAIL;
+
+#ifdef NEED_COGL_CONTEXT
+ /* XXX: in cairo-cogl-context.c we set some sideband data on the
+ * surface before issuing a fill so we need to do that here too... */
+ surface->user_path = &path;
+ cairo_matrix_init_identity (&identity);
+ surface->ctm = &identity;
+ surface->ctm_inverse = &identity;
+ surface->path_is_rectangle = TRUE;
+ surface->path_rectangle_x = 0;
+ surface->path_rectangle_y = 0;
+ surface->path_rectangle_width = surface->width;
+ surface->path_rectangle_height = surface->height;
+#endif
+
+ status = _cairo_cogl_surface_fill (abstract_surface,
+ op,
+ source,
+ &path,
+ CAIRO_FILL_RULE_WINDING,
+ 1,
+ CAIRO_ANTIALIAS_DEFAULT,
+ clip);
+BAIL:
+ _cairo_path_fixed_fini (&path);
+ return status;
+}
+
+static CoglPipelineWrapMode
+get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
+{
+ switch (extend_mode)
+ {
+ case CAIRO_EXTEND_NONE:
+ return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+ case CAIRO_EXTEND_PAD:
+ return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+ case CAIRO_EXTEND_REPEAT:
+ return COGL_PIPELINE_WRAP_MODE_REPEAT;
+ case CAIRO_EXTEND_REFLECT:
+ /* TODO: return COGL_PIPELINE_WRAP_MODE_MIRROR; */
+ return CAIRO_EXTEND_REPEAT;
+ }
+ assert (0); /* not reached */
+ return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+}
+
+#if 0
+/* Given an arbitrary texture, check if it's already a pot texture and simply
+ * return it back if so. If not create a new pot texture, scale the old to
+ * fill it, unref the old and return a pointer to the new pot texture. */
+static cairo_int_status_t
+_cairo_cogl_get_pot_texture (CoglContext *context,
+ CoglTexture *texture,
+ CoglTexture **pot_texture)
+{
+ int width = cogl_texture_get_width (texture);
+ int height = cogl_texture_get_height (texture);
+ int pot_width;
+ int pot_height;
+ CoglHandle offscreen = NULL;
+ CoglTexture2D *pot = NULL;
+ GError *error;
+
+ pot_width = _cairo_cogl_util_next_p2 (width);
+ pot_height = _cairo_cogl_util_next_p2 (height);
+
+ if (pot_width == width && pot_height == height)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ for (;;) {
+ error = NULL;
+ pot = cogl_texture_2d_new_with_size (context,
+ pot_width,
+ pot_height,
+ cogl_texture_get_format (texture),
+ &error);
+ if (pot)
+ break;
+ else
+ g_error_free (error);
+
+ if (pot_width > pot_height)
+ pot_width >>= 1;
+ else
+ pot_height >>= 1;
+
+ if (!pot_width || !pot_height)
+ break;
+ }
+
+ *pot_texture = COGL_TEXTURE (pot);
+
+ if (!pot)
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ /* Use the GPU to do a bilinear filtered scale from npot to pot... */
+ offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (pot));
+ error = NULL;
+ if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) {
+ /* NB: if we don't pass an error then Cogl is allowed to simply abort
+ * automatically. */
+ g_error_free (error);
+ cogl_object_unref (pot);
+ *pot_texture = NULL;
+ return CAIRO_INT_STATUS_NO_MEMORY;
+ }
+
+ cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
+ cogl_set_source_texture (texture);
+ cogl_rectangle (-1, 1, 1, -1);
+ cogl_pop_framebuffer ();
+
+ cogl_object_unref (offscreen);
+}
+#endif
+
+/* NB: a reference for the texture is transferred to the caller which should
+ * be unrefed */
+static CoglTexture *
+_cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t *reference_surface,
+ cairo_surface_t *abstract_surface)
+{
+ cairo_image_surface_t *image;
+ cairo_image_surface_t *acquired_image = NULL;
+ void *image_extra;
+ CoglPixelFormat format;
+ cairo_image_surface_t *image_clone = NULL;
+ CoglTexture2D *texture;
+ GError *error = NULL;
+ cairo_surface_t *clone;
+
+ if (abstract_surface->device == reference_surface->base.device) {
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+ _cairo_cogl_surface_flush (surface, 0);
+ return surface->texture ? cogl_object_ref (surface->texture) : NULL;
+ }
+
+ if (abstract_surface->type == CAIRO_SURFACE_TYPE_COGL) {
+ if (_cairo_surface_is_subsurface (abstract_surface)) {
+ cairo_cogl_surface_t *surface;
+
+ surface = (cairo_cogl_surface_t *)
+ _cairo_surface_subsurface_get_target (abstract_surface);
+ if (surface->base.device == reference_surface->base.device)
+ return surface->texture ? cogl_object_ref (surface->texture) : NULL;
+ }
+ }
+
+ clone = _cairo_surface_has_snapshot (abstract_surface, &_cairo_cogl_surface_backend);
+ if (clone) {
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)clone;
+ return surface->texture ? cogl_object_ref (surface->texture) : NULL;
+ }
+
+ g_warning ("Uploading image surface to texture");
+
+ if (_cairo_surface_is_image (abstract_surface)) {
+ image = (cairo_image_surface_t *)abstract_surface;
+ } else {
+ cairo_status_t status = _cairo_surface_acquire_source_image (abstract_surface,
+ &acquired_image, &image_extra);
+ if (unlikely (status)) {
+ g_warning ("acquire_source_image failed: %s [%d]\n",
+ cairo_status_to_string (status), status);
+ return NULL;
+ }
+ image = acquired_image;
+ }
+
+ format = get_cogl_format_from_cairo_format (image->format);
+ if (!format)
+ {
+ image_clone = _cairo_image_surface_coerce (image);
+ if (unlikely (image_clone->base.status)) {
+ g_warning ("image_surface_coerce failed");
+ texture = NULL;
+ goto BAIL;
+ }
+
+ format = get_cogl_format_from_cairo_format (image_clone->format);
+ assert (format);
+ }
+
+ texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
+ image->width,
+ image->height,
+ format, /* incoming */
+ format, /* desired */
+ image->stride,
+ image->data,
+ &error);
+ if (!texture) {
+ g_warning ("Failed to allocate texture: %s", error->message);
+ g_error_free (error);
+ goto BAIL;
+ }
+
+ clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
+ reference_surface->ignore_alpha,
+ NULL, COGL_TEXTURE (texture));
+
+ _cairo_surface_attach_snapshot (abstract_surface, clone, NULL);
+
+ /* Attaching the snapshot will take a reference on the clone surface... */
+ cairo_surface_destroy (clone);
+
+BAIL:
+ if (image_clone)
+ cairo_surface_destroy (&image_clone->base);
+ if (acquired_image)
+ _cairo_surface_release_source_image (abstract_surface, acquired_image, image_extra);
+
+ return COGL_TEXTURE (texture);
+}
+
+/* NB: a reference for the texture is transferred to the caller which should
+ * be unrefed */
+static CoglTexture *
+_cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
+ cairo_cogl_surface_t *destination,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ cairo_cogl_texture_attributes_t *attributes)
+{
+ CoglTexture *texture = NULL;
+
+ switch ((int)pattern->type)
+ {
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface;
+ texture = _cairo_cogl_acquire_surface_texture (destination, surface);
+ if (!texture)
+ return NULL;
+
+ /* XXX: determine if it would have no effect to change the
+ * extend mode to EXTEND_PAD instead since we can simply map
+ * EXTEND_PAD to CLAMP_TO_EDGE without needing fragment shader
+ * tricks or extra border texels. */
+#if 0
+ /* TODO: We still need to consider HW such as SGX which doesn't have
+ * full support for NPOT textures. */
+ if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_REFLECT) {
+ _cairo_cogl_get_pot_texture ();
+ }
+#endif
+
+ cairo_matrix_init_identity (&attributes->matrix);
+
+ /* Convert from un-normalized source coordinates in backend
+ * coordinates to normalized texture coordinates */
+ cairo_matrix_scale (&attributes->matrix,
+ 1.0f / cogl_texture_get_width (texture),
+ 1.0f / cogl_texture_get_height (texture));
+
+ /* XXX: need to multiply in the pattern->matrix */
+
+ attributes->extend = pattern->extend;
+ attributes->filter = CAIRO_FILTER_BILINEAR;
+ attributes->has_component_alpha = pattern->has_component_alpha;
+
+ attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
+ attributes->t_wrap = attributes->s_wrap;
+
+ return texture;
+ }
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH: {
+ cairo_surface_t *surface;
+ cairo_matrix_t texture_matrix;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ extents->width, extents->height);
+ if (_cairo_surface_offset_paint (surface,
+ extents->x, extents->y,
+ CAIRO_OPERATOR_SOURCE,
+ pattern, NULL)) {
+ cairo_surface_destroy (surface);
+ return NULL;
+ }
+
+ texture = _cairo_cogl_acquire_surface_texture (destination, surface);
+ if (!texture)
+ goto BAIL;
+
+ cairo_matrix_init_identity (&texture_matrix);
+
+ /* Convert from un-normalized source coordinates in backend
+ * coordinates to normalized texture coordinates */
+ cairo_matrix_scale (&texture_matrix,
+ 1.0f / cogl_texture_get_width (texture),
+ 1.0f / cogl_texture_get_height (texture));
+
+ cairo_matrix_translate (&texture_matrix, -extents->x, -extents->y);
+
+ attributes->matrix = texture_matrix;
+ attributes->extend = pattern->extend;
+ attributes->filter = CAIRO_FILTER_NEAREST;
+ attributes->has_component_alpha = pattern->has_component_alpha;
+
+ /* any pattern extend modes have already been dealt with... */
+ attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+ attributes->t_wrap = attributes->s_wrap;
+
+BAIL:
+ cairo_surface_destroy (surface);
+
+ return texture;
+ }
+ case CAIRO_PATTERN_TYPE_LINEAR: {
+ cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern;
+ cairo_cogl_linear_gradient_t *gradient;
+ cairo_cogl_linear_texture_entry_t *linear_texture;
+ cairo_int_status_t status;
+ float a, b;
+ float dist;
+ float scale;
+ float angle;
+
+ status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device),
+ pattern->extend,
+ linear_pattern->base.n_stops,
+ linear_pattern->base.stops,
+ &gradient);
+ if (unlikely (status))
+ return NULL;
+
+ linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend);
+
+ attributes->extend = pattern->extend;
+ attributes->filter = CAIRO_FILTER_BILINEAR;
+ attributes->has_component_alpha = pattern->has_component_alpha;
+ attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
+ attributes->t_wrap = COGL_PIPELINE_WRAP_MODE_REPEAT;
+
+ cairo_matrix_init_identity (&attributes->matrix);
+
+ a = linear_pattern->pd2.x - linear_pattern->pd1.x;
+ b = linear_pattern->pd2.y - linear_pattern->pd1.y;
+ dist = sqrtf (a*a + b*b);
+ scale = 1.0f / dist;
+ angle = - atan2f (b, a);
+
+ cairo_matrix_rotate (&attributes->matrix, angle);
+ cairo_matrix_scale (&attributes->matrix, scale, scale);
+
+ cairo_matrix_translate (&attributes->matrix,
+ -linear_pattern->pd1.x,
+ -linear_pattern->pd1.y);
+
+ /* XXX: this caught me out: cairo doesn't follow the standard
+ * maths convention for multiplying two matrices A x B - cairo
+ * does B x A so the final matrix is as if A's transforms were
+ * applied first.
+ */
+ cairo_matrix_multiply (&attributes->matrix,
+ &pattern->matrix,
+ &attributes->matrix);
+
+ return cogl_object_ref (linear_texture->texture);
+ }
+ default:
+ g_warning ("Un-supported source type");
+ return NULL;
+ }
+}
+
+static void
+set_layer_texture_with_attributes (CoglPipeline *pipeline,
+ int layer_index,
+ CoglTexture *texture,
+ cairo_cogl_texture_attributes_t *attributes)
+{
+ cogl_pipeline_set_layer_texture (pipeline, layer_index, texture);
+
+ if (!_cairo_matrix_is_identity (&attributes->matrix)) {
+ cairo_matrix_t *m = &attributes->matrix;
+ float texture_matrixfv[16] = {
+ m->xx, m->yx, 0, 0,
+ m->xy, m->yy, 0, 0,
+ 0, 0, 1, 0,
+ m->x0, m->y0, 0, 1
+ };
+ CoglMatrix texture_matrix;
+ cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv);
+ cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix);
+ }
+
+ if (attributes->s_wrap != attributes->t_wrap) {
+ cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap);
+ cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap);
+ } else
+ cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap);
+}
+
+static CoglPipeline *
+get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
+ const cairo_pattern_t *source,
+ cairo_operator_t op,
+ cairo_cogl_surface_t *destination,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_cogl_template_type template_type;
+ CoglPipeline *pipeline;
+
+ switch ((int)source->type)
+ {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ template_type = mask ?
+ CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID : CAIRO_COGL_TEMPLATE_TYPE_SOLID;
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ template_type = mask ?
+ CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE : CAIRO_COGL_TEMPLATE_TYPE_TEXTURE;
+ break;
+ default:
+ g_warning ("Un-supported source type");
+ return NULL;
+ }
+
+ pipeline = cogl_pipeline_copy (to_device(destination->base.device)->template_pipelines[op][template_type]);
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source;
+ cogl_pipeline_set_color4f (pipeline,
+ solid_pattern->color.red * solid_pattern->color.alpha,
+ solid_pattern->color.green * solid_pattern->color.alpha,
+ solid_pattern->color.blue * solid_pattern->color.alpha,
+ solid_pattern->color.alpha);
+ } else {
+ cairo_cogl_texture_attributes_t attributes;
+ CoglTexture *texture =
+ _cairo_cogl_acquire_pattern_texture (source, destination,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &attributes);
+ if (!texture)
+ goto BAIL;
+ set_layer_texture_with_attributes (pipeline, 0, texture, &attributes);
+ cogl_object_unref (texture);
+ }
+
+ if (mask) {
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask;
+ CoglColor color;
+ cogl_color_init_from_4f (&color,
+ solid_pattern->color.red * solid_pattern->color.alpha,
+ solid_pattern->color.green * solid_pattern->color.alpha,
+ solid_pattern->color.blue * solid_pattern->color.alpha,
+ solid_pattern->color.alpha);
+ cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
+ } else {
+ cairo_cogl_texture_attributes_t attributes;
+ CoglTexture *texture =
+ _cairo_cogl_acquire_pattern_texture (mask, destination,
+ &extents->bounded,
+ &extents->mask_sample_area,
+ &attributes);
+ if (!texture)
+ goto BAIL;
+ set_layer_texture_with_attributes (pipeline, 1, texture, &attributes);
+ cogl_object_unref (texture);
+ }
+ }
+
+ return pipeline;
+
+BAIL:
+ cogl_object_unref (pipeline);
+ return NULL;
+}
+
+#if 0
+CoglPrimitive *
+_cairo_cogl_rectangle_new_p2t2t2 (float x,
+ float y,
+ float width,
+ float height)
+{
+ CoglVertexP2 vertices[] = {
+ {x, y}, {x, y + height}, {x + width, y + height},
+ {x, y}, {x + width, y + height}, {x + width, y}
+ };
+ CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (sizeof (vertices));
+ CoglAttribute *pos = cogl_attribute_new (buffer,
+ "cogl_position_in",
+ sizeof (CoglVertexP2),
+ 0,
+ 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
+ "cogl_tex_coord0_in",
+ sizeof (CoglVertexP2),
+ 0,
+ 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
+ "cogl_tex_coord0_in",
+ sizeof (CoglVertexP2),
+ 0,
+ 2,
+ COGL_ATTRIBUTE_TYPE_FLOAT);
+ CoglPrimitive *prim;
+
+ cogl_buffer_set_data (COGL_BUFFER (buffer), 0, vertices, sizeof (vertices));
+
+ /* The attributes will now keep the buffer alive... */
+ cogl_object_unref (buffer);
+
+ prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
+ 6, pos, tex_coords, NULL);
+
+ /* The primitive will now keep the attribute alive... */
+ cogl_object_unref (pos);
+
+ return prim;
+}
+#endif
+
+static void
+_cairo_cogl_log_clip (cairo_cogl_surface_t *surface,
+ const cairo_clip_t *clip)
+{
+ if (!_cairo_clip_equal (clip, surface->last_clip)) {
+ _cairo_cogl_journal_log_clip (surface, clip);
+ _cairo_clip_destroy (surface->last_clip);
+ surface->last_clip = _cairo_clip_copy (clip);
+ }
+}
+
+static void
+_cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_clip_t *clip = composite->clip;
+
+ if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
+ clip = NULL;
+
+ if (clip == NULL) {
+ if (_cairo_composite_rectangles_can_reduce_clip (composite,
+ surface->last_clip))
+ return;
+ }
+
+ _cairo_cogl_log_clip (surface, clip);
+}
+
+static cairo_bool_t
+is_operator_supported (cairo_operator_t op)
+{
+ switch ((int)op) {
+ case CAIRO_OPERATOR_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 cairo_int_status_t
+_cairo_cogl_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_cogl_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_status_t status;
+ CoglPipeline *pipeline;
+ cairo_matrix_t identity;
+
+ /* XXX: Use this to smoke test the acquire_source/dest_image fallback
+ * paths... */
+ //return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (!is_operator_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ &surface->base,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ pipeline = get_source_mask_operator_destination_pipeline (mask, source,
+ op, surface, &extents);
+ if (!pipeline){
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
+ }
+
+ _cairo_cogl_maybe_log_clip (surface, &extents);
+
+ cairo_matrix_init_identity (&identity);
+ _cairo_cogl_journal_log_rectangle (surface, pipeline,
+ extents.bounded.x,
+ extents.bounded.y,
+ extents.bounded.width,
+ extents.bounded.height,
+ 2,
+ &identity);
+
+ /* The journal will take a reference on the pipeline and clip_path... */
+ cogl_object_unref (pipeline);
+
+BAIL:
+ return status;
+}
+
+static int
+_cairo_cogl_source_n_layers (const cairo_pattern_t *source)
+{
+ switch ((int)source->type)
+ {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return 0;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return 1;
+ default:
+ g_warning ("Unsupported source type");
+ return 0;
+ }
+}
+
+static cairo_bool_t
+_cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b)
+{
+ const cairo_cogl_path_fill_meta_t *meta0 = key_a;
+ const cairo_cogl_path_fill_meta_t *meta1 = key_b;
+
+ return _cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
+}
+
+static cairo_bool_t
+_cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a,
+ const cairo_stroke_style_t *b)
+{
+ if (a->line_width == b->line_width &&
+ a->line_cap == b->line_cap &&
+ a->line_join == b->line_join &&
+ a->miter_limit == b->miter_limit &&
+ a->num_dashes == b->num_dashes &&
+ a->dash_offset == b->dash_offset)
+ {
+ unsigned int i;
+ for (i = 0; i < a->num_dashes; i++) {
+ if (a->dash[i] != b->dash[i])
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_cogl_path_stroke_meta_equal (const void *key_a, const void *key_b)
+{
+ const cairo_cogl_path_stroke_meta_t *meta0 = key_a;
+ const cairo_cogl_path_stroke_meta_t *meta1 = key_b;
+
+ return _cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style) &&
+ _cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
+}
+
+static cairo_cogl_path_stroke_meta_t *
+_cairo_cogl_path_stroke_meta_reference (cairo_cogl_path_stroke_meta_t *meta)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+ _cairo_reference_count_inc (&meta->ref_count);
+
+ return meta;
+}
+
+static void
+_cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
+ return;
+
+ _cairo_path_fixed_fini (meta->user_path);
+ free (meta->user_path);
+
+ _cairo_stroke_style_fini (&meta->style);
+
+ if (meta->prim)
+ cogl_object_unref (meta->prim);
+
+ free (meta);
+}
+
+static cairo_cogl_path_stroke_meta_t *
+_cairo_cogl_path_stroke_meta_lookup (cairo_cogl_device_t *ctx,
+ unsigned long hash,
+ cairo_path_fixed_t *user_path,
+ const cairo_stroke_style_t *style,
+ double tolerance)
+{
+ cairo_cogl_path_stroke_meta_t *ret;
+ cairo_cogl_path_stroke_meta_t lookup;
+
+ lookup.cache_entry.hash = hash;
+ lookup.user_path = user_path;
+ lookup.style = *style;
+ lookup.tolerance = tolerance;
+
+ ret = _cairo_cache_lookup (&ctx->path_stroke_staging_cache, &lookup.cache_entry);
+ if (!ret)
+ ret = _cairo_cache_lookup (&ctx->path_stroke_prim_cache, &lookup.cache_entry);
+ return ret;
+}
+
+static void
+_cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface,
+ cairo_cogl_path_stroke_meta_t *meta,
+ size_t size)
+{
+ /* now that we know the meta structure is associated with a primitive
+ * we promote it from the staging cache into the primitive cache.
+ */
+
+ /* XXX: _cairo_cache borks if you try and remove an entry that's already
+ * been evicted so we explicitly look it up first... */
+ if (_cairo_cache_lookup (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry)) {
+ _cairo_cogl_path_stroke_meta_reference (meta);
+ _cairo_cache_remove (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry);
+ }
+
+ meta->cache_entry.size = size;
+ if (_cairo_cache_insert (&to_device(surface->base.device)->path_stroke_prim_cache, &meta->cache_entry) !=
+ CAIRO_STATUS_SUCCESS)
+ _cairo_cogl_path_stroke_meta_destroy (meta);
+}
+
+static unsigned int
+_cairo_cogl_stroke_style_hash (unsigned int hash,
+ const cairo_stroke_style_t *style)
+{
+ unsigned int i;
+ hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width));
+ hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap));
+ hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join));
+ hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit));
+ hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes));
+ hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset));
+ for (i = 0; i < style->num_dashes; i++)
+ hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double));
+ return hash;
+}
+
+static cairo_cogl_path_stroke_meta_t *
+_cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t *surface,
+ const cairo_stroke_style_t *style,
+ double tolerance)
+{
+ unsigned long hash;
+ cairo_cogl_path_stroke_meta_t *meta = NULL;
+ cairo_path_fixed_t *meta_path = NULL;
+ cairo_status_t status;
+
+ if (!surface->user_path)
+ return NULL;
+
+ hash = _cairo_path_fixed_hash (surface->user_path);
+ hash = _cairo_cogl_stroke_style_hash (hash, style);
+ hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
+
+ meta = _cairo_cogl_path_stroke_meta_lookup (to_device(surface->base.device), hash,
+ surface->user_path, style, tolerance);
+ if (meta)
+ return meta;
+
+ meta = calloc (1, sizeof (cairo_cogl_path_stroke_meta_t));
+ if (!meta)
+ goto BAIL;
+ CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
+ meta->cache_entry.hash = hash;
+ meta->counter = 0;
+ meta_path = malloc (sizeof (cairo_path_fixed_t));
+ if (!meta_path)
+ goto BAIL;
+ /* FIXME: we should add a ref-counted wrapper for our user_paths
+ * so we don't have to keep copying them here! */
+ status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
+ if (unlikely (status))
+ goto BAIL;
+ meta->user_path = meta_path;
+ meta->ctm_inverse = *surface->ctm_inverse;
+
+ status = _cairo_stroke_style_init_copy (&meta->style, style);
+ if (unlikely (status)) {
+ _cairo_path_fixed_fini (meta_path);
+ goto BAIL;
+ }
+ meta->tolerance = tolerance;
+
+ return meta;
+
+BAIL:
+ free (meta_path);
+ free (meta);
+ return NULL;
+}
+
+static cairo_int_status_t
+_cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t *surface,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ int n_layers,
+ cairo_bool_t one_shot,
+ CoglPrimitive **primitive,
+ size_t *size)
+{
+ cairo_traps_t traps;
+ cairo_int_status_t status;
+
+ _cairo_traps_init (&traps);
+
+ status = _cairo_path_fixed_stroke_polygon_to_traps (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &traps);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (traps.num_traps == 0) {
+ status = CAIRO_INT_STATUS_NOTHING_TO_DO;
+ goto BAIL;
+ }
+
+ *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
+
+ //g_print ("new stroke prim\n");
+ *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
+ if (!*primitive) {
+ status = CAIRO_INT_STATUS_NO_MEMORY;
+ goto BAIL;
+ }
+
+BAIL:
+ _cairo_traps_fini (&traps);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+ cairo_composite_rectangles_t extents;
+ CoglPipeline *pipeline;
+ cairo_status_t status;
+#ifdef ENABLE_PATH_CACHE
+ cairo_cogl_path_stroke_meta_t *meta = NULL;
+ cairo_matrix_t transform_matrix;
+#endif
+ cairo_matrix_t *transform = NULL;
+ gboolean one_shot = TRUE;
+ CoglPrimitive *prim = NULL;
+ cairo_bool_t new_prim = FALSE;
+
+ if (! is_operator_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* FIXME - support unbounded operators */
+ if (!_cairo_operator_bounded_by_mask (op)) {
+ /* Currently IN this is the only unbounded operator we aim to support
+ * in cairo-cogl. */
+ assert (op == CAIRO_OPERATOR_IN);
+ g_warning ("FIXME: handle stroking with unbounded operators!");
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &surface->base,
+ op, source, path,
+ style,
+ ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+#ifdef ENABLE_PATH_CACHE
+ /* FIXME: we are currently leaking the meta state if we don't reach
+ * the cache_insert at the end. */
+ meta = _cairo_cogl_get_path_stroke_meta (surface, style, tolerance);
+ if (meta) {
+ prim = meta->prim;
+ if (prim) {
+ cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
+ transform = &transform_matrix;
+ } else if (meta->counter++ > 10)
+ one_shot = FALSE;
+ }
+#endif
+
+ if (!prim) {
+ int n_layers = _cairo_cogl_source_n_layers (source);
+ size_t prim_size = 0;
+ status = _cairo_cogl_stroke_to_primitive (surface, path, style,
+ ctm, ctm_inverse, tolerance,
+ n_layers, one_shot,
+ &prim, &prim_size);
+ if (unlikely (status))
+ return status;
+ new_prim = TRUE;
+#if defined (ENABLE_PATH_CACHE)
+ if (meta) {
+ meta->prim = cogl_object_ref (prim);
+ _cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size);
+ }
+#endif
+ }
+
+ pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
+ op, surface, &extents);
+ if (!pipeline)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_cogl_maybe_log_clip (surface, &extents);
+
+ _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
+
+ /* The journal will take a reference on the pipeline and primitive... */
+ cogl_object_unref (pipeline);
+ if (new_prim)
+ cogl_object_unref (prim);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_cogl_path_fill_meta_t *
+_cairo_cogl_path_fill_meta_reference (cairo_cogl_path_fill_meta_t *meta)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+ _cairo_reference_count_inc (&meta->ref_count);
+
+ return meta;
+}
+
+static void
+_cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
+ return;
+
+ _cairo_path_fixed_fini (meta->user_path);
+ free (meta->user_path);
+
+ if (meta->prim)
+ cogl_object_unref (meta->prim);
+
+ free (meta);
+}
+
+static cairo_cogl_path_fill_meta_t *
+_cairo_cogl_path_fill_meta_lookup (cairo_cogl_device_t *ctx,
+ unsigned long hash,
+ cairo_path_fixed_t *user_path)
+{
+ cairo_cogl_path_fill_meta_t *ret;
+ cairo_cogl_path_fill_meta_t lookup;
+
+ lookup.cache_entry.hash = hash;
+ lookup.user_path = user_path;
+
+ ret = _cairo_cache_lookup (&ctx->path_fill_staging_cache, &lookup.cache_entry);
+ if (!ret)
+ ret = _cairo_cache_lookup (&ctx->path_fill_prim_cache, &lookup.cache_entry);
+ return ret;
+}
+
+static void
+_cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t *surface,
+ cairo_cogl_path_fill_meta_t *meta,
+ size_t size)
+{
+ /* now that we know the meta structure is associated with a primitive
+ * we promote it from the staging cache into the primitive cache.
+ */
+
+ /* XXX: _cairo_cache borks if you try and remove an entry that's already
+ * been evicted so we explicitly look it up first... */
+ if (_cairo_cache_lookup (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry)) {
+ _cairo_cogl_path_fill_meta_reference (meta);
+ _cairo_cache_remove (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry);
+ }
+
+ meta->cache_entry.size = size;
+ if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_prim_cache, &meta->cache_entry) !=
+ CAIRO_STATUS_SUCCESS)
+ _cairo_cogl_path_fill_meta_destroy (meta);
+}
+
+static cairo_cogl_path_fill_meta_t *
+_cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface)
+{
+ unsigned long hash;
+ cairo_cogl_path_fill_meta_t *meta = NULL;
+ cairo_path_fixed_t *meta_path = NULL;
+ cairo_status_t status;
+
+ if (!surface->user_path)
+ return NULL;
+
+ hash = _cairo_path_fixed_hash (surface->user_path);
+
+ meta = _cairo_cogl_path_fill_meta_lookup (to_device(surface->base.device),
+ hash, surface->user_path);
+ if (meta)
+ return meta;
+
+ meta = calloc (1, sizeof (cairo_cogl_path_fill_meta_t));
+ if (!meta)
+ goto BAIL;
+ meta->cache_entry.hash = hash;
+ meta->counter = 0;
+ CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
+ meta_path = malloc (sizeof (cairo_path_fixed_t));
+ if (!meta_path)
+ goto BAIL;
+ /* FIXME: we should add a ref-counted wrapper for our user_paths
+ * so we don't have to keep copying them here! */
+ status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
+ if (unlikely (status))
+ goto BAIL;
+ meta->user_path = meta_path;
+ meta->ctm_inverse = *surface->ctm_inverse;
+
+ /* To start with - until we associate a CoglPrimitive with the meta
+ * structure - we keep the meta in a staging structure until we
+ * see whether it actually gets re-used. */
+ meta->cache_entry.size = 1;
+ if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry) !=
+ CAIRO_STATUS_SUCCESS)
+ _cairo_cogl_path_fill_meta_destroy (meta);
+
+ return meta;
+
+BAIL:
+ free (meta_path);
+ free (meta);
+ return NULL;
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_cogl_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_status_t status;
+#ifdef ENABLE_PATH_CACHE
+ cairo_cogl_path_fill_meta_t *meta = NULL;
+ cairo_matrix_t transform_matrix;
+#endif
+ cairo_matrix_t *transform = NULL;
+ cairo_bool_t one_shot = TRUE;
+ CoglPrimitive *prim = NULL;
+ cairo_bool_t new_prim = FALSE;
+ CoglPipeline *pipeline;
+
+ if (! is_operator_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* FIXME - support unbounded operators */
+ if (!_cairo_operator_bounded_by_mask (op)) {
+ /* Currently IN this is the only unbounded operator we aim to support
+ * in cairo-cogl. */
+ assert (op == CAIRO_OPERATOR_IN);
+ g_warning ("FIXME: handle filling with unbounded operators!");
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &surface->base,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+#ifndef FILL_WITH_COGL_PATH
+#ifdef ENABLE_PATH_CACHE
+ meta = _cairo_cogl_get_path_fill_meta (surface);
+ if (meta) {
+ prim = meta->prim;
+ if (prim) {
+ cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
+ transform = &transform_matrix;
+ } else if (meta->counter++ > 10)
+ one_shot = FALSE;
+ }
+#endif /* ENABLE_PATH_CACHE */
+
+ if (!prim) {
+ int n_layers = _cairo_cogl_source_n_layers (source);
+ size_t prim_size;
+ status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, tolerance,
+ one_shot, n_layers, &prim, &prim_size);
+ if (unlikely (status))
+ return status;
+ new_prim = TRUE;
+#ifdef ENABLE_PATH_CACHE
+ if (meta) {
+ meta->prim = cogl_object_ref (prim);
+ _cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size);
+ }
+#endif /* ENABLE_PATH_CACHE */
+ }
+
+#endif /* !FILL_WITH_COGL_PATH */
+
+ pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
+ op, surface, &extents);
+ if (!pipeline)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_cogl_maybe_log_clip (surface, &extents);
+
+#ifndef FILL_WITH_COGL_PATH
+ _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
+ /* The journal will take a reference on the prim */
+ if (new_prim)
+ cogl_object_unref (prim);
+#else
+ CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance);
+ _cairo_cogl_journal_log_path (surface, pipeline, cogl_path);
+ cogl_object_unref (cogl_path);
+#endif
+
+ /* The journal will take a reference on the pipeline... */
+ cogl_object_unref (pipeline);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_cogl_surface_fill_rectangle (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ double x,
+ double y,
+ double width,
+ double height,
+ cairo_matrix_t *ctm,
+ const cairo_clip_t *clip)
+{
+ cairo_cogl_surface_t *surface = abstract_surface;
+ CoglPipeline *pipeline;
+
+ if (! is_operator_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* FIXME - support unbounded operators */
+ if (!_cairo_operator_bounded_by_mask (op)) {
+ /* Currently IN this is the only unbounded operator we aim to support
+ * in cairo-cogl. */
+ assert (op == CAIRO_OPERATOR_IN);
+ g_warning ("FIXME: handle filling with unbounded operators!");
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* FIXME */
+#if 0
+ status = _cairo_composite_rectangles_init_for_fill_rectangle (&extents,
+ &surface->base,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+#endif
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ double x1 = x;
+ double y1 = y;
+ double x2 = x1 + width;
+ double y2 = y1 + height;
+
+ pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
+ op, surface, NULL);
+ if (!pipeline)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_cogl_log_clip (surface, clip);
+
+ _cairo_cogl_journal_log_rectangle (surface,
+ pipeline,
+ x1, y1, x2, y2,
+ 0,
+ ctm);
+ return CAIRO_INT_STATUS_SUCCESS;
+ } else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* TODO:
+ * We need to acquire the textures here, look at the corresponding
+ * attributes and see if this can be trivially handled by logging
+ * a textured rectangle only needing simple scaling or translation
+ * of texture coordinates.
+ *
+ * At this point we should also aim to remap the default
+ * EXTEND_NONE mode to EXTEND_PAD which is more efficient if we
+ * know it makes no difference either way since we can map that to
+ * CLAMP_TO_EDGE.
+ */
+}
+
+static cairo_int_status_t
+_cairo_cogl_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,
+ const cairo_clip_t *clip)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+const cairo_surface_backend_t _cairo_cogl_surface_backend = {
+ CAIRO_SURFACE_TYPE_COGL,
+ _cairo_cogl_surface_finish,
+#ifdef NEED_COGL_CONTEXT
+ _cairo_cogl_context_create,
+#else
+ _cairo_default_context_create,
+#endif
+
+ _cairo_cogl_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ _cairo_cogl_surface_acquire_source_image,
+ _cairo_cogl_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_cogl_surface_get_extents,
+ NULL, /* get_font_options */
+
+ _cairo_cogl_surface_flush, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_cogl_surface_paint,
+ _cairo_cogl_surface_mask,
+ _cairo_cogl_surface_stroke,
+ _cairo_cogl_surface_fill,
+ NULL, /* fill_stroke*/
+ _cairo_surface_fallback_glyphs,
+};
+
+static cairo_surface_t *
+_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
+ cairo_bool_t ignore_alpha,
+ CoglFramebuffer *framebuffer,
+ CoglTexture *texture)
+{
+ cairo_cogl_surface_t *surface;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&dev->base);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ surface = malloc (sizeof (cairo_cogl_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface->ignore_alpha = ignore_alpha;
+
+ surface->framebuffer = framebuffer;
+ if (framebuffer) {
+ surface->width = cogl_framebuffer_get_width (framebuffer);
+ surface->height = cogl_framebuffer_get_height (framebuffer);
+ surface->cogl_format = cogl_framebuffer_get_color_format (framebuffer);
+ cogl_object_ref (framebuffer);
+ }
+
+ /* FIXME: If texture == NULL and we are given an offscreen framebuffer
+ * then we want a way to poke inside the framebuffer to get a texture */
+ surface->texture = texture;
+ if (texture) {
+ if (!framebuffer) {
+ surface->width = cogl_texture_get_width (texture);
+ surface->height = cogl_texture_get_height (texture);
+ surface->cogl_format = cogl_texture_get_format (texture);
+ }
+ cogl_object_ref (texture);
+ }
+
+ assert(surface->width && surface->height);
+
+ surface->journal = NULL;
+
+ surface->buffer_stack = NULL;
+ surface->buffer_stack_size = 4096;
+
+ surface->last_clip = NULL;
+
+ surface->n_clip_updates_per_frame = 0;
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_cogl_surface_backend,
+ &dev->base,
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ return &surface->base;
+}
+
+cairo_surface_t *
+cairo_cogl_surface_create (cairo_device_t *abstract_device,
+ CoglFramebuffer *framebuffer)
+{
+ cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
+
+ if (abstract_device == NULL)
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ if (abstract_device->status)
+ return _cairo_surface_create_in_error (abstract_device->status);
+
+ if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+ return _cairo_cogl_surface_create_full (dev, FALSE, framebuffer, NULL);
+}
+slim_hidden_def (cairo_cogl_surface_create);
+
+CoglFramebuffer *
+cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface)
+{
+ cairo_cogl_surface_t *surface;
+
+ if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ surface = (cairo_cogl_surface_t *) abstract_surface;
+
+ return surface->framebuffer;
+}
+slim_hidden_def (cairo_cogl_surface_get_framebuffer);
+
+CoglTexture *
+cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface)
+{
+ cairo_cogl_surface_t *surface;
+
+ if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ surface = (cairo_cogl_surface_t *) abstract_surface;
+
+ return surface->texture;
+}
+slim_hidden_def (cairo_cogl_surface_get_texture);
+
+static cairo_status_t
+_cairo_cogl_device_flush (void *device)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (device);
+ if (unlikely (status))
+ return status;
+
+ /* XXX: we don't need to flush Cogl here, we just need to flush
+ * any batching we do of compositing primitives. */
+
+ cairo_device_release (device);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cogl_device_finish (void *device)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (device);
+ if (unlikely (status))
+ return;
+
+ /* XXX: Drop references to external resources */
+
+ cairo_device_release (device);
+}
+
+static void
+_cairo_cogl_device_destroy (void *device)
+{
+ cairo_cogl_device_t *dev = device;
+
+ /* FIXME: Free stuff! */
+
+ g_free (dev);
+}
+
+static const cairo_device_backend_t _cairo_cogl_device_backend = {
+ CAIRO_DEVICE_TYPE_COGL,
+
+ NULL, /* lock */
+ NULL, /* unlock */
+
+ _cairo_cogl_device_flush,
+ _cairo_cogl_device_finish,
+ _cairo_cogl_device_destroy,
+};
+
+static cairo_bool_t
+set_blend (CoglPipeline *pipeline, const char *blend_string)
+{
+ GError *error = NULL;
+ if (!cogl_pipeline_set_blend (pipeline, blend_string, &error)) {
+ g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string);
+ g_error_free (error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
+{
+ cairo_bool_t status = FALSE;
+
+ switch ((int)op)
+ {
+ case CAIRO_OPERATOR_SOURCE:
+ status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)");
+ break;
+ case CAIRO_OPERATOR_OVER:
+ status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))");
+ break;
+ case CAIRO_OPERATOR_IN:
+ status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)");
+ break;
+ case CAIRO_OPERATOR_DEST_OVER:
+ status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)");
+ break;
+ case CAIRO_OPERATOR_DEST_IN:
+ status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])");
+ break;
+ case CAIRO_OPERATOR_ADD:
+ status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)");
+ break;
+ }
+
+ return status;
+}
+
+static void
+create_templates_for_op (cairo_cogl_device_t *dev, cairo_operator_t op)
+{
+ CoglPipeline *base = cogl_pipeline_new ();
+ CoglPipeline *pipeline;
+ CoglColor color;
+
+ if (!_cairo_cogl_setup_op_state (base, op)) {
+ cogl_object_unref (base);
+ return;
+ }
+
+ dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
+
+ pipeline = cogl_pipeline_copy (base);
+ cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+ dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_TEXTURE] = pipeline;
+
+ pipeline = cogl_pipeline_copy (base);
+ cogl_pipeline_set_layer_combine (pipeline, 1,
+ "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
+ NULL);
+ cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
+ cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
+ dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID] = pipeline;
+
+ pipeline = cogl_pipeline_copy (base);
+ cogl_pipeline_set_layer_combine (pipeline, 1,
+ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+ NULL);
+ cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
+ dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE] = pipeline;
+}
+
+cairo_device_t *
+cairo_cogl_device_create (CoglContext *cogl_context)
+{
+ cairo_cogl_device_t *dev = g_new0 (cairo_cogl_device_t, 1);
+ cairo_status_t status;
+
+ dev->backend_vtable_initialized = FALSE;
+
+ dev->cogl_context = cogl_context;
+
+ dev->dummy_texture = cogl_texture_new_with_size (1, 1,
+ COGL_TEXTURE_NO_SLICING,
+ COGL_PIXEL_FORMAT_ANY);
+ if (!dev->dummy_texture)
+ goto ERROR;
+
+ memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines));
+ create_templates_for_op (dev, CAIRO_OPERATOR_SOURCE);
+ create_templates_for_op (dev, CAIRO_OPERATOR_OVER);
+ create_templates_for_op (dev, CAIRO_OPERATOR_IN);
+ create_templates_for_op (dev, CAIRO_OPERATOR_DEST_OVER);
+ create_templates_for_op (dev, CAIRO_OPERATOR_DEST_IN);
+ create_templates_for_op (dev, CAIRO_OPERATOR_ADD);
+
+ status = _cairo_cache_init (&dev->linear_cache,
+ _cairo_cogl_linear_gradient_equal,
+ NULL,
+ (cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy,
+ CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE);
+ if (unlikely (status))
+ return _cairo_device_create_in_error(status);
+
+ status = _cairo_cache_init (&dev->path_fill_staging_cache,
+ _cairo_cogl_path_fill_meta_equal,
+ NULL,
+ (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
+ 1000);
+
+ status = _cairo_cache_init (&dev->path_stroke_staging_cache,
+ _cairo_cogl_path_stroke_meta_equal,
+ NULL,
+ (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
+ 1000);
+
+ status = _cairo_cache_init (&dev->path_fill_prim_cache,
+ _cairo_cogl_path_fill_meta_equal,
+ NULL,
+ (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
+ CAIRO_COGL_PATH_META_CACHE_SIZE);
+
+ status = _cairo_cache_init (&dev->path_stroke_prim_cache,
+ _cairo_cogl_path_stroke_meta_equal,
+ NULL,
+ (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
+ CAIRO_COGL_PATH_META_CACHE_SIZE);
+
+ _cairo_device_init (&dev->base, &_cairo_cogl_device_backend);
+ return &dev->base;
+
+ERROR:
+ g_free (dev);
+ return NULL;
+}
+slim_hidden_def (cairo_cogl_device_create);
+
+void
+cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface)
+{
+ cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+ cairo_surface_flush (abstract_surface);
+
+ //g_print ("n_clip_update_per_frame = %d\n", surface->n_clip_updates_per_frame);
+ surface->n_clip_updates_per_frame = 0;
+}
+slim_hidden_def (cairo_cogl_surface_end_frame);
diff --git a/src/cairo-cogl-utils-private.h b/src/cairo-cogl-utils-private.h
new file mode 100755
index 000000000..ee77f3034
--- /dev/null
+++ b/src/cairo-cogl-utils-private.h
@@ -0,0 +1,54 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_UTILS_PRIVATE_H
+#define CAIRO_COGL_UTILS_PRIVATE_H
+
+#include "cairo-path-fixed-private.h"
+#include <cogl/cogl2-experimental.h>
+
+CoglPath *
+_cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ float tolerance);
+
+int
+_cairo_cogl_util_next_p2 (int a);
+
+#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS))
+
+static inline float
+_cairo_cogl_util_fixed_to_float (cairo_fixed_t f)
+{
+ return ((float) f) / CAIRO_FIXED_ONE_FLOAT;
+}
+
+#endif /* CAIRO_COGL_UTILS_PRIVATE_H */
diff --git a/src/cairo-cogl-utils.c b/src/cairo-cogl-utils.c
new file mode 100755
index 000000000..4f02aaa52
--- /dev/null
+++ b/src/cairo-cogl-utils.c
@@ -0,0 +1,126 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-cogl-utils-private.h"
+
+#include <cogl/cogl.h>
+#include <glib.h>
+
+static cairo_status_t
+_cogl_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cogl_path_move_to (closure,
+ _cairo_cogl_util_fixed_to_float (point->x),
+ _cairo_cogl_util_fixed_to_float (point->y));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cogl_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cogl_path_line_to (closure,
+ _cairo_cogl_util_fixed_to_float (point->x),
+ _cairo_cogl_util_fixed_to_float (point->y));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cogl_curve_to (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ cogl_path_curve_to (closure,
+ _cairo_cogl_util_fixed_to_float (p0->x),
+ _cairo_cogl_util_fixed_to_float (p0->y),
+ _cairo_cogl_util_fixed_to_float (p1->x),
+ _cairo_cogl_util_fixed_to_float (p1->y),
+ _cairo_cogl_util_fixed_to_float (p2->x),
+ _cairo_cogl_util_fixed_to_float (p2->y));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cogl_close_path (void *closure)
+{
+ cogl_path_close (closure);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+CoglPath *
+_cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ float tolerance)
+{
+ CoglPath *cogl_path = cogl_path_new ();
+ cairo_status_t status;
+
+ if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD)
+ cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_EVEN_ODD);
+ else
+ cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_NON_ZERO);
+
+#ifdef USE_CAIRO_PATH_FLATTENER
+ /* XXX: rely on cairo to do path flattening, since it seems Cogl's
+ * curve_to flattening is much slower */
+ status = _cairo_path_fixed_interpret_flat (path,
+ _cogl_move_to,
+ _cogl_line_to,
+ _cogl_close_path,
+ cogl_path,
+ tolerance);
+#else
+ status = _cairo_path_fixed_interpret (path,
+ _cogl_move_to,
+ _cogl_line_to,
+ _cogl_curve_to,
+ _cogl_close_path,
+ cogl_path);
+#endif
+
+ assert (status == CAIRO_STATUS_SUCCESS);
+ return cogl_path;
+}
+
+int
+_cairo_cogl_util_next_p2 (int a)
+{
+ int rval = 1;
+
+ while (rval < a)
+ rval <<= 1;
+
+ return rval;
+}
+
diff --git a/src/cairo-cogl.h b/src/cairo-cogl.h
new file mode 100755
index 000000000..f270d74d3
--- /dev/null
+++ b/src/cairo-cogl.h
@@ -0,0 +1,69 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifndef CAIRO_VG_H
+#define CAIRO_VG_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_COGL_SURFACE
+
+#include <cogl/cogl2-experimental.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_device_t *
+cairo_cogl_device_create (CoglContext *context);
+
+cairo_public cairo_surface_t *
+cairo_cogl_surface_create (cairo_device_t *device,
+ CoglFramebuffer *framebuffer);
+
+cairo_public CoglFramebuffer *
+cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface);
+
+cairo_public CoglTexture *
+cairo_cogl_surface_get_texture (cairo_surface_t *surface);
+
+cairo_public void
+cairo_cogl_surface_end_frame (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_COGL_SURFACE*/
+# error Cairo was not compiled with support for the Cogl backend
+#endif /* CAIRO_HAS_COGL_SURFACE*/
+
+#endif /* CAIRO_COGL_H */
diff --git a/src/cairo-color.c b/src/cairo-color.c
new file mode 100755
index 000000000..9c852559e
--- /dev/null
+++ b/src/cairo-color.c
@@ -0,0 +1,198 @@
+/* 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;
+ }
+}
+
+/* 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/src/cairo-combsort-inline.h b/src/cairo-combsort-inline.h
new file mode 100755
index 000000000..d359faeb5
--- /dev/null
+++ b/src/cairo-combsort-inline.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <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); \
+}
+
+#define CAIRO_COMBSORT_DECLARE_WITH_DATA(NAME, TYPE, CMP) \
+static void \
+NAME (TYPE *base, unsigned int nmemb, void *data) \
+{ \
+ unsigned int gap = nmemb; \
+ unsigned int i, j; \
+ int swapped; \
+ do { \
+ gap = _cairo_combsort_newgap (gap); \
+ swapped = gap > 1; \
+ for (i = 0; i < nmemb-gap ; i++) { \
+ j = i + gap; \
+ if (CMP (base[i], base[j], data) > 0 ) { \
+ TYPE tmp; \
+ tmp = base[i]; \
+ base[i] = base[j]; \
+ base[j] = tmp; \
+ swapped = 1; \
+ } \
+ } \
+ } while (swapped); \
+}
diff --git a/src/cairo-compiler-private.h b/src/cairo-compiler-private.h
new file mode 100755
index 000000000..f3e971ebb
--- /dev/null
+++ b/src/cairo-compiler-private.h
@@ -0,0 +1,246 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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(__APPLE__)) && \
+ !defined(__sun)
+#define cairo_private_no_warn __attribute__((__visibility__("hidden")))
+#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+#define cairo_private_no_warn __hidden
+#else /* not gcc >= 3.3 and not Sun Studio >= 8 */
+#define cairo_private_no_warn
+#undef CAIRO_HAS_HIDDEN_SYMBOLS
+#endif
+
+#ifndef WARN_UNUSED_RESULT
+#define WARN_UNUSED_RESULT
+#endif
+/* Add attribute(warn_unused_result) if supported */
+#define cairo_warn WARN_UNUSED_RESULT
+#define cairo_private cairo_private_no_warn cairo_warn
+
+/* This macro allow us to deprecate a function by providing an alias
+ for the old function name to the new function name. With this
+ macro, binary compatibility is preserved. The macro only works on
+ some platforms --- tough.
+
+ Meanwhile, new definitions in the public header file break the
+ source code so that it will no longer link against the old
+ symbols. Instead it will give a descriptive error message
+ indicating that the old function has been deprecated by the new
+ function.
+*/
+#if __GNUC__ >= 2 && defined(__ELF__)
+# define CAIRO_FUNCTION_ALIAS(old, new) \
+ extern __typeof (new) old \
+ __asm__ ("" #old) \
+ __attribute__((__alias__("" #new)))
+#else
+# define CAIRO_FUNCTION_ALIAS(old, new)
+#endif
+
+/*
+ * Cairo uses the following function attributes in order to improve the
+ * generated code (effectively by manual inter-procedural analysis).
+ *
+ * 'cairo_pure': The function is only allowed to read from its arguments
+ * and global memory (i.e. following a pointer argument or
+ * accessing a shared variable). The return value should
+ * only depend on its arguments, and for an identical set of
+ * arguments should return the same value.
+ *
+ * 'cairo_const': The function is only allowed to read from its arguments.
+ * It is not allowed to access global memory. The return
+ * value should only depend its arguments, and for an
+ * identical set of arguments should return the same value.
+ * This is currently the most strict function attribute.
+ *
+ * Both these function attributes allow gcc to perform CSE and
+ * constant-folding, with 'cairo_const 'also guaranteeing that pointer contents
+ * do not change across the function call.
+ */
+#if __GNUC__ >= 3
+#define cairo_pure __attribute__((pure))
+#define cairo_const __attribute__((const))
+#define cairo_always_inline inline __attribute__((always_inline))
+#else
+#define cairo_pure
+#define cairo_const
+#define cairo_always_inline inline
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define likely(expr) (__builtin_expect (!!(expr), 1))
+#define unlikely(expr) (__builtin_expect (!!(expr), 0))
+#else
+#define likely(expr) (expr)
+#define unlikely(expr) (expr)
+#endif
+
+#ifndef __GNUC__
+#undef __attribute__
+#define __attribute__(x)
+#endif
+
+#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
+#define access _access
+#define fdopen _fdopen
+#define hypot _hypot
+#define pclose _pclose
+#define popen _popen
+#define snprintf _snprintf
+#define strdup _strdup
+#define unlink _unlink
+#define vsnprintf _vsnprintf
+#endif
+
+#ifdef _MSC_VER
+#ifndef __cplusplus
+#undef inline
+#define inline __inline
+#endif
+#endif
+
+#if defined(_MSC_VER) && defined(_M_IX86)
+/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together.
+ The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and
+ will never be folded into another one. Something like this might eventually
+ be needed for GCC but it seems fine for now. */
+#define CAIRO_ENSURE_UNIQUE \
+ do { \
+ char func[] = __FUNCTION__; \
+ char file[] = __FILE__; \
+ __asm { \
+ __asm jmp __internal_skip_line_no \
+ __asm _emit (__LINE__ & 0xff) \
+ __asm _emit ((__LINE__>>8) & 0xff) \
+ __asm _emit ((__LINE__>>16) & 0xff) \
+ __asm _emit ((__LINE__>>24) & 0xff) \
+ __asm lea eax, func \
+ __asm lea eax, file \
+ __asm __internal_skip_line_no: \
+ }; \
+ } while (0)
+#else
+#define CAIRO_ENSURE_UNIQUE do { } while (0)
+#endif
+
+#ifdef __STRICT_ANSI__
+#undef inline
+#define inline __inline__
+#endif
+
+#endif
diff --git a/src/cairo-composite-rectangles-private.h b/src/cairo-composite-rectangles-private.h
new file mode 100755
index 000000000..108137427
--- /dev/null
+++ b/src/cairo-composite-rectangles-private.h
@@ -0,0 +1,203 @@
+/* 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"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+
+CAIRO_BEGIN_DECLS
+
+/* Rectangles that take part in a composite operation.
+ *
+ * The source and mask track the extents of the respective patterns in device
+ * space. The unbounded rectangle is essentially the clip rectangle. And the
+ * intersection of all is the bounded rectangle, which is the minimum extents
+ * the operation may require. Whether or not the operation is actually bounded
+ * is tracked in the is_bounded boolean.
+ *
+ */
+struct _cairo_composite_rectangles {
+ cairo_surface_t *surface;
+ cairo_operator_t op;
+
+ cairo_rectangle_int_t source;
+ cairo_rectangle_int_t mask;
+ cairo_rectangle_int_t destination;
+
+ cairo_rectangle_int_t bounded; /* source? IN mask? IN unbounded */
+ cairo_rectangle_int_t unbounded; /* destination IN clip */
+ uint32_t is_bounded;
+
+ cairo_rectangle_int_t source_sample_area;
+ cairo_rectangle_int_t mask_sample_area;
+
+ cairo_pattern_union_t source_pattern;
+ cairo_pattern_union_t mask_pattern;
+ const cairo_pattern_t *original_source_pattern;
+ const cairo_pattern_t *original_mask_pattern;
+
+ cairo_clip_t *clip; /* clip will be reduced to the minimal container */
+};
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_paint (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_mask (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_stroke (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_fill (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_boxes_t *boxes,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_polygon_t *polygon,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ cairo_bool_t *overlap);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_glyphs (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ cairo_bool_t *overlap);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
+ const cairo_box_t *box);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
+ const cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
+ cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
+ cairo_boxes_t *damage);
+
+cairo_private void
+_cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */
diff --git a/src/cairo-composite-rectangles.c b/src/cairo-composite-rectangles.c
new file mode 100755
index 000000000..f288bb785
--- /dev/null
+++ b/src/cairo-composite-rectangles.c
@@ -0,0 +1,731 @@
+/* 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-clip-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-pattern-private.h"
+
+/* A collection of routines to facilitate writing compositors. */
+
+void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents)
+{
+ _cairo_clip_destroy (extents->clip);
+}
+
+static inline cairo_bool_t
+_cairo_composite_rectangles_check_lazy_init (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_pattern_type_t type;
+
+ if (! extents->is_bounded)
+ return FALSE;
+
+ type = cairo_pattern_get_type ((cairo_pattern_t *)pattern);
+
+ if (type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+ cairo_surface_t *pattern_surface = surface_pattern->surface;
+
+ /* XXX: both source and target are GL surface */
+ if (cairo_surface_get_type (pattern_surface) == CAIRO_SURFACE_TYPE_GL &&
+ pattern_surface->backend->type == CAIRO_SURFACE_TYPE_GL &&
+ cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_GL &&
+ surface->device == pattern_surface->device)
+ return TRUE;
+ } else if (type == CAIRO_PATTERN_TYPE_SOLID ||
+ type == CAIRO_PATTERN_TYPE_RADIAL ||
+ type == CAIRO_PATTERN_TYPE_LINEAR)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+_cairo_composite_reduce_pattern (const cairo_pattern_t *src,
+ cairo_pattern_union_t *dst)
+{
+ int tx, ty;
+
+ _cairo_pattern_init_static_copy (&dst->base, src);
+ if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
+ return;
+
+ dst->base.filter = _cairo_pattern_analyze_filter (&dst->base, NULL),
+
+ tx = ty = 0;
+ if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
+ dst->base.filter,
+ &tx, &ty))
+ {
+ dst->base.matrix.x0 = tx;
+ dst->base.matrix.y0 = ty;
+ }
+}
+
+static inline cairo_bool_t
+_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ cairo_bool_t *should_be_lazy)
+{
+ if (_cairo_clip_is_all_clipped (clip))
+ return FALSE;
+
+ extents->surface = surface;
+ extents->op = op;
+
+ _cairo_surface_get_extents (surface, &extents->destination);
+ extents->clip = NULL;
+
+ extents->unbounded = extents->destination;
+ extents->is_bounded = _cairo_operator_bounded_by_either (op);
+
+ if (*should_be_lazy)
+ *should_be_lazy = _cairo_composite_rectangles_check_lazy_init (extents,
+ surface,
+ source);
+
+ if ((! *should_be_lazy) &&
+ (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
+ _cairo_clip_get_extents (clip))))
+ return FALSE;
+
+ extents->bounded = extents->unbounded;
+
+ extents->original_source_pattern = source;
+ if (! *should_be_lazy) {
+ _cairo_composite_reduce_pattern (source, &extents->source_pattern);
+
+ _cairo_pattern_get_extents (&extents->source_pattern.base,
+ &extents->source);
+ } else
+ _cairo_pattern_get_extents (extents->original_source_pattern,
+ &extents->source);
+
+ if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
+ return FALSE;
+ }
+
+ extents->original_mask_pattern = NULL;
+ extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID;
+ extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */
+ extents->mask_pattern.solid.color.alpha_short = 0xffff;
+
+ return TRUE;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = FALSE;
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ extents->mask = extents->destination;
+
+ extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+ if (_cairo_clip_is_all_clipped (extents->clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (! _cairo_rectangle_intersect (&extents->unbounded,
+ _cairo_clip_get_extents (extents->clip)))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+ _cairo_pattern_sampled_area (&extents->source_pattern.base,
+ &extents->bounded,
+ &extents->source_sample_area);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_paint (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = TRUE;
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ if (! should_be_lazy) {
+ extents->mask = extents->destination;
+
+ extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+ if (_cairo_clip_is_all_clipped (extents->clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+ _cairo_pattern_sampled_area (&extents->source_pattern.base,
+ &extents->bounded,
+ &extents->source_sample_area);
+ } else
+ extents->clip = _cairo_clip_copy (clip);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t ret;
+
+ ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask);
+ if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
+ extents->unbounded = extents->bounded;
+ } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
+ if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+ if (_cairo_clip_is_all_clipped (extents->clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (! _cairo_rectangle_intersect (&extents->unbounded,
+ _cairo_clip_get_extents (extents->clip)))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+ _cairo_pattern_sampled_area (&extents->source_pattern.base,
+ &extents->bounded,
+ &extents->source_sample_area);
+ if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
+ _cairo_pattern_sampled_area (&extents->mask_pattern.base,
+ &extents->bounded,
+ &extents->mask_sample_area);
+ if (extents->mask_sample_area.width == 0 ||
+ extents->mask_sample_area.height == 0) {
+ _cairo_composite_rectangles_fini (extents);
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
+ const cairo_box_t *box)
+{
+ cairo_rectangle_int_t rect;
+ cairo_clip_t *clip;
+
+ _cairo_box_round_to_rectangle (box, &rect);
+ if (rect.x == extents->source.x &&
+ rect.y == extents->source.y &&
+ rect.width == extents->source.width &&
+ rect.height == extents->source.height)
+ {
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ _cairo_rectangle_intersect (&extents->source, &rect);
+
+ rect = extents->bounded;
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) &&
+ extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (rect.width == extents->bounded.width &&
+ rect.height == extents->bounded.height)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
+ extents->unbounded = extents->bounded;
+ } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
+ if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ clip = extents->clip;
+ extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+ if (clip != extents->clip)
+ _cairo_clip_destroy (clip);
+
+ if (_cairo_clip_is_all_clipped (extents->clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (! _cairo_rectangle_intersect (&extents->unbounded,
+ _cairo_clip_get_extents (extents->clip)))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+ _cairo_pattern_sampled_area (&extents->source_pattern.base,
+ &extents->bounded,
+ &extents->source_sample_area);
+ if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
+ _cairo_pattern_sampled_area (&extents->mask_pattern.base,
+ &extents->bounded,
+ &extents->mask_sample_area);
+ if (extents->mask_sample_area.width == 0 ||
+ extents->mask_sample_area.height == 0)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
+ const cairo_box_t *box)
+{
+ cairo_rectangle_int_t mask;
+ cairo_clip_t *clip;
+
+ _cairo_box_round_to_rectangle (box, &mask);
+ if (mask.x == extents->mask.x &&
+ mask.y == extents->mask.y &&
+ mask.width == extents->mask.width &&
+ mask.height == extents->mask.height)
+ {
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ _cairo_rectangle_intersect (&extents->mask, &mask);
+
+ mask = extents->bounded;
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) &&
+ extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (mask.width == extents->bounded.width &&
+ mask.height == extents->bounded.height)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
+ extents->unbounded = extents->bounded;
+ } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
+ if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ clip = extents->clip;
+ extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+ if (clip != extents->clip)
+ _cairo_clip_destroy (clip);
+
+ if (_cairo_clip_is_all_clipped (extents->clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (! _cairo_rectangle_intersect (&extents->unbounded,
+ _cairo_clip_get_extents (extents->clip)))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+ _cairo_pattern_sampled_area (&extents->source_pattern.base,
+ &extents->bounded,
+ &extents->source_sample_area);
+ if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
+ _cairo_pattern_sampled_area (&extents->mask_pattern.base,
+ &extents->bounded,
+ &extents->mask_sample_area);
+ if (extents->mask_sample_area.width == 0 ||
+ extents->mask_sample_area.height == 0)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
+ cairo_surface_t*surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = FALSE;
+
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ extents->original_mask_pattern = mask;
+ _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
+ _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask);
+
+ return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_mask (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t ret;
+ cairo_bool_t should_be_lazy = (op == CAIRO_OPERATOR_SOURCE) ? FALSE : TRUE;
+
+
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ extents->original_mask_pattern = mask;
+
+ if (! should_be_lazy) {
+ _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
+ _cairo_pattern_get_extents (&extents->mask_pattern.base,
+ &extents->mask);
+ return _cairo_composite_rectangles_intersect (extents, clip);
+ }
+
+ _cairo_pattern_get_extents (extents->original_mask_pattern,
+ &extents->mask);
+
+ 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;
+
+ if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE))
+ extents->unbounded = extents->bounded;
+ else if ((extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) &&
+ (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ extents->clip = _cairo_clip_copy (clip);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = FALSE;
+
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask);
+
+ return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_stroke (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = TRUE;
+
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ if (! should_be_lazy) {
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm,
+ &extents->mask);
+ return _cairo_composite_rectangles_intersect (extents, clip);
+ }
+
+ extents->clip = _cairo_clip_copy (clip);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = FALSE;
+
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
+
+ return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_fill (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = TRUE;
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ if (! should_be_lazy) {
+ _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
+
+ return _cairo_composite_rectangles_intersect (extents, clip);
+ }
+
+ extents->clip = _cairo_clip_copy (clip);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_polygon_t *polygon,
+ const cairo_clip_t *clip)
+{
+ cairo_bool_t should_be_lazy = FALSE;
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
+ return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_boxes_t *boxes,
+ const cairo_clip_t *clip)
+{
+ cairo_box_t box;
+ cairo_bool_t should_be_lazy = FALSE;
+
+ if (! _cairo_composite_rectangles_init (extents,
+ surface, op, source, clip,
+ &should_be_lazy))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ _cairo_boxes_extents (boxes, &box);
+ _cairo_box_round_to_rectangle (&box, &extents->mask);
+ return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ cairo_bool_t *overlap)
+{
+ cairo_status_t status;
+ cairo_bool_t should_be_lazy = FALSE;
+
+ if (! _cairo_composite_rectangles_init (extents, surface, op, source,
+ clip, &should_be_lazy))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ /* Computing the exact bbox and the overlap is expensive.
+ * First perform a cheap test to see if the glyphs are all clipped out.
+ */
+ if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK &&
+ _cairo_scaled_font_glyph_approximate_extents (scaled_font,
+ glyphs, num_glyphs,
+ &extents->mask))
+ {
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+ glyphs, num_glyphs,
+ &extents->mask,
+ overlap);
+ if (unlikely (status))
+ return status;
+
+ if (overlap && *overlap &&
+ scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE &&
+ _cairo_pattern_is_opaque_solid (&extents->source_pattern.base))
+ {
+ *overlap = FALSE;
+ }
+
+ return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_lazy_init_for_glyphs (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ cairo_bool_t *overlap)
+{
+ cairo_status_t status;
+ cairo_bool_t should_be_lazy = TRUE;
+
+ if (! _cairo_composite_rectangles_init (extents, surface, op, source,
+ clip, &should_be_lazy))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE &&
+ _cairo_scaled_font_glyph_approximate_extents (scaled_font,
+ glyphs, num_glyphs,
+ &extents->source))
+ {
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+ glyphs, num_glyphs,
+ &extents->source,
+ overlap);
+ if (unlikely (status))
+ return status;
+
+ if (*overlap &&
+ scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE &&
+ _cairo_pattern_is_opaque_solid (extents->original_source_pattern))
+ {
+ *overlap = FALSE;
+ }
+
+ extents->clip = _cairo_clip_copy (clip);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
+ cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t extents;
+ cairo_box_t box;
+
+ if (clip == NULL)
+ return TRUE;
+
+ extents = composite->destination;
+ if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
+ _cairo_rectangle_intersect (&extents, &composite->source);
+ if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
+ _cairo_rectangle_intersect (&extents, &composite->mask);
+
+ _cairo_box_from_rectangle (&box, &extents);
+ return _cairo_clip_contains_box (clip, &box);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
+ cairo_boxes_t *damage)
+{
+ cairo_int_status_t status;
+ int n;
+
+ for (n = 0; n < composite->clip->num_boxes; n++) {
+ status = _cairo_boxes_add (damage,
+ CAIRO_ANTIALIAS_NONE,
+ &composite->clip->boxes[n]);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
diff --git a/src/cairo-compositor-private.h b/src/cairo-compositor-private.h
new file mode 100755
index 000000000..3a461bf06
--- /dev/null
+++ b/src/cairo-compositor-private.h
@@ -0,0 +1,378 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_COMPOSITOR_PRIVATE_H
+#define CAIRO_COMPOSITOR_PRIVATE_H
+
+#include "cairo-composite-rectangles-private.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct {
+ cairo_scaled_font_t *font;
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+ cairo_bool_t use_mask;
+ cairo_rectangle_int_t extents;
+} cairo_composite_glyphs_info_t;
+
+struct cairo_compositor {
+ const cairo_compositor_t *delegate;
+
+ cairo_warn cairo_int_status_t
+ (*paint) (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents);
+
+ cairo_warn cairo_int_status_t
+ (*mask) (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents);
+
+ cairo_warn cairo_int_status_t
+ (*stroke) (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias);
+
+ cairo_warn cairo_int_status_t
+ (*fill) (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias);
+
+ cairo_warn cairo_int_status_t
+ (*glyphs) (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap);
+ cairo_bool_t lazy_init;
+};
+
+struct cairo_mask_compositor {
+ cairo_compositor_t base;
+
+ cairo_int_status_t (*acquire) (void *surface);
+ cairo_int_status_t (*release) (void *surface);
+
+ cairo_int_status_t (*set_clip_region) (void *surface,
+ cairo_region_t *clip_region);
+
+ cairo_surface_t * (*pattern_to_surface) (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y);
+
+ cairo_int_status_t (*draw_image_boxes) (void *surface,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy);
+
+ cairo_int_status_t (*copy_boxes) (void *surface,
+ cairo_surface_t *src,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents,
+ int dx, int dy);
+
+ cairo_int_status_t
+ (*fill_rectangles) (void *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rectangles,
+ int num_rects);
+
+ cairo_int_status_t
+ (*fill_boxes) (void *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes);
+
+ cairo_int_status_t
+ (*check_composite) (const cairo_composite_rectangles_t *extents);
+
+ cairo_int_status_t
+ (*composite) (void *dst,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ cairo_surface_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height);
+
+ cairo_int_status_t
+ (*composite_boxes) (void *surface,
+ cairo_operator_t op,
+ cairo_surface_t *source,
+ cairo_surface_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents);
+
+ cairo_int_status_t
+ (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs);
+ cairo_int_status_t
+ (*composite_glyphs) (void *surface,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info);
+};
+
+struct cairo_traps_compositor {
+ cairo_compositor_t base;
+
+ cairo_int_status_t
+ (*acquire) (void *surface);
+
+ cairo_int_status_t
+ (*release) (void *surface);
+
+ cairo_int_status_t
+ (*set_clip_region) (void *surface,
+ cairo_region_t *clip_region);
+
+ cairo_surface_t *
+ (*pattern_to_surface) (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y);
+
+ cairo_int_status_t (*draw_image_boxes) (void *surface,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy);
+
+ cairo_int_status_t (*copy_boxes) (void *surface,
+ cairo_surface_t *src,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents,
+ int dx, int dy);
+
+ cairo_int_status_t
+ (*fill_boxes) (void *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes);
+
+ cairo_int_status_t
+ (*check_composite) (const cairo_composite_rectangles_t *extents);
+
+ cairo_int_status_t
+ (*composite) (void *dst,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ cairo_surface_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height);
+ cairo_int_status_t
+ (*lerp) (void *_dst,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height);
+ cairo_int_status_t
+ (*lerp_color_glyph) (void *_dst,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height);
+
+ cairo_int_status_t
+ (*composite_boxes) (void *surface,
+ cairo_operator_t op,
+ cairo_surface_t *source,
+ cairo_surface_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents);
+
+ cairo_int_status_t
+ (*composite_traps) (void *dst,
+ cairo_operator_t op,
+ cairo_surface_t *source,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps);
+
+ cairo_int_status_t
+ (*composite_tristrip) (void *dst,
+ cairo_operator_t op,
+ cairo_surface_t *source,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_tristrip_t *tristrip);
+
+ cairo_int_status_t
+ (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs);
+ cairo_int_status_t
+ (*composite_glyphs) (void *surface,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info);
+};
+
+cairo_private extern const cairo_compositor_t __cairo_no_compositor;
+cairo_private extern const cairo_compositor_t _cairo_fallback_compositor;
+
+cairo_private void
+_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
+ const cairo_compositor_t *delegate);
+
+cairo_private void
+_cairo_shape_mask_compositor_init (cairo_compositor_t *compositor,
+ const cairo_compositor_t *delegate);
+
+cairo_private void
+_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor,
+ const cairo_compositor_t *delegate);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_COMPOSITOR_PRIVATE_H */
diff --git a/src/cairo-compositor.c b/src/cairo-compositor.c
new file mode 100755
index 000000000..0c4d34cc0
--- /dev/null
+++ b/src/cairo-compositor.c
@@ -0,0 +1,391 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-damage-private.h"
+#include "cairo-error-private.h"
+
+cairo_int_status_t
+_cairo_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+ cairo_bool_t initialized = TRUE;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (compositor->lazy_init) {
+ status = _cairo_composite_rectangles_lazy_init_for_paint (&extents,
+ surface,
+ op, source,
+ clip);
+ initialized = FALSE;
+ }
+ else
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ surface,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ do {
+ while (compositor->paint == NULL)
+ compositor = compositor->delegate;
+
+ if (! compositor->lazy_init && ! initialized) {
+ /* XXX: we should do better instead of re-init */
+ _cairo_composite_rectangles_fini (&extents);
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ surface,
+ op, source,
+ clip);
+ initialized = TRUE;
+
+ if (unlikely (status))
+ return status;
+ }
+
+ status = compositor->paint (compositor, &extents);
+
+ compositor = compositor->delegate;
+ } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
+ TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
+ __FUNCTION__,
+ extents.unbounded.x, extents.unbounded.y,
+ extents.unbounded.width, extents.unbounded.height));
+ surface->damage = _cairo_damage_add_rectangle (surface->damage,
+ &extents.unbounded);
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+ cairo_bool_t initialized = TRUE;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (compositor->lazy_init) {
+ status = _cairo_composite_rectangles_lazy_init_for_mask (&extents,
+ surface,
+ op, source,
+ mask, clip);
+ initialized = FALSE;
+ } else
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ surface,
+ op, source,
+ mask, clip);
+ if (unlikely (status))
+ return status;
+
+ do {
+ while (compositor->mask == NULL)
+ compositor = compositor->delegate;
+
+ if (! compositor->lazy_init && ! initialized) {
+ /* XXX: we should do better instead of re-init */
+ _cairo_composite_rectangles_fini (&extents);
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ surface,
+ op, source,
+ mask, clip);
+ initialized = TRUE;
+
+ if (unlikely (status))
+ return status;
+ }
+
+ status = compositor->mask (compositor, &extents);
+
+ compositor = compositor->delegate;
+ } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
+ TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
+ __FUNCTION__,
+ extents.unbounded.x, extents.unbounded.y,
+ extents.unbounded.width, extents.unbounded.height));
+ surface->damage = _cairo_damage_add_rectangle (surface->damage,
+ &extents.unbounded);
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+ cairo_bool_t initialized = TRUE;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (_cairo_pen_vertices_needed (tolerance, style->line_width/2, ctm) <= 1)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (compositor->lazy_init) {
+ status = _cairo_composite_rectangles_lazy_init_for_stroke (&extents,
+ surface,
+ op, source,
+ path, style,
+ ctm, clip);
+ initialized = FALSE;
+ }
+ else
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ surface,
+ op, source,
+ path, style,
+ ctm, clip);
+ if (unlikely (status))
+ return status;
+
+ do {
+ while (compositor->stroke == NULL)
+ compositor = compositor->delegate;
+
+ if (! compositor->lazy_init && ! initialized) {
+ /* XXX: we should do better instead of re-init */
+ _cairo_composite_rectangles_fini (&extents);
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ surface,
+ op,
+ source,
+ path,
+ style,
+ ctm,
+ clip);
+ initialized = TRUE;
+
+ if (unlikely (status))
+ return status;
+ }
+
+ status = compositor->stroke (compositor, &extents,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias);
+
+ compositor = compositor->delegate;
+ } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
+ TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
+ __FUNCTION__,
+ extents.unbounded.x, extents.unbounded.y,
+ extents.unbounded.width, extents.unbounded.height));
+ surface->damage = _cairo_damage_add_rectangle (surface->damage,
+ &extents.unbounded);
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+ cairo_bool_t initialized = TRUE;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (compositor->lazy_init) {
+ status = _cairo_composite_rectangles_lazy_init_for_fill (&extents,
+ surface,
+ op, source,
+ path, clip);
+ initialized = FALSE;
+ }
+ else
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ surface,
+ op, source,
+ path, clip);
+ if (unlikely (status))
+ return status;
+
+ do {
+ while (compositor->fill == NULL)
+ compositor = compositor->delegate;
+
+ if (! compositor->lazy_init && ! initialized) {
+ /* XXX: we should do better instead of re-init */
+ _cairo_composite_rectangles_fini (&extents);
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ surface,
+ op, source,
+ path, clip);
+ initialized = TRUE;
+
+ if (unlikely (status))
+ return status;
+ }
+
+ status = compositor->fill (compositor, &extents,
+ path, fill_rule, tolerance, antialias);
+
+ compositor = compositor->delegate;
+ } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
+ TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
+ __FUNCTION__,
+ extents.unbounded.x, extents.unbounded.y,
+ extents.unbounded.width, extents.unbounded.height));
+ surface->damage = _cairo_damage_add_rectangle (surface->damage,
+ &extents.unbounded);
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_bool_t overlap;
+ cairo_int_status_t status;
+ cairo_bool_t initialized = TRUE;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (compositor->lazy_init) {
+ status = _cairo_composite_rectangles_lazy_init_for_glyphs (&extents, surface,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip, &overlap);
+ initialized = FALSE;
+ } else
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip, &overlap);
+ if (unlikely (status))
+ return status;
+
+ do {
+ while (compositor->glyphs == NULL)
+ compositor = compositor->delegate;
+
+ if (! compositor->lazy_init && ! initialized) {
+ /* XXX: we should do better instead of re-init */
+ _cairo_composite_rectangles_fini (&extents);
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip, &overlap);
+ initialized = TRUE;
+
+ if (unlikely (status))
+ return status;
+ }
+
+ status = compositor->glyphs (compositor, &extents,
+ scaled_font, glyphs, num_glyphs, overlap);
+
+ compositor = compositor->delegate;
+ } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
+ TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
+ __FUNCTION__,
+ extents.unbounded.x, extents.unbounded.y,
+ extents.unbounded.width, extents.unbounded.height));
+ surface->damage = _cairo_damage_add_rectangle (surface->damage,
+ &extents.unbounded);
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+
+ return status;
+}
diff --git a/src/cairo-contour-inline.h b/src/cairo-contour-inline.h
new file mode 100755
index 000000000..7972c1ac5
--- /dev/null
+++ b/src/cairo-contour-inline.h
@@ -0,0 +1,80 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CONTOUR_INLINE_H
+#define CAIRO_CONTOUR_INLINE_H
+
+#include "cairo-contour-private.h"
+
+CAIRO_BEGIN_DECLS
+
+static inline cairo_int_status_t
+_cairo_contour_add_point (cairo_contour_t *contour,
+ const cairo_point_t *point)
+{
+ struct _cairo_contour_chain *tail = contour->tail;
+
+ if (unlikely (tail->num_points == tail->size_points))
+ return __cairo_contour_add_point (contour, point);
+
+ tail->points[tail->num_points++] = *point;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static inline cairo_point_t *
+_cairo_contour_first_point (cairo_contour_t *c)
+{
+ return &c->chain.points[0];
+}
+
+static inline cairo_point_t *
+_cairo_contour_last_point (cairo_contour_t *c)
+{
+ return &c->tail->points[c->tail->num_points-1];
+}
+
+static inline void
+_cairo_contour_remove_last_point (cairo_contour_t *contour)
+{
+ if (contour->chain.num_points == 0)
+ return;
+
+ if (--contour->tail->num_points == 0)
+ __cairo_contour_remove_last_chain (contour);
+}
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_CONTOUR_INLINE_H */
diff --git a/src/cairo-contour-private.h b/src/cairo-contour-private.h
new file mode 100755
index 000000000..1dfc46f3a
--- /dev/null
+++ b/src/cairo-contour-private.h
@@ -0,0 +1,124 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CONTOUR_PRIVATE_H
+#define CAIRO_CONTOUR_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-private.h"
+
+#include <stdio.h>
+
+CAIRO_BEGIN_DECLS
+
+/* A contour is simply a closed chain of points that divide the infinite plane
+ * into inside and outside. Each contour is a simple polygon, that is it
+ * contains no holes or self-intersections, but maybe either concave or convex.
+ */
+
+struct _cairo_contour_chain {
+ cairo_point_t *points;
+ int num_points, size_points;
+ struct _cairo_contour_chain *next;
+};
+
+struct _cairo_contour_iter {
+ cairo_point_t *point;
+ cairo_contour_chain_t *chain;
+};
+
+struct _cairo_contour {
+ cairo_list_t next;
+ int direction;
+ cairo_contour_chain_t chain, *tail;
+
+ cairo_point_t embedded_points[64];
+};
+
+/* Initial definition of a shape is a set of contours (some representing holes) */
+struct _cairo_shape {
+ cairo_list_t contours;
+};
+
+typedef struct _cairo_shape cairo_shape_t;
+
+#if 0
+cairo_private cairo_status_t
+_cairo_shape_init_from_polygon (cairo_shape_t *shape,
+ const cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_shape_reduce (cairo_shape_t *shape, double tolerance);
+#endif
+
+cairo_private void
+_cairo_contour_init (cairo_contour_t *contour,
+ int direction);
+
+cairo_private cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+ const cairo_point_t *point);
+
+cairo_private void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance);
+
+cairo_private void
+_cairo_contour_reverse (cairo_contour_t *contour);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+ const cairo_contour_t *src);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+ const cairo_contour_t *src);
+
+cairo_private void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_contour_reset (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_contour_fini (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_CONTOUR_PRIVATE_H */
diff --git a/src/cairo-contour.c b/src/cairo-contour.c
new file mode 100755
index 000000000..d356f4f65
--- /dev/null
+++ b/src/cairo-contour.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-inline.h"
+#include "cairo-contour-inline.h"
+#include "cairo-contour-private.h"
+
+void
+_cairo_contour_init (cairo_contour_t *contour,
+ int direction)
+{
+ contour->direction = direction;
+ contour->chain.points = contour->embedded_points;
+ contour->chain.next = NULL;
+ contour->chain.num_points = 0;
+ contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points);
+ contour->tail = &contour->chain;
+}
+
+cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+ const cairo_point_t *point)
+{
+ cairo_contour_chain_t *tail = contour->tail;
+ cairo_contour_chain_t *next;
+
+ assert (tail->next == NULL);
+
+ next = _cairo_malloc_ab_plus_c (tail->size_points*2,
+ sizeof (cairo_point_t),
+ sizeof (cairo_contour_chain_t));
+ if (unlikely (next == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ next->size_points = tail->size_points*2;
+ next->num_points = 1;
+ next->points = (cairo_point_t *)(next+1);
+ next->next = NULL;
+ tail->next = next;
+ contour->tail = next;
+
+ next->points[0] = *point;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static void
+first_inc (cairo_contour_t *contour,
+ cairo_point_t **p,
+ cairo_contour_chain_t **chain)
+{
+ if (*p == (*chain)->points + (*chain)->num_points) {
+ assert ((*chain)->next);
+ *chain = (*chain)->next;
+ *p = &(*chain)->points[0];
+ } else
+ ++*p;
+}
+
+static void
+last_dec (cairo_contour_t *contour,
+ cairo_point_t **p,
+ cairo_contour_chain_t **chain)
+{
+ if (*p == (*chain)->points) {
+ cairo_contour_chain_t *prev;
+ assert (*chain != &contour->chain);
+ for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
+ ;
+ *chain = prev;
+ *p = &(*chain)->points[(*chain)->num_points-1];
+ } else
+ --*p;
+}
+
+void
+_cairo_contour_reverse (cairo_contour_t *contour)
+{
+ cairo_contour_chain_t *first_chain, *last_chain;
+ cairo_point_t *first, *last;
+
+ contour->direction = -contour->direction;
+
+ if (contour->chain.num_points <= 1)
+ return;
+
+ first_chain = &contour->chain;
+ last_chain = contour->tail;
+
+ first = &first_chain->points[0];
+ last = &last_chain->points[last_chain->num_points-1];
+
+ while (first != last) {
+ cairo_point_t p;
+
+ p = *first;
+ *first = *last;
+ *last = p;
+
+ first_inc (contour, &first, &first_chain);
+ last_dec (contour, &last, &last_chain);
+ }
+}
+
+cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+ const cairo_contour_t *src)
+{
+ const cairo_contour_chain_t *chain;
+ cairo_int_status_t status;
+ int i;
+
+ for (chain = &src->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ status = _cairo_contour_add_point (dst, &chain->points[i]);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static inline cairo_bool_t
+iter_next (cairo_contour_iter_t *iter)
+{
+ if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
+ iter->chain = iter->chain->next;
+ if (iter->chain == NULL)
+ return FALSE;
+
+ iter->point = &iter->chain->points[0];
+ return TRUE;
+ } else {
+ iter->point++;
+ return TRUE;
+ }
+}
+
+static cairo_bool_t
+iter_equal (const cairo_contour_iter_t *i1,
+ const cairo_contour_iter_t *i2)
+{
+ return i1->chain == i2->chain && i1->point == i2->point;
+}
+
+static void
+iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour)
+{
+ iter->chain = &contour->chain;
+ iter->point = &contour->chain.points[0];
+}
+
+static void
+iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour)
+{
+ iter->chain = contour->tail;
+ iter->point = &contour->tail->points[contour->tail->num_points-1];
+}
+
+static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour,
+ const cairo_contour_chain_t *chain)
+{
+ const cairo_contour_chain_t *prev;
+
+ if (chain == &contour->chain)
+ return NULL;
+
+ for (prev = &contour->chain; prev->next != chain; prev = prev->next)
+ ;
+
+ return prev;
+}
+
+cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+ const cairo_contour_t *src)
+{
+ const cairo_contour_chain_t *last;
+ cairo_int_status_t status;
+ int i;
+
+ if (src->chain.num_points == 0)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ for (last = src->tail; last; last = prev_const_chain (src, last)) {
+ for (i = last->num_points-1; i >= 0; i--) {
+ status = _cairo_contour_add_point (dst, &last->points[i]);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_uint64_t
+point_distance_sq (const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ int32_t dx = p1->x - p2->x;
+ int32_t dy = p1->y - p2->y;
+ return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
+}
+
+#define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX)
+#define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX)
+
+static cairo_bool_t
+_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
+ const cairo_contour_iter_t *first,
+ const cairo_contour_iter_t *last)
+{
+ cairo_contour_iter_t iter, furthest;
+ uint64_t max_error;
+ int x0, y0;
+ int nx, ny;
+ int count;
+
+ iter = *first;
+ iter_next (&iter);
+ if (iter_equal (&iter, last))
+ return FALSE;
+
+ x0 = first->point->x;
+ y0 = first->point->y;
+ nx = last->point->y - y0;
+ ny = x0 - last->point->x;
+
+ count = 0;
+ max_error = 0;
+ do {
+ cairo_point_t *p = iter.point;
+ if (! DELETED(p)) {
+ uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y);
+ if (d * d > max_error) {
+ max_error = d * d;
+ furthest = iter;
+ }
+ count++;
+ }
+ iter_next (&iter);
+ } while (! iter_equal (&iter, last));
+ if (count == 0)
+ return FALSE;
+
+ if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) {
+ cairo_bool_t simplified;
+
+ simplified = FALSE;
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ first, &furthest);
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ &furthest, last);
+ return simplified;
+ } else {
+ iter = *first;
+ iter_next (&iter);
+ do {
+ MARK_DELETED (iter.point);
+ iter_next (&iter);
+ } while (! iter_equal (&iter, last));
+
+ return TRUE;
+ }
+}
+
+void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
+{
+ cairo_contour_chain_t *chain;
+ cairo_point_t *last = NULL;
+ cairo_contour_iter_t iter, furthest;
+ cairo_bool_t simplified;
+ uint64_t max = 0;
+ int i;
+
+ if (contour->chain.num_points <= 2)
+ return;
+
+ tolerance = tolerance * CAIRO_FIXED_ONE;
+ tolerance *= tolerance;
+
+ /* stage 1: vertex reduction */
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ if (last == NULL ||
+ point_distance_sq (last, &chain->points[i]) > tolerance) {
+ last = &chain->points[i];
+ } else {
+ MARK_DELETED (&chain->points[i]);
+ }
+ }
+ }
+
+ /* stage2: polygon simplification using Douglas-Peucker */
+ simplified = FALSE;
+ do {
+ last = &contour->chain.points[0];
+ iter_init (&furthest, contour);
+ max = 0;
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ uint64_t d;
+
+ if (DELETED (&chain->points[i]))
+ continue;
+
+ d = point_distance_sq (last, &chain->points[i]);
+ if (d > max) {
+ furthest.chain = chain;
+ furthest.point = &chain->points[i];
+ max = d;
+ }
+ }
+ }
+ assert (max);
+
+ simplified = FALSE;
+ iter_init (&iter, contour);
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ &iter, &furthest);
+
+ iter_init_last (&iter, contour);
+ if (! iter_equal (&furthest, &iter))
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ &furthest, &iter);
+ } while (simplified);
+
+ iter_init (&iter, contour);
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ int num_points = chain->num_points;
+ chain->num_points = 0;
+ for (i = 0; i < num_points; i++) {
+ if (! DELETED(&chain->points[i])) {
+ if (iter.point != &chain->points[i])
+ *iter.point = chain->points[i];
+ iter.chain->num_points++;
+ iter_next (&iter);
+ }
+ }
+ }
+
+ if (iter.chain) {
+ cairo_contour_chain_t *next;
+
+ for (chain = iter.chain->next; chain; chain = next) {
+ next = chain->next;
+ free (chain);
+ }
+
+ iter.chain->next = NULL;
+ contour->tail = iter.chain;
+ }
+}
+
+void
+_cairo_contour_reset (cairo_contour_t *contour)
+{
+ _cairo_contour_fini (contour);
+ _cairo_contour_init (contour, contour->direction);
+}
+
+void
+_cairo_contour_fini (cairo_contour_t *contour)
+{
+ cairo_contour_chain_t *chain, *next;
+
+ for (chain = contour->chain.next; chain; chain = next) {
+ next = chain->next;
+ free (chain);
+ }
+}
+
+void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
+{
+ cairo_contour_chain_t *chain;
+ int num_points, size_points;
+ int i;
+
+ num_points = 0;
+ size_points = 0;
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ num_points += chain->num_points;
+ size_points += chain->size_points;
+ }
+
+ fprintf (file, "contour: direction=%d, num_points=%d / %d\n",
+ contour->direction, num_points, size_points);
+
+ num_points = 0;
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ fprintf (file, " [%d] = (%f, %f)\n",
+ num_points++,
+ _cairo_fixed_to_double (chain->points[i].x),
+ _cairo_fixed_to_double (chain->points[i].y));
+ }
+ }
+}
+
+void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour)
+{
+ cairo_contour_chain_t *chain;
+
+ if (contour->tail == &contour->chain)
+ return;
+
+ for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next)
+ ;
+ free (contour->tail);
+ contour->tail = chain;
+ chain->next = NULL;
+}
diff --git a/src/cairo-damage-private.h b/src/cairo-damage-private.h
new file mode 100755
index 000000000..97b177e86
--- /dev/null
+++ b/src/cairo-damage-private.h
@@ -0,0 +1,85 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_DAMAGE_PRIVATE_H
+#define CAIRO_DAMAGE_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#include <pixman.h>
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_damage {
+ cairo_status_t status;
+ cairo_region_t *region;
+
+ int dirty, remain;
+ struct _cairo_damage_chunk {
+ struct _cairo_damage_chunk *next;
+ cairo_box_t *base;
+ int count;
+ int size;
+ } chunks, *tail;
+ cairo_box_t boxes[32];
+};
+
+cairo_private cairo_damage_t *
+_cairo_damage_create (void);
+
+cairo_private cairo_damage_t *
+_cairo_damage_create_in_error (cairo_status_t status);
+
+cairo_private cairo_damage_t *
+_cairo_damage_add_box (cairo_damage_t *damage,
+ const cairo_box_t *box);
+
+cairo_private cairo_damage_t *
+_cairo_damage_add_rectangle (cairo_damage_t *damage,
+ const cairo_rectangle_int_t *rect);
+
+cairo_private cairo_damage_t *
+_cairo_damage_add_region (cairo_damage_t *damage,
+ const cairo_region_t *region);
+
+cairo_private cairo_damage_t *
+_cairo_damage_reduce (cairo_damage_t *damage);
+
+cairo_private void
+_cairo_damage_destroy (cairo_damage_t *damage);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_DAMAGE_PRIVATE_H */
diff --git a/src/cairo-damage.c b/src/cairo-damage.c
new file mode 100755
index 000000000..63191fee9
--- /dev/null
+++ b/src/cairo-damage.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-damage-private.h"
+#include "cairo-region-private.h"
+
+static const cairo_damage_t __cairo_damage__nil = { CAIRO_STATUS_NO_MEMORY };
+
+cairo_damage_t *
+_cairo_damage_create_in_error (cairo_status_t status)
+{
+ _cairo_error_throw (status);
+ return (cairo_damage_t *) &__cairo_damage__nil;
+}
+
+cairo_damage_t *
+_cairo_damage_create (void)
+{
+ cairo_damage_t *damage;
+
+ damage = malloc (sizeof (*damage));
+ if (unlikely (damage == NULL)) {
+ _cairo_error_throw(CAIRO_STATUS_NO_MEMORY);
+ return (cairo_damage_t *) &__cairo_damage__nil;
+ }
+
+ damage->status = CAIRO_STATUS_SUCCESS;
+ damage->region = NULL;
+ damage->dirty = 0;
+ damage->tail = &damage->chunks;
+ damage->chunks.base = damage->boxes;
+ damage->chunks.size = ARRAY_LENGTH(damage->boxes);
+ damage->chunks.count = 0;
+ damage->chunks.next = NULL;
+
+ damage->remain = damage->chunks.size;
+
+ return damage;
+}
+
+void
+_cairo_damage_destroy (cairo_damage_t *damage)
+{
+ struct _cairo_damage_chunk *chunk, *next;
+
+ if (damage == (cairo_damage_t *) &__cairo_damage__nil)
+ return;
+
+ for (chunk = damage->chunks.next; chunk != NULL; chunk = next) {
+ next = chunk->next;
+ free (chunk);
+ }
+ cairo_region_destroy (damage->region);
+ free (damage);
+}
+
+static cairo_damage_t *
+_cairo_damage_add_boxes(cairo_damage_t *damage,
+ const cairo_box_t *boxes,
+ int count)
+{
+ struct _cairo_damage_chunk *chunk;
+ int n, size;
+
+ TRACE ((stderr, "%s x%d\n", __FUNCTION__, count));
+
+ if (damage == NULL)
+ damage = _cairo_damage_create ();
+ if (damage->status)
+ return damage;
+
+ damage->dirty += count;
+
+ n = count;
+ if (n > damage->remain)
+ n = damage->remain;
+
+ memcpy (damage->tail->base + damage->tail->count, boxes,
+ n * sizeof (cairo_box_t));
+
+ count -= n;
+ damage->tail->count += n;
+ damage->remain -= n;
+
+ if (count == 0)
+ return damage;
+
+ size = 2 * damage->tail->size;
+ if (size < count)
+ size = (count + 64) & ~63;
+
+ chunk = malloc (sizeof (*chunk) + sizeof (cairo_box_t) * size);
+ if (unlikely (chunk == NULL)) {
+ _cairo_damage_destroy (damage);
+ return (cairo_damage_t *) &__cairo_damage__nil;
+ }
+
+ chunk->next = NULL;
+ chunk->base = (cairo_box_t *) (chunk + 1);
+ chunk->size = size;
+ chunk->count = count;
+
+ damage->tail->next = chunk;
+ damage->tail = chunk;
+
+ memcpy (damage->tail->base, boxes + n,
+ count * sizeof (cairo_box_t));
+ damage->remain = size - count;
+
+ return damage;
+}
+
+cairo_damage_t *
+_cairo_damage_add_box(cairo_damage_t *damage,
+ const cairo_box_t *box)
+{
+ TRACE ((stderr, "%s: (%d, %d),(%d, %d)\n", __FUNCTION__,
+ box->p1.x, box->p1.y, box->p2.x, box->p2.y));
+
+ return _cairo_damage_add_boxes(damage, box, 1);
+}
+
+cairo_damage_t *
+_cairo_damage_add_rectangle(cairo_damage_t *damage,
+ const cairo_rectangle_int_t *r)
+{
+ cairo_box_t box;
+
+ TRACE ((stderr, "%s: (%d, %d)x(%d, %d)\n", __FUNCTION__,
+ r->x, r->y, r->width, r->height));
+
+ box.p1.x = r->x;
+ box.p1.y = r->y;
+ box.p2.x = r->x + r->width;
+ box.p2.y = r->y + r->height;
+
+ return _cairo_damage_add_boxes(damage, &box, 1);
+}
+
+cairo_damage_t *
+_cairo_damage_add_region (cairo_damage_t *damage,
+ const cairo_region_t *region)
+{
+ cairo_box_t *boxes;
+ int nbox;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ boxes = _cairo_region_get_boxes (region, &nbox);
+ return _cairo_damage_add_boxes(damage, boxes, nbox);
+}
+
+cairo_damage_t *
+_cairo_damage_reduce (cairo_damage_t *damage)
+{
+ cairo_box_t *free_boxes = NULL;
+ cairo_box_t *boxes, *b;
+ struct _cairo_damage_chunk *chunk, *last;
+
+ TRACE ((stderr, "%s: dirty=%d\n", __FUNCTION__,
+ damage ? damage->dirty : -1));
+ if (damage == NULL || damage->status || !damage->dirty)
+ return damage;
+
+ if (damage->region) {
+ cairo_region_t *region;
+
+ region = damage->region;
+ damage->region = NULL;
+
+ damage = _cairo_damage_add_region (damage, region);
+ cairo_region_destroy (region);
+
+ if (unlikely (damage->status))
+ return damage;
+ }
+
+ boxes = damage->tail->base;
+ if (damage->dirty > damage->tail->size) {
+ boxes = free_boxes = malloc (damage->dirty * sizeof (cairo_box_t));
+ if (unlikely (boxes == NULL)) {
+ _cairo_damage_destroy (damage);
+ return (cairo_damage_t *) &__cairo_damage__nil;
+ }
+
+ b = boxes;
+ last = NULL;
+ } else {
+ b = boxes + damage->tail->count;
+ last = damage->tail;
+ }
+
+ for (chunk = &damage->chunks; chunk != last; chunk = chunk->next) {
+ memcpy (b, chunk->base, chunk->count * sizeof (cairo_box_t));
+ b += chunk->count;
+ }
+
+ damage->region = _cairo_region_create_from_boxes (boxes, damage->dirty);
+ free (free_boxes);
+
+ if (unlikely (damage->region->status)) {
+ _cairo_damage_destroy (damage);
+ return (cairo_damage_t *) &__cairo_damage__nil;
+ }
+
+ damage->dirty = 0;
+ return damage;
+}
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
new file mode 100755
index 000000000..33d46aa3f
--- /dev/null
+++ b/src/cairo-debug.c
@@ -0,0 +1,304 @@
+/* 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"
+#include "cairo-image-surface-private.h"
+
+/**
+ * cairo_debug_reset_static_data:
+ *
+ * Resets all static data within cairo to its original state,
+ * (ie. identical to the state at the time of program invocation). For
+ * example, all caches within cairo will be flushed empty.
+ *
+ * This function is intended to be useful when using memory-checking
+ * tools such as valgrind. When valgrind's memcheck analyzes a
+ * cairo-using program without a call to cairo_debug_reset_static_data(),
+ * it will report all data reachable via cairo's static objects as
+ * "still reachable". Calling cairo_debug_reset_static_data() just prior
+ * to program termination will make it easier to get squeaky clean
+ * reports from valgrind.
+ *
+ * WARNING: It is only safe to call this function when there are no
+ * active cairo objects remaining, (ie. the appropriate destroy
+ * functions have been called as necessary). If there are active cairo
+ * objects, this call is likely to cause a crash, (eg. an assertion
+ * failure due to a hash table being destroyed when non-empty).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_debug_reset_static_data (void)
+{
+ CAIRO_MUTEX_INITIALIZE ();
+
+ _cairo_scaled_font_map_destroy ();
+
+ _cairo_toy_font_face_reset_static_data ();
+
+#if CAIRO_HAS_FT_FONT
+ _cairo_ft_font_reset_static_data ();
+#endif
+
+#if CAIRO_HAS_WIN32_FONT
+ _cairo_win32_font_reset_static_data ();
+#endif
+
+ _cairo_intern_string_reset_static_data ();
+
+ _cairo_scaled_font_reset_static_data ();
+
+ _cairo_pattern_reset_static_data ();
+
+ _cairo_clip_reset_static_data ();
+
+ _cairo_image_reset_static_data ();
+
+#if CAIRO_HAS_DRM_SURFACE
+ _cairo_drm_device_reset_static_data ();
+#endif
+
+ _cairo_default_context_reset_static_data ();
+
+#if CAIRO_HAS_COGL_SURFACE
+ _cairo_cogl_context_reset_static_data ();
+#endif
+
+ CAIRO_MUTEX_FINALIZE ();
+}
+
+#if HAVE_VALGRIND
+void
+_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface)
+{
+ const cairo_image_surface_t *image = (cairo_image_surface_t *) surface;
+ const uint8_t *bits;
+ int row, width;
+
+ if (surface == NULL)
+ return;
+
+ if (! RUNNING_ON_VALGRIND)
+ return;
+
+ bits = image->data;
+ switch (image->format) {
+ case CAIRO_FORMAT_A1:
+ width = (image->width + 7)/8;
+ break;
+ case CAIRO_FORMAT_A8:
+ width = image->width;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ width = image->width*2;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_ARGB32:
+ width = image->width*4;
+ break;
+ case CAIRO_FORMAT_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;
+ cairo_box_t box;
+
+ fprintf (stream,
+ "path: extents=(%f, %f), (%f, %f)\n",
+ _cairo_fixed_to_double (path->extents.p1.x),
+ _cairo_fixed_to_double (path->extents.p1.y),
+ _cairo_fixed_to_double (path->extents.p2.x),
+ _cairo_fixed_to_double (path->extents.p2.y));
+
+ status = _cairo_path_fixed_interpret (path,
+ _print_move_to,
+ _print_line_to,
+ _print_curve_to,
+ _print_close,
+ stream);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ if (_cairo_path_fixed_is_box (path, &box)) {
+ fprintf (stream, "[box (%d, %d), (%d, %d)]",
+ box.p1.x, box.p1.y, box.p2.x, box.p2.y);
+ }
+
+ printf ("\n");
+}
+
+void
+_cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon)
+{
+ int n;
+
+ fprintf (stream,
+ "polygon: extents=(%f, %f), (%f, %f)\n",
+ _cairo_fixed_to_double (polygon->extents.p1.x),
+ _cairo_fixed_to_double (polygon->extents.p1.y),
+ _cairo_fixed_to_double (polygon->extents.p2.x),
+ _cairo_fixed_to_double (polygon->extents.p2.y));
+ if (polygon->num_limits) {
+ fprintf (stream,
+ " : limit=(%f, %f), (%f, %f) x %d\n",
+ _cairo_fixed_to_double (polygon->limit.p1.x),
+ _cairo_fixed_to_double (polygon->limit.p1.y),
+ _cairo_fixed_to_double (polygon->limit.p2.x),
+ _cairo_fixed_to_double (polygon->limit.p2.y),
+ polygon->num_limits);
+ }
+
+ for (n = 0; n < polygon->num_edges; n++) {
+ cairo_edge_t *edge = &polygon->edges[n];
+
+ fprintf (stream,
+ " [%d] = [(%f, %f), (%f, %f)], top=%f, bottom=%f, dir=%d\n",
+ n,
+ _cairo_fixed_to_double (edge->line.p1.x),
+ _cairo_fixed_to_double (edge->line.p1.y),
+ _cairo_fixed_to_double (edge->line.p2.x),
+ _cairo_fixed_to_double (edge->line.p2.y),
+ _cairo_fixed_to_double (edge->top),
+ _cairo_fixed_to_double (edge->bottom),
+ edge->dir);
+
+ }
+}
diff --git a/src/cairo-default-context-private.h b/src/cairo-default-context-private.h
new file mode 100755
index 000000000..fd159b496
--- /dev/null
+++ b/src/cairo-default-context-private.h
@@ -0,0 +1,68 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_DEFAULT_CONTEXT_PRIVATE_H
+#define CAIRO_DEFAULT_CONTEXT_PRIVATE_H
+
+#include "cairo-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_default_context cairo_default_context_t;
+
+struct _cairo_default_context {
+ cairo_t base;
+
+ cairo_gstate_t *gstate;
+ cairo_gstate_t gstate_tail[2];
+ cairo_gstate_t *gstate_freelist;
+
+ cairo_path_fixed_t path[1];
+};
+
+cairo_private cairo_t *
+_cairo_default_context_create (void *target);
+
+cairo_private cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr, void *target);
+
+cairo_private void
+_cairo_default_context_fini (cairo_default_context_t *cr);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */
diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c
new file mode 100755
index 000000000..b7a38d2f0
--- /dev/null
+++ b/src/cairo-default-context.c
@@ -0,0 +1,1552 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+
+#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1)
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+static freed_pool_t context_pool;
+
+void
+_cairo_default_context_reset_static_data (void)
+{
+ _freed_pool_reset (&context_pool);
+}
+
+void
+_cairo_default_context_fini (cairo_default_context_t *cr)
+{
+ while (cr->gstate != &cr->gstate_tail[0]) {
+ if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
+ break;
+ }
+
+ _cairo_gstate_fini (cr->gstate);
+ cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */
+ while (cr->gstate_freelist != NULL) {
+ cairo_gstate_t *gstate = cr->gstate_freelist;
+ cr->gstate_freelist = gstate->next;
+ free (gstate);
+ }
+
+ _cairo_path_fixed_fini (cr->path);
+
+ _cairo_fini (&cr->base);
+}
+
+static void
+_cairo_default_context_destroy (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_default_context_fini (cr);
+
+ /* mark the context as invalid to protect against misuse */
+ cr->base.status = CAIRO_STATUS_NULL_POINTER;
+ _freed_pool_put (&context_pool, cr);
+}
+
+static cairo_surface_t *
+_cairo_default_context_get_original_target (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_original_target (cr->gstate);
+}
+
+static cairo_surface_t *
+_cairo_default_context_get_current_target (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_target (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_save (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
+}
+
+static cairo_status_t
+_cairo_default_context_restore (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ if (unlikely (_cairo_gstate_is_group (cr->gstate)))
+ return _cairo_error (CAIRO_STATUS_INVALID_RESTORE);
+
+ return _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
+}
+
+static cairo_status_t
+_cairo_default_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_surface_t *group_surface;
+ cairo_clip_t *clip;
+ cairo_status_t status;
+
+ clip = _cairo_gstate_get_clip (cr->gstate);
+ if (_cairo_clip_is_all_clipped (clip)) {
+ group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+ status = group_surface->status;
+ if (unlikely (status))
+ goto bail;
+ } else {
+ cairo_surface_t *parent_surface;
+ cairo_rectangle_int_t extents;
+ cairo_bool_t bounded, is_empty;
+
+ parent_surface = _cairo_gstate_get_target (cr->gstate);
+
+ /* Get the extents that we'll use in creating our new group surface */
+ bounded = _cairo_surface_get_extents (parent_surface, &extents);
+ if (clip)
+ /* XXX: This assignment just fixes a compiler warning? */
+ is_empty = _cairo_rectangle_intersect (&extents,
+ _cairo_clip_get_extents (clip));
+
+ if (!bounded) {
+ /* XXX: Generic solution? */
+ group_surface = cairo_recording_surface_create (content, NULL);
+ extents.x = extents.y = 0;
+ } else {
+ group_surface = _cairo_surface_create_similar_solid (parent_surface,
+ content,
+ extents.width,
+ extents.height,
+ CAIRO_COLOR_TRANSPARENT);
+ }
+ status = group_surface->status;
+ if (unlikely (status))
+ goto bail;
+
+ /* Set device offsets on the new surface so that logically it appears at
+ * the same location on the parent surface -- when we pop_group this,
+ * the source pattern will get fixed up for the appropriate target surface
+ * device offsets, so we want to set our own surface offsets from /that/,
+ * and not from the device origin. */
+ cairo_surface_set_device_offset (group_surface,
+ parent_surface->device_transform.x0 - extents.x,
+ parent_surface->device_transform.y0 - extents.y);
+
+ /* If we have a current path, we need to adjust it to compensate for
+ * the device offset just applied. */
+ _cairo_path_fixed_translate (cr->path,
+ _cairo_fixed_from_int (-extents.x),
+ _cairo_fixed_from_int (-extents.y));
+ }
+
+ /* create a new gstate for the redirect */
+ status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
+ if (unlikely (status))
+ goto bail;
+
+ status = _cairo_gstate_redirect_target (cr->gstate, group_surface);
+
+bail:
+ cairo_surface_destroy (group_surface);
+ return status;
+}
+
+static cairo_pattern_t *
+_cairo_default_context_pop_group (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_surface_t *group_surface;
+ cairo_pattern_t *group_pattern;
+ cairo_matrix_t group_matrix, device_transform_matrix;
+ cairo_status_t status;
+
+ /* Verify that we are at the right nesting level */
+ if (unlikely (! _cairo_gstate_is_group (cr->gstate)))
+ return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP);
+
+ /* Get a reference to the active surface before restoring */
+ group_surface = _cairo_gstate_get_target (cr->gstate);
+ group_surface = cairo_surface_reference (group_surface);
+
+ status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ group_pattern = cairo_pattern_create_for_surface (group_surface);
+ status = group_pattern->status;
+ if (unlikely (status))
+ goto done;
+
+ _cairo_gstate_get_matrix (cr->gstate, &group_matrix);
+ /* 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;
+}
+
+static cairo_status_t
+_cairo_default_context_set_source (void *abstract_cr,
+ cairo_pattern_t *source)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_source (cr->gstate, source);
+}
+
+static cairo_bool_t
+_current_source_matches_solid (const cairo_pattern_t *pattern,
+ double red,
+ double green,
+ double blue,
+ double alpha)
+{
+ cairo_color_t color;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return FALSE;
+
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+ alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+ _cairo_color_init_rgba (&color, red, green, blue, alpha);
+ return _cairo_color_equal (&color,
+ &((cairo_solid_pattern_t *) pattern)->color);
+}
+
+static cairo_status_t
+_cairo_default_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_pattern_t *pattern;
+ cairo_status_t status;
+
+ if (_current_source_matches_solid (cr->gstate->source,
+ red, green, blue, alpha))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* push the current pattern to the freed lists */
+ _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
+
+ pattern = cairo_pattern_create_rgba (red, green, blue, alpha);
+ if (unlikely (pattern->status)) {
+ status = pattern->status;
+ cairo_pattern_destroy (pattern);
+ return pattern->status;
+ }
+
+ status = _cairo_default_context_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_default_context_set_source_surface (void *abstract_cr,
+ cairo_surface_t *surface,
+ double x,
+ double y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ cairo_status_t status;
+
+ /* push the current pattern to the freed lists */
+ _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+ if (unlikely (pattern->status)) {
+ status = pattern->status;
+ cairo_pattern_destroy (pattern);
+ return status;
+ }
+
+ cairo_matrix_init_translate (&matrix, -x, -y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ status = _cairo_default_context_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ return status;
+}
+
+static cairo_pattern_t *
+_cairo_default_context_get_source (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_source (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_set_tolerance (void *abstract_cr,
+ double tolerance)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ if (tolerance < CAIRO_TOLERANCE_MINIMUM)
+ tolerance = CAIRO_TOLERANCE_MINIMUM;
+
+ return _cairo_gstate_set_tolerance (cr->gstate, tolerance);
+}
+
+static cairo_status_t
+_cairo_default_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_operator (cr->gstate, op);
+}
+
+static cairo_status_t
+_cairo_default_context_set_opacity (void *abstract_cr, double opacity)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_opacity (cr->gstate, opacity);
+}
+
+static cairo_status_t
+_cairo_default_context_set_antialias (void *abstract_cr,
+ cairo_antialias_t antialias)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_antialias (cr->gstate, antialias);
+}
+
+static cairo_status_t
+_cairo_default_context_set_fill_rule (void *abstract_cr,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_fill_rule (cr->gstate, fill_rule);
+}
+
+static cairo_status_t
+_cairo_default_context_set_line_width (void *abstract_cr,
+ double line_width)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_line_width (cr->gstate, line_width);
+}
+
+static cairo_status_t
+_cairo_default_context_set_line_cap (void *abstract_cr,
+ cairo_line_cap_t line_cap)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_line_cap (cr->gstate, line_cap);
+}
+
+static cairo_status_t
+_cairo_default_context_set_line_join (void *abstract_cr,
+ cairo_line_join_t line_join)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_line_join (cr->gstate, line_join);
+}
+
+static cairo_status_t
+_cairo_default_context_set_dash (void *abstract_cr,
+ const double *dashes,
+ int num_dashes,
+ double offset)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_dash (cr->gstate,
+ dashes, num_dashes, offset);
+}
+
+static cairo_status_t
+_cairo_default_context_set_miter_limit (void *abstract_cr,
+ double limit)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_miter_limit (cr->gstate, limit);
+}
+
+static cairo_antialias_t
+_cairo_default_context_get_antialias (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_antialias (cr->gstate);
+}
+
+static void
+_cairo_default_context_get_dash (void *abstract_cr,
+ double *dashes,
+ int *num_dashes,
+ double *offset)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_get_dash (cr->gstate, dashes, num_dashes, offset);
+}
+
+static cairo_fill_rule_t
+_cairo_default_context_get_fill_rule (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_fill_rule (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_line_width (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_line_width (cr->gstate);
+}
+
+static cairo_line_cap_t
+_cairo_default_context_get_line_cap (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_line_cap (cr->gstate);
+}
+
+static cairo_line_join_t
+_cairo_default_context_get_line_join (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_line_join (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_miter_limit (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_miter_limit (cr->gstate);
+}
+
+static cairo_operator_t
+_cairo_default_context_get_operator (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_operator (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_opacity (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_opacity (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_tolerance (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_tolerance (cr->gstate);
+}
+
+
+/* Current tranformation matrix */
+
+static cairo_status_t
+_cairo_default_context_translate (void *abstract_cr,
+ double tx,
+ double ty)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_translate (cr->gstate, tx, ty);
+}
+
+static cairo_status_t
+_cairo_default_context_scale (void *abstract_cr,
+ double sx,
+ double sy)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_scale (cr->gstate, sx, sy);
+}
+
+static cairo_status_t
+_cairo_default_context_rotate (void *abstract_cr,
+ double theta)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_rotate (cr->gstate, theta);
+}
+
+static cairo_status_t
+_cairo_default_context_transform (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_transform (cr->gstate, matrix);
+}
+
+static cairo_status_t
+_cairo_default_context_set_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_matrix (cr->gstate, matrix);
+}
+
+static cairo_status_t
+_cairo_default_context_set_identity_matrix (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_identity_matrix (cr->gstate);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_default_context_get_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_get_matrix (cr->gstate, matrix);
+}
+
+static void
+_cairo_default_context_user_to_device (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_user_to_device (cr->gstate, x, y);
+}
+
+static void
+_cairo_default_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy);
+}
+
+static void
+_cairo_default_context_device_to_user (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_device_to_user (cr->gstate, x, y);
+}
+
+static void
+_cairo_default_context_device_to_user_distance (void *abstract_cr,
+ double *dx,
+ double *dy)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy);
+}
+
+static void
+_cairo_default_context_backend_to_user (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_backend_to_user (cr->gstate, x, y);
+}
+
+static void
+_cairo_default_context_backend_to_user_distance (void *abstract_cr, double *dx, double *dy)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_backend_to_user_distance (cr->gstate, dx, dy);
+}
+
+static void
+_cairo_default_context_user_to_backend (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_user_to_backend (cr->gstate, x, y);
+}
+
+static void
+_cairo_default_context_user_to_backend_distance (void *abstract_cr,
+ double *dx,
+ double *dy)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_user_to_backend_distance (cr->gstate, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_default_context_new_path (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_path_fixed_fini (cr->path);
+ _cairo_path_fixed_init (cr->path);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_new_sub_path (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_path_fixed_new_sub_path (cr->path);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_move_to (void *abstract_cr, double x, double y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_fixed_t x_fixed, y_fixed;
+
+ _cairo_gstate_user_to_backend (cr->gstate, &x, &y);
+ x_fixed = _cairo_fixed_from_double (x);
+ y_fixed = _cairo_fixed_from_double (y);
+
+ return _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_line_to (void *abstract_cr, double x, double y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_fixed_t x_fixed, y_fixed;
+
+ _cairo_gstate_user_to_backend (cr->gstate, &x, &y);
+ x_fixed = _cairo_fixed_from_double (x);
+ y_fixed = _cairo_fixed_from_double (y);
+
+ return _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_curve_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_fixed_t x1_fixed, y1_fixed;
+ cairo_fixed_t x2_fixed, y2_fixed;
+ cairo_fixed_t x3_fixed, y3_fixed;
+
+ _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1);
+ _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2);
+ _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3);
+
+ x1_fixed = _cairo_fixed_from_double (x1);
+ y1_fixed = _cairo_fixed_from_double (y1);
+
+ x2_fixed = _cairo_fixed_from_double (x2);
+ y2_fixed = _cairo_fixed_from_double (y2);
+
+ x3_fixed = _cairo_fixed_from_double (x3);
+ y3_fixed = _cairo_fixed_from_double (y3);
+
+ return _cairo_path_fixed_curve_to (cr->path,
+ x1_fixed, y1_fixed,
+ x2_fixed, y2_fixed,
+ x3_fixed, y3_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_arc (void *abstract_cr,
+ double xc, double yc, double radius,
+ double angle1, double angle2,
+ cairo_bool_t forward)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ /* Do nothing, successfully, if radius is <= 0 */
+ if (radius <= 0.0) {
+ cairo_fixed_t x_fixed, y_fixed;
+
+ _cairo_gstate_user_to_backend (cr->gstate, &xc, &yc);
+ x_fixed = _cairo_fixed_from_double (xc);
+ y_fixed = _cairo_fixed_from_double (yc);
+ status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_default_context_line_to (cr,
+ xc + radius * cos (angle1),
+ yc + radius * sin (angle1));
+
+ if (unlikely (status))
+ return status;
+
+ if (forward)
+ _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
+ else
+ _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);
+
+ return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */
+}
+
+static cairo_status_t
+_cairo_default_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_fixed_t dx_fixed, dy_fixed;
+
+ _cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy);
+
+ dx_fixed = _cairo_fixed_from_double (dx);
+ dy_fixed = _cairo_fixed_from_double (dy);
+
+ return _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_fixed_t dx_fixed, dy_fixed;
+
+ _cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy);
+
+ dx_fixed = _cairo_fixed_from_double (dx);
+ dy_fixed = _cairo_fixed_from_double (dy);
+
+ return _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed);
+}
+
+
+static cairo_status_t
+_cairo_default_context_rel_curve_to (void *abstract_cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_fixed_t dx1_fixed, dy1_fixed;
+ cairo_fixed_t dx2_fixed, dy2_fixed;
+ cairo_fixed_t dx3_fixed, dy3_fixed;
+
+ _cairo_gstate_user_to_backend_distance (cr->gstate, &dx1, &dy1);
+ _cairo_gstate_user_to_backend_distance (cr->gstate, &dx2, &dy2);
+ _cairo_gstate_user_to_backend_distance (cr->gstate, &dx3, &dy3);
+
+ dx1_fixed = _cairo_fixed_from_double (dx1);
+ dy1_fixed = _cairo_fixed_from_double (dy1);
+
+ dx2_fixed = _cairo_fixed_from_double (dx2);
+ dy2_fixed = _cairo_fixed_from_double (dy2);
+
+ dx3_fixed = _cairo_fixed_from_double (dx3);
+ dy3_fixed = _cairo_fixed_from_double (dy3);
+
+ return _cairo_path_fixed_rel_curve_to (cr->path,
+ dx1_fixed, dy1_fixed,
+ dx2_fixed, dy2_fixed,
+ dx3_fixed, dy3_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_close_path (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_path_fixed_close_path (cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_rectangle (void *abstract_cr,
+ double x, double y,
+ double width, double height)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_default_context_move_to (cr, x, y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_default_context_rel_line_to (cr, width, 0);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_default_context_rel_line_to (cr, 0, height);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_default_context_rel_line_to (cr, -width, 0);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_default_context_close_path (cr);
+}
+
+static void
+_cairo_default_context_path_extents (void *abstract_cr,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_path_extents (cr->gstate,
+ cr->path,
+ x1, y1, x2, y2);
+}
+
+static cairo_bool_t
+_cairo_default_context_has_current_point (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return cr->path->has_current_point;
+}
+
+static cairo_bool_t
+_cairo_default_context_get_current_point (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_fixed_t x_fixed, y_fixed;
+
+ if (_cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed))
+ {
+ *x = _cairo_fixed_to_double (x_fixed);
+ *y = _cairo_fixed_to_double (y_fixed);
+ _cairo_gstate_backend_to_user (cr->gstate, x, y);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static cairo_path_t *
+_cairo_default_context_copy_path (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_path_create (cr->path, &cr->base);
+}
+
+static cairo_path_t *
+_cairo_default_context_copy_path_flat (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_path_create_flat (cr->path, &cr->base);
+}
+
+static cairo_status_t
+_cairo_default_context_append_path (void *abstract_cr,
+ const cairo_path_t *path)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_path_append_to_context (path, &cr->base);
+}
+
+static cairo_status_t
+_cairo_default_context_paint (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_paint (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_paint_with_alpha (void *abstract_cr,
+ double alpha)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_solid_pattern_t pattern;
+ cairo_status_t status;
+ cairo_color_t color;
+
+ if (CAIRO_ALPHA_IS_OPAQUE (alpha))
+ return _cairo_gstate_paint (cr->gstate);
+
+ if (CAIRO_ALPHA_IS_ZERO (alpha) &&
+ _cairo_operator_bounded_by_mask (cr->gstate->op)) {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_color_init_rgba (&color, 0., 0., 0., alpha);
+ _cairo_pattern_init_solid (&pattern, &color);
+
+ status = _cairo_gstate_mask (cr->gstate, &pattern.base);
+ _cairo_pattern_fini (&pattern.base);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_default_context_mask (void *abstract_cr,
+ cairo_pattern_t *mask)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_mask (cr->gstate, mask);
+}
+
+static cairo_status_t
+_cairo_default_context_stroke_preserve (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_stroke (cr->gstate, cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_stroke (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_gstate_stroke (cr->gstate, cr->path);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_default_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_default_context_in_stroke (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_in_stroke (cr->gstate,
+ cr->path,
+ x, y,
+ inside);
+}
+
+static cairo_status_t
+_cairo_default_context_stroke_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_stroke_extents (cr->gstate,
+ cr->path,
+ x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_default_context_fill_preserve (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_fill (cr->gstate, cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_fill (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_gstate_fill (cr->gstate, cr->path);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_default_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_default_context_in_fill (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ *inside = _cairo_gstate_in_fill (cr->gstate,
+ cr->path,
+ x, y);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_fill_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_fill_extents (cr->gstate,
+ cr->path,
+ x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_default_context_clip_preserve (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_clip (cr->gstate, cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_clip (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_gstate_clip (cr->gstate, cr->path);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_default_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_default_context_in_clip (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ *inside = _cairo_gstate_in_clip (cr->gstate, x, y);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_reset_clip (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_reset_clip (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_clip_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) {
+ *x1 = -INFINITY;
+ *y1 = -INFINITY;
+ *x2 = +INFINITY;
+ *y2 = +INFINITY;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_rectangle_list_t *
+_cairo_default_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_copy_clip_rectangle_list (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_copy_page (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_copy_page (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_show_page (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_show_page (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_face (void *abstract_cr,
+ cairo_font_face_t *font_face)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_font_face (cr->gstate, font_face);
+}
+
+static cairo_font_face_t *
+_cairo_default_context_get_font_face (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_font_face_t *font_face;
+ cairo_status_t status;
+
+ status = _cairo_gstate_get_font_face (cr->gstate, &font_face);
+ if (unlikely (status)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+ }
+
+ return font_face;
+}
+
+static cairo_status_t
+_cairo_default_context_font_extents (void *abstract_cr,
+ cairo_font_extents_t *extents)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_get_font_extents (cr->gstate, extents);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_size (void *abstract_cr,
+ double size)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_font_size (cr->gstate, size);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_font_matrix (cr->gstate, matrix);
+}
+
+static void
+_cairo_default_context_get_font_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_get_font_matrix (cr->gstate, matrix);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_options (void *abstract_cr,
+ const cairo_font_options_t *options)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_set_font_options (cr->gstate, options);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_default_context_get_font_options (void *abstract_cr,
+ cairo_font_options_t *options)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_get_font_options (cr->gstate, options);
+}
+
+static cairo_status_t
+_cairo_default_context_set_scaled_font (void *abstract_cr,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_bool_t was_previous;
+ cairo_status_t status;
+
+ if (scaled_font == cr->gstate->scaled_font)
+ return CAIRO_STATUS_SUCCESS;
+
+ was_previous = scaled_font == cr->gstate->previous_scaled_font;
+
+ status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix);
+ if (unlikely (status))
+ return status;
+
+ _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options);
+
+ if (was_previous)
+ cr->gstate->scaled_font = cairo_scaled_font_reference (scaled_font);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_scaled_font_t *
+_cairo_default_context_get_scaled_font (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ cairo_scaled_font_t *scaled_font;
+ cairo_status_t status;
+
+ status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font);
+ if (unlikely (status))
+ return _cairo_scaled_font_create_in_error (status);
+
+ return scaled_font;
+}
+
+static cairo_status_t
+_cairo_default_context_glyphs (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_glyph_text_info_t *info)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_show_text_glyphs (cr->gstate, glyphs, num_glyphs, info);
+}
+
+static cairo_status_t
+_cairo_default_context_glyph_path (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_glyph_path (cr->gstate,
+ glyphs, num_glyphs,
+ cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_glyph_extents (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents);
+}
+
+static cairo_status_t
+_cairo_default_context_set_shadow (void *abstract_cr,
+ cairo_shadow_type_t shadow)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_shadow (cr->gstate, shadow);
+}
+
+static cairo_status_t
+_cairo_default_context_set_shadow_offset (void *abstract_cr,
+ double x_offset,
+ double y_offset)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_shadow_offset (cr->gstate, x_offset, y_offset);
+}
+
+static cairo_status_t
+_cairo_default_context_set_shadow_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_shadow_rgba (cr->gstate, red, green, blue, alpha);
+}
+
+static cairo_status_t
+_cairo_default_context_set_shadow_blur (void *abstract_cr,
+ double x_blur,
+ double y_blur)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_set_shadow_blur (cr->gstate, x_blur, y_blur);
+}
+
+static void
+_cairo_default_context_set_draw_shadow_only (void *abstract_cr,
+ cairo_bool_t draw_shadow_only)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_gstate_set_draw_shadow_only (cr->gstate, draw_shadow_only);
+}
+
+static void
+_cairo_default_context_shadow_enable_cache (void *abstract_cr,
+ cairo_bool_t enable)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ return _cairo_gstate_shadow_enable_cache (cr->gstate, enable);
+}
+
+static void
+_cairo_default_context_set_path_is_inset_shadow_with_spread (void *abstract_cr,
+ cairo_bool_t is_spread_path)
+{
+ cairo_default_context_t *cr = abstract_cr;
+ return _cairo_gstate_set_path_is_inset_shadow_with_spread (cr->gstate,
+ is_spread_path);
+}
+
+static const cairo_backend_t _cairo_default_context_backend = {
+ CAIRO_TYPE_DEFAULT,
+ _cairo_default_context_destroy,
+
+ _cairo_default_context_get_original_target,
+ _cairo_default_context_get_current_target,
+
+ _cairo_default_context_save,
+ _cairo_default_context_restore,
+
+ _cairo_default_context_push_group,
+ _cairo_default_context_pop_group,
+
+ _cairo_default_context_set_source_rgba,
+ _cairo_default_context_set_source_surface,
+ _cairo_default_context_set_source,
+ _cairo_default_context_get_source,
+
+ _cairo_default_context_set_antialias,
+ _cairo_default_context_set_dash,
+ _cairo_default_context_set_fill_rule,
+ _cairo_default_context_set_line_cap,
+ _cairo_default_context_set_line_join,
+ _cairo_default_context_set_line_width,
+ _cairo_default_context_set_miter_limit,
+ _cairo_default_context_set_opacity,
+ _cairo_default_context_set_operator,
+ _cairo_default_context_set_tolerance,
+ _cairo_default_context_get_antialias,
+ _cairo_default_context_get_dash,
+ _cairo_default_context_get_fill_rule,
+ _cairo_default_context_get_line_cap,
+ _cairo_default_context_get_line_join,
+ _cairo_default_context_get_line_width,
+ _cairo_default_context_get_miter_limit,
+ _cairo_default_context_get_opacity,
+ _cairo_default_context_get_operator,
+ _cairo_default_context_get_tolerance,
+
+ _cairo_default_context_translate,
+ _cairo_default_context_scale,
+ _cairo_default_context_rotate,
+ _cairo_default_context_transform,
+ _cairo_default_context_set_matrix,
+ _cairo_default_context_set_identity_matrix,
+ _cairo_default_context_get_matrix,
+
+ _cairo_default_context_user_to_device,
+ _cairo_default_context_user_to_device_distance,
+ _cairo_default_context_device_to_user,
+ _cairo_default_context_device_to_user_distance,
+
+ _cairo_default_context_user_to_backend,
+ _cairo_default_context_user_to_backend_distance,
+ _cairo_default_context_backend_to_user,
+ _cairo_default_context_backend_to_user_distance,
+
+ _cairo_default_context_new_path,
+ _cairo_default_context_new_sub_path,
+ _cairo_default_context_move_to,
+ _cairo_default_context_rel_move_to,
+ _cairo_default_context_line_to,
+ _cairo_default_context_rel_line_to,
+ _cairo_default_context_curve_to,
+ _cairo_default_context_rel_curve_to,
+ NULL, /* arc-to */
+ NULL, /* rel-arc-to */
+ _cairo_default_context_close_path,
+ _cairo_default_context_arc,
+ _cairo_default_context_rectangle,
+ _cairo_default_context_path_extents,
+ _cairo_default_context_has_current_point,
+ _cairo_default_context_get_current_point,
+ _cairo_default_context_copy_path,
+ _cairo_default_context_copy_path_flat,
+ _cairo_default_context_append_path,
+
+ NULL, /* stroke-to-path */
+
+ _cairo_default_context_clip,
+ _cairo_default_context_clip_preserve,
+ _cairo_default_context_in_clip,
+ _cairo_default_context_clip_extents,
+ _cairo_default_context_reset_clip,
+ _cairo_default_context_copy_clip_rectangle_list,
+
+ _cairo_default_context_paint,
+ _cairo_default_context_paint_with_alpha,
+ _cairo_default_context_mask,
+
+ _cairo_default_context_stroke,
+ _cairo_default_context_stroke_preserve,
+ _cairo_default_context_in_stroke,
+ _cairo_default_context_stroke_extents,
+
+ _cairo_default_context_fill,
+ _cairo_default_context_fill_preserve,
+ _cairo_default_context_in_fill,
+ _cairo_default_context_fill_extents,
+
+ _cairo_default_context_set_font_face,
+ _cairo_default_context_get_font_face,
+ _cairo_default_context_set_font_size,
+ _cairo_default_context_set_font_matrix,
+ _cairo_default_context_get_font_matrix,
+ _cairo_default_context_set_font_options,
+ _cairo_default_context_get_font_options,
+ _cairo_default_context_set_scaled_font,
+ _cairo_default_context_get_scaled_font,
+ _cairo_default_context_font_extents,
+
+ _cairo_default_context_glyphs,
+ _cairo_default_context_glyph_path,
+ _cairo_default_context_glyph_extents,
+
+ _cairo_default_context_copy_page,
+ _cairo_default_context_show_page,
+
+ /* shadow */
+ _cairo_default_context_set_shadow,
+ _cairo_default_context_set_shadow_offset,
+ _cairo_default_context_set_shadow_rgba,
+ _cairo_default_context_set_shadow_blur,
+ _cairo_default_context_set_draw_shadow_only,
+ _cairo_default_context_shadow_enable_cache,
+ _cairo_default_context_set_path_is_inset_shadow_with_spread,
+};
+
+cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr, void *target)
+{
+ _cairo_init (&cr->base, &_cairo_default_context_backend);
+ _cairo_path_fixed_init (cr->path);
+
+ cr->gstate = &cr->gstate_tail[0];
+ cr->gstate_freelist = &cr->gstate_tail[1];
+ cr->gstate_tail[1].next = NULL;
+
+ return _cairo_gstate_init (cr->gstate, target);
+}
+
+cairo_t *
+_cairo_default_context_create (void *target)
+{
+ cairo_default_context_t *cr;
+ cairo_status_t status;
+
+ cr = _freed_pool_get (&context_pool);
+ if (unlikely (cr == NULL)) {
+ cr = malloc (sizeof (cairo_default_context_t));
+ if (unlikely (cr == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ status = _cairo_default_context_init (cr, target);
+ if (unlikely (status)) {
+ _freed_pool_put (&context_pool, cr);
+ return _cairo_create_in_error (status);
+ }
+
+ return &cr->base;
+}
diff --git a/src/cairo-deflate-stream.c b/src/cairo-deflate-stream.c
new file mode 100755
index 000000000..ae23bda94
--- /dev/null
+++ b/src/cairo-deflate-stream.c
@@ -0,0 +1,156 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Author(s):
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_DEFLATE_STREAM
+
+#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;
+}
+
+#endif /* CAIRO_HAS_DEFLATE_STREAM */
diff --git a/src/cairo-deprecated.h b/src/cairo-deprecated.h
new file mode 100755
index 000000000..7a56aadbf
--- /dev/null
+++ b/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/src/cairo-device-private.h b/src/cairo-device-private.h
new file mode 100755
index 000000000..1c687d2fe
--- /dev/null
+++ b/src/cairo-device-private.h
@@ -0,0 +1,89 @@
+/* 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;
+
+ unsigned long shadow_caches_size;
+ cairo_list_t shadow_caches;
+};
+
+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/src/cairo-device.c b/src/cairo-device.c
new file mode 100755
index 000000000..e845912a3
--- /dev/null
+++ b/src/cairo-device.c
@@ -0,0 +1,557 @@
+/* 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"
+#include "cairo-list-inline.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:
+ case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+ case CAIRO_STATUS_DEVICE_FINISHED:
+ 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_list_init (&device->shadow_caches);
+ device->shadow_caches_size = 0;
+}
+
+/**
+ * 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->finished)
+ return;
+
+ if (device->backend->flush != NULL) {
+ status = device->backend->flush (device);
+ if (unlikely (status))
+ status = _cairo_device_set_error (device, status);
+ }
+}
+slim_hidden_def (cairo_device_flush);
+
+/**
+ * cairo_device_finish:
+ * @device: the #cairo_device_t to finish
+ *
+ * This function finishes the device and drops all references to
+ * external resources. All surfaces, fonts and other objects created
+ * for this @device will be finished, too.
+ * Further operations on the @device will not affect the @device but
+ * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error.
+ *
+ * When the last call to cairo_device_destroy() decreases the
+ * reference count to zero, cairo will call cairo_device_finish() if
+ * it hasn't been called already, before freeing the resources
+ * associated with the device.
+ *
+ * This function may acquire devices.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_device_finish (cairo_device_t *device)
+{
+ if (device == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+ {
+ return;
+ }
+
+ if (device->finished)
+ return;
+
+ cairo_device_flush (device);
+
+ if (device->backend->finish != NULL)
+ device->backend->finish (device);
+
+ /* We only finish the device after the backend's callback returns because
+ * the device might still be needed during the callback
+ * (e.g. for cairo_device_acquire ()).
+ */
+ device->finished = TRUE;
+}
+slim_hidden_def (cairo_device_finish);
+
+/**
+ * cairo_device_destroy:
+ * @device: a #cairo_device_t
+ *
+ * Decreases the reference count on @device by one. If the result is
+ * zero, then @device and all associated resources are freed. See
+ * cairo_device_reference().
+ *
+ * This function may acquire devices if the last reference was dropped.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_device_destroy (cairo_device_t *device)
+{
+ cairo_user_data_array_t user_data;
+
+ if (device == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+ {
+ return;
+ }
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count));
+ if (! _cairo_reference_count_dec_and_test (&device->ref_count))
+ return;
+
+ while (! cairo_list_is_empty (&device->shadow_caches)) {
+ cairo_shadow_cache_t *shadow;
+
+ shadow = cairo_list_first_entry (&device->shadow_caches,
+ cairo_shadow_cache_t,
+ link);
+
+ cairo_list_del (&shadow->link);
+ cairo_surface_destroy (shadow->surface);
+ free (shadow);
+ }
+ device->shadow_caches_size = 0;
+
+ cairo_device_finish (device);
+
+ assert (device->mutex_depth == 0);
+ CAIRO_MUTEX_FINI (device->mutex);
+
+ user_data = device->user_data;
+
+ device->backend->destroy (device);
+
+ _cairo_user_data_array_fini (&user_data);
+
+}
+slim_hidden_def (cairo_device_destroy);
+
+/**
+ * cairo_device_get_type:
+ * @device: a #cairo_device_t
+ *
+ * This function returns the type of the device. See #cairo_device_type_t
+ * for available types.
+ *
+ * Return value: The type of @device.
+ *
+ * Since: 1.10
+ **/
+cairo_device_type_t
+cairo_device_get_type (cairo_device_t *device)
+{
+ if (device == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+ {
+ return CAIRO_DEVICE_TYPE_INVALID;
+ }
+
+ return device->backend->type;
+}
+
+/**
+ * cairo_device_acquire:
+ * @device: a #cairo_device_t
+ *
+ * Acquires the @device for the current thread. This function will block
+ * until no other thread has acquired the device.
+ *
+ * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the
+ * device. From now on your thread owns the device and no other thread will be
+ * able to acquire it until a matching call to cairo_device_release(). It is
+ * allowed to recursively acquire the device multiple times from the same
+ * thread.
+ *
+ * <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_DEVICE_FINISHED);
+
+ CAIRO_MUTEX_LOCK (device->mutex);
+ if (device->mutex_depth++ == 0) {
+ if (device->backend->lock != NULL)
+ device->backend->lock (device);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_device_acquire);
+
+/**
+ * cairo_device_release:
+ * @device: a #cairo_device_t
+ *
+ * Releases a @device previously acquired using cairo_device_acquire(). See
+ * that function for details.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_device_release (cairo_device_t *device)
+{
+ if (device == NULL)
+ return;
+
+ assert (device->mutex_depth > 0);
+
+ if (--device->mutex_depth == 0) {
+ if (device->backend->unlock != NULL)
+ device->backend->unlock (device);
+ }
+
+ CAIRO_MUTEX_UNLOCK (device->mutex);
+}
+slim_hidden_def (cairo_device_release);
+
+cairo_status_t
+_cairo_device_set_error (cairo_device_t *device,
+ cairo_status_t status)
+{
+ if (status == CAIRO_STATUS_SUCCESS)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_status_set_error (&device->status, status);
+
+ return _cairo_error (status);
+}
+
+/**
+ * cairo_device_get_reference_count:
+ * @device: a #cairo_device_t
+ *
+ * Returns the current reference count of @device.
+ *
+ * Return value: the current reference count of @device. If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.10
+ **/
+unsigned int
+cairo_device_get_reference_count (cairo_device_t *device)
+{
+ if (device == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+ return 0;
+
+ return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count);
+}
+
+/**
+ * cairo_device_get_user_data:
+ * @device: a #cairo_device_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @device using the
+ * specified key. If no user data has been attached with the given
+ * key this function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.10
+ **/
+void *
+cairo_device_get_user_data (cairo_device_t *device,
+ const cairo_user_data_key_t *key)
+{
+ return _cairo_user_data_array_get_data (&device->user_data,
+ key);
+}
+
+/**
+ * cairo_device_set_user_data:
+ * @device: a #cairo_device_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the #cairo_device_t
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * #cairo_t is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @device. To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_device_set_user_data (cairo_device_t *device,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy)
+{
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+ return device->status;
+
+ return _cairo_user_data_array_set_data (&device->user_data,
+ key, user_data, destroy);
+}
diff --git a/src/cairo-directfb-surface.c b/src/cairo-directfb-surface.c
new file mode 100755
index 000000000..16e367af8
--- /dev/null
+++ b/src/cairo-directfb-surface.c
@@ -0,0 +1,544 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-directfb.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-fallback-private.h"
+
+#include <pixman.h>
+
+#include <directfb.h>
+#include <direct/types.h>
+#include <direct/debug.h>
+#include <direct/memcpy.h>
+#include <direct/util.h>
+
+slim_hidden_proto(cairo_directfb_surface_create);
+
+typedef struct _cairo_dfb_surface {
+ cairo_image_surface_t image;
+
+ IDirectFB *dfb;
+ IDirectFBSurface *dfb_surface;
+
+ unsigned blit_premultiplied : 1;
+} cairo_dfb_surface_t;
+
+static cairo_content_t
+_directfb_format_to_content (DFBSurfacePixelFormat format)
+{
+ cairo_content_t content = 0;
+
+ if (DFB_PIXELFORMAT_HAS_ALPHA (format))
+ content |= CAIRO_CONTENT_ALPHA;
+ if (DFB_COLOR_BITS_PER_PIXEL (format))
+ content |= CAIRO_CONTENT_COLOR_ALPHA;
+
+ assert(content);
+ return content;
+}
+
+static inline pixman_format_code_t
+_directfb_to_pixman_format (DFBSurfacePixelFormat format)
+{
+ switch (format) {
+ case DSPF_UNKNOWN: return 0;
+ case DSPF_ARGB1555: return PIXMAN_a1r5g5b5;
+ case DSPF_RGB16: return PIXMAN_r5g6b5;
+ case DSPF_RGB24: return PIXMAN_r8g8b8;
+ case DSPF_RGB32: return PIXMAN_x8r8g8b8;
+ case DSPF_ARGB: return PIXMAN_a8r8g8b8;
+ case DSPF_A8: return PIXMAN_a8;
+ case DSPF_YUY2: return PIXMAN_yuy2;
+ case DSPF_RGB332: return PIXMAN_r3g3b2;
+ case DSPF_UYVY: return 0;
+ case DSPF_I420: return 0;
+ case DSPF_YV12: return PIXMAN_yv12;
+ case DSPF_LUT8: return 0;
+ case DSPF_ALUT44: return 0;
+ case DSPF_AiRGB: return 0;
+ case DSPF_A1: return 0; /* bit reversed, oops */
+ case DSPF_NV12: return 0;
+ case DSPF_NV16: return 0;
+ case DSPF_ARGB2554: return 0;
+ case DSPF_ARGB4444: return PIXMAN_a4r4g4b4;
+ case DSPF_NV21: return 0;
+ case DSPF_AYUV: return 0;
+ case DSPF_A4: return PIXMAN_a4;
+ case DSPF_ARGB1666: return 0;
+ case DSPF_ARGB6666: return 0;
+ case DSPF_RGB18: return 0;
+ case DSPF_LUT2: return 0;
+ case DSPF_RGB444: return PIXMAN_x4r4g4b4;
+ case DSPF_RGB555: return PIXMAN_x1r5g5b5;
+#if DFB_NUM_PIXELFORMATS >= 29
+ case DSPF_BGR555: return PIXMAN_x1b5g5r5;
+#endif
+ }
+ return 0;
+}
+
+static cairo_surface_t *
+_cairo_dfb_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_dfb_surface_t *other = abstract_src;
+ DFBSurfacePixelFormat format;
+ IDirectFBSurface *buffer;
+ DFBSurfaceDescription dsc;
+ cairo_surface_t *surface;
+
+ if (width <= 0 || height <= 0)
+ return _cairo_image_surface_create_with_content (content, width, height);
+
+ switch (content) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ format = DSPF_ARGB;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ format = DSPF_RGB32;
+ break;
+ case CAIRO_CONTENT_ALPHA:
+ format = DSPF_A8;
+ break;
+ }
+
+ dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
+ dsc.caps = DSCAPS_PREMULTIPLIED;
+ dsc.width = width;
+ dsc.height = height;
+ dsc.pixelformat = format;
+
+ if (other->dfb->CreateSurface (other->dfb, &dsc, &buffer))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_ERROR));
+
+ surface = cairo_directfb_surface_create (other->dfb, buffer);
+ buffer->Release (buffer);
+
+ return surface;
+}
+
+static cairo_status_t
+_cairo_dfb_surface_finish (void *abstract_surface)
+{
+ cairo_dfb_surface_t *surface = abstract_surface;
+
+ surface->dfb_surface->Release (surface->dfb_surface);
+ return _cairo_image_surface_finish (abstract_surface);
+}
+
+static cairo_image_surface_t *
+_cairo_dfb_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_dfb_surface_t *surface = abstract_surface;
+
+ if (surface->image.pixman_image == NULL) {
+ IDirectFBSurface *buffer = surface->dfb_surface;
+ pixman_image_t *image;
+ void *data;
+ int pitch;
+
+ if (buffer->Lock (buffer, DSLF_READ | DSLF_WRITE, &data, &pitch))
+ return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ image = pixman_image_create_bits (surface->image.pixman_format,
+ surface->image.width,
+ surface->image.height,
+ data, pitch);
+ if (image == NULL) {
+ buffer->Unlock (buffer);
+ return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ _cairo_image_surface_init (&surface->image, image, surface->image.pixman_format);
+ }
+
+ return _cairo_surface_map_to_image (&surface->image.base, extents);
+}
+
+static cairo_int_status_t
+_cairo_dfb_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_dfb_surface_t *surface = abstract_surface;
+ return _cairo_surface_unmap_image (&surface->image.base, image);
+}
+
+static cairo_status_t
+_cairo_dfb_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ cairo_dfb_surface_t *surface = abstract_surface;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->image.pixman_image) {
+ surface->dfb_surface->Unlock (surface->dfb_surface);
+
+ pixman_image_unref (surface->image.pixman_image);
+ surface->image.pixman_image = NULL;
+ surface->image.data = NULL;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0
+static inline DFBSurfacePixelFormat
+_directfb_from_pixman_format (pixman_format_code_t format)
+{
+ switch ((int) format) {
+ case PIXMAN_a1r5g5b5: return DSPF_ARGB1555;
+ case PIXMAN_r5g6b5: return DSPF_RGB16;
+ case PIXMAN_r8g8b8: return DSPF_RGB24;
+ case PIXMAN_x8r8g8b8: return DSPF_RGB32;
+ case PIXMAN_a8r8g8b8: return DSPF_ARGB;
+ case PIXMAN_a8: return DSPF_A8;
+ case PIXMAN_yuy2: return DSPF_YUY2;
+ case PIXMAN_r3g3b2: return DSPF_RGB332;
+ case PIXMAN_yv12: return DSPF_YV12;
+ case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */
+ case PIXMAN_a4r4g4b4: return DSPF_ARGB4444;
+ case PIXMAN_a4: return DSPF_A4;
+ case PIXMAN_x4r4g4b4: return DSPF_RGB444;
+ case PIXMAN_x1r5g5b5: return DSPF_RGB555;
+#if DFB_NUM_PIXELFORMATS >= 29
+ case PIXMAN_x1b5g5r5: return DSPF_BGR555;
+#endif
+ default: return 0;
+ }
+}
+
+static cairo_bool_t
+_directfb_get_operator (cairo_operator_t operator,
+ DFBSurfaceBlendFunction *ret_srcblend,
+ DFBSurfaceBlendFunction *ret_dstblend)
+{
+ DFBSurfaceBlendFunction srcblend = DSBF_ONE;
+ DFBSurfaceBlendFunction dstblend = DSBF_ZERO;
+
+ switch (operator) {
+ case CAIRO_OPERATOR_CLEAR:
+ srcblend = DSBF_ZERO;
+ dstblend = DSBF_ZERO;
+ break;
+ case CAIRO_OPERATOR_SOURCE:
+ srcblend = DSBF_ONE;
+ dstblend = DSBF_ZERO;
+ break;
+ case CAIRO_OPERATOR_OVER:
+ srcblend = DSBF_ONE;
+ dstblend = DSBF_INVSRCALPHA;
+ break;
+ case CAIRO_OPERATOR_IN:
+ srcblend = DSBF_DESTALPHA;
+ dstblend = DSBF_ZERO;
+ break;
+ case CAIRO_OPERATOR_OUT:
+ srcblend = DSBF_INVDESTALPHA;
+ dstblend = DSBF_ZERO;
+ break;
+ case CAIRO_OPERATOR_ATOP:
+ srcblend = DSBF_DESTALPHA;
+ dstblend = DSBF_INVSRCALPHA;
+ break;
+ case CAIRO_OPERATOR_DEST:
+ srcblend = DSBF_ZERO;
+ dstblend = DSBF_ONE;
+ break;
+ case CAIRO_OPERATOR_DEST_OVER:
+ srcblend = DSBF_INVDESTALPHA;
+ dstblend = DSBF_ONE;
+ break;
+ case CAIRO_OPERATOR_DEST_IN:
+ srcblend = DSBF_ZERO;
+ dstblend = DSBF_SRCALPHA;
+ break;
+ case CAIRO_OPERATOR_DEST_OUT:
+ srcblend = DSBF_ZERO;
+ dstblend = DSBF_INVSRCALPHA;
+ break;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ srcblend = DSBF_INVDESTALPHA;
+ dstblend = DSBF_SRCALPHA;
+ break;
+ case CAIRO_OPERATOR_XOR:
+ srcblend = DSBF_INVDESTALPHA;
+ dstblend = DSBF_INVSRCALPHA;
+ break;
+ case CAIRO_OPERATOR_ADD:
+ srcblend = DSBF_ONE;
+ dstblend = DSBF_ONE;
+ break;
+ case CAIRO_OPERATOR_SATURATE:
+ /* XXX This does not work. */
+#if 0
+ srcblend = DSBF_SRCALPHASAT;
+ dstblend = DSBF_ONE;
+ break;
+#endif
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ default:
+ return FALSE;
+ }
+
+ *ret_srcblend = srcblend;
+ *ret_dstblend = dstblend;
+
+ return TRUE;
+}
+#define RUN_CLIPPED(surface, clip_region, clip, func) {\
+ if ((clip_region) != NULL) {\
+ int n_clips = cairo_region_num_rectangles (clip_region), n; \
+ for (n = 0; n < n_clips; n++) {\
+ if (clip) {\
+ DFBRegion reg, *cli = (clip); \
+ cairo_rectangle_int_t rect; \
+ cairo_region_get_rectangle (clip_region, n, &rect); \
+ reg.x1 = rect.x; \
+ reg.y1 = rect.y; \
+ reg.x2 = rect.x + rect.width - 1; \
+ reg.y2 = rect.y + rect.height - 1; \
+ if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\
+ reg.x1 > cli->x2 || reg.y1 > cli->y2)\
+ continue;\
+ if (reg.x1 < cli->x1)\
+ reg.x1 = cli->x1;\
+ if (reg.y1 < cli->y1)\
+ reg.y1 = cli->y1;\
+ if (reg.x2 > cli->x2)\
+ reg.x2 = cli->x2;\
+ if (reg.y2 > cli->y2)\
+ reg.y2 = cli->y2;\
+ (surface)->dfbsurface->SetClip ((surface)->dfbsurface, &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;\
+ }\
+}
+
+static cairo_int_status_t
+_cairo_dfb_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int n_rects)
+{
+ cairo_dfb_surface_t *dst = abstract_surface;
+ DFBSurfaceDrawingFlags flags;
+ DFBSurfaceBlendFunction sblend;
+ DFBSurfaceBlendFunction dblend;
+ DFBRectangle r[n_rects];
+ int i;
+
+ D_DEBUG_AT (CairoDFB_Render,
+ "%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n",
+ __FUNCTION__, dst, op, color, rects, n_rects);
+
+ if (! dst->supported_destination)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _directfb_get_operator (op, &sblend, &dblend))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (CAIRO_COLOR_IS_OPAQUE (color)) {
+ if (sblend == DSBF_SRCALPHA)
+ sblend = DSBF_ONE;
+ else if (sblend == DSBF_INVSRCALPHA)
+ sblend = DSBF_ZERO;
+
+ if (dblend == DSBF_SRCALPHA)
+ dblend = DSBF_ONE;
+ else if (dblend == DSBF_INVSRCALPHA)
+ dblend = DSBF_ZERO;
+ }
+ if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) {
+ if (sblend == DSBF_DESTALPHA)
+ sblend = DSBF_ONE;
+ else if (sblend == DSBF_INVDESTALPHA)
+ sblend = DSBF_ZERO;
+
+ if (dblend == DSBF_DESTALPHA)
+ dblend = DSBF_ONE;
+ else if (dblend == DSBF_INVDESTALPHA)
+ dblend = DSBF_ZERO;
+ }
+
+ flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) ? DSDRAW_NOFX : DSDRAW_BLEND;
+ dst->dfbsurface->SetDrawingFlags (dst->dfbsurface, flags);
+ if (flags & DSDRAW_BLEND) {
+ dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend);
+ dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend);
+ }
+
+ dst->dfbsurface->SetColor (dst->dfbsurface,
+ color->red_short >> 8,
+ color->green_short >> 8,
+ color->blue_short >> 8,
+ color->alpha_short >> 8);
+
+ for (i = 0; i < n_rects; i++) {
+ r[i].x = rects[i].x;
+ r[i].y = rects[i].y;
+ r[i].w = rects[i].width;
+ r[i].h = rects[i].height;
+ }
+
+ RUN_CLIPPED (dst, NULL, NULL,
+ dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+static cairo_surface_backend_t
+_cairo_dfb_surface_backend = {
+ CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/
+ _cairo_dfb_surface_finish, /*finish*/
+ _cairo_default_context_create,
+
+ _cairo_dfb_surface_create_similar,/*create_similar*/
+ NULL, /* create similar image */
+ _cairo_dfb_surface_map_to_image,
+ _cairo_dfb_surface_unmap_image,
+
+ _cairo_surface_default_source,
+ _cairo_surface_default_acquire_source_image,
+ _cairo_surface_default_release_source_image,
+ NULL,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_image_surface_get_extents,
+ _cairo_image_surface_get_font_options,
+
+ _cairo_dfb_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_surface_fallback_paint,
+ _cairo_surface_fallback_mask,
+ _cairo_surface_fallback_stroke,
+ _cairo_surface_fallback_fill,
+ NULL, /* fill-stroke */
+ _cairo_surface_fallback_glyphs,
+};
+
+cairo_surface_t *
+cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface)
+{
+ cairo_dfb_surface_t *surface;
+ DFBSurfacePixelFormat format;
+ DFBSurfaceCapabilities caps;
+ pixman_format_code_t pixman_format;
+ int width, height;
+
+ D_ASSERT (dfb != NULL);
+ D_ASSERT (dfbsurface != NULL);
+
+ dfbsurface->GetPixelFormat (dfbsurface, &format);
+ dfbsurface->GetSize (dfbsurface, &width, &height);
+
+ pixman_format = _directfb_to_pixman_format (format);
+ if (! pixman_format_supported_destination (pixman_format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ surface = calloc (1, sizeof (cairo_dfb_surface_t));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ /* XXX dfb -> device */
+ _cairo_surface_init (&surface->image.base,
+ &_cairo_dfb_surface_backend,
+ NULL, /* device */
+ _directfb_format_to_content (format));
+
+ surface->image.pixman_format = pixman_format;
+ surface->image.format = _cairo_format_from_pixman_format (pixman_format);
+
+ surface->image.width = width;
+ surface->image.height = height;
+ surface->image.depth = PIXMAN_FORMAT_DEPTH(pixman_format);
+
+ surface->dfb = dfb;
+ surface->dfb_surface = dfbsurface;
+ dfbsurface->AddRef (dfbsurface);
+
+ dfbsurface->GetCapabilities (dfbsurface, &caps);
+ if (caps & DSCAPS_PREMULTIPLIED)
+ surface->blit_premultiplied = TRUE;
+
+ return &surface->image.base;
+}
+slim_hidden_def(cairo_directfb_surface_create);
diff --git a/src/cairo-directfb.h b/src/cairo-directfb.h
new file mode 100755
index 000000000..e3d818c66
--- /dev/null
+++ b/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/src/cairo-drm.h b/src/cairo-drm.h
new file mode 100755
index 000000000..907610dcd
--- /dev/null
+++ b/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/src/cairo-egl-context.c b/src/cairo-egl-context.c
new file mode 100755
index 000000000..fa52a04ba
--- /dev/null
+++ b/src/cairo-egl-context.c
@@ -0,0 +1,421 @@
+/* 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"
+
+#if CAIRO_HAS_EVASGL_SURFACE && CAIRO_HAS_GLESV2_SURFACE
+extern void glActiveTexture (GLenum texture);
+extern void glBindTexture (GLenum target, GLuint texture);
+extern void glBlendFunc (GLenum sfactor, GLenum dfactor);
+extern void glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB,
+ GLenum srcAlpha, GLenum dstAlpha);
+extern void glClear (GLbitfield mask);
+extern void glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+extern void glClearStencil (GLint s);
+extern void glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+extern void glDeleteTextures (GLsizei n, const GLuint *textures);
+extern void glDepthMask (GLboolean flag);
+extern void glDisable (GLenum cap);
+extern void glDrawArrays (GLenum mode, GLint first, GLsizei count);
+extern void glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+extern void glEnable (GLenum cap);
+extern void glFlush (void);
+extern void glGenTextures (GLsizei n, GLuint *textures);
+extern void glGetBooleanv (GLenum pname, GLboolean *data);
+extern GLenum glGetError (void);
+extern void glGetFloatv (GLenum pname, GLfloat *data);
+extern void glGetIntegerv (GLenum pname, GLint *data);
+extern const unsigned char* glGetString (GLenum pname);
+extern void glPixelStorei (GLenum pname, GLint param);
+extern void glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid *data);
+extern void glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
+extern void glStencilFunc (GLenum func, GLint ref, GLuint mask);
+extern void glStencilMask (GLuint mask);
+extern void glStencilOp (GLenum sfail, GLenum dpfail, GLenum dppass);
+extern void glTexSubImage2D (GLenum target, GLint level,
+ GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type, const GLvoid *data);
+extern void glTexImage2D (GLenum target, GLint level, GLenum internalformat,
+ GLsizei width, GLsizei height,
+ GLint border, GLenum format,
+ GLenum type, const GLvoid *data);
+extern void glTexParameteri (GLenum target, GLenum pname, GLint param);
+extern void glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
+#endif
+
+typedef struct _cairo_egl_context {
+ cairo_gl_context_t base;
+
+ EGLDisplay display;
+ EGLContext context;
+
+ EGLSurface dummy_surface;
+ EGLSurface current_surface;
+
+ EGLDisplay previous_display;
+ EGLContext previous_context;
+ EGLSurface previous_surface;
+} cairo_egl_context_t;
+
+typedef struct _cairo_egl_surface {
+ cairo_gl_surface_t base;
+
+ EGLSurface egl;
+} cairo_egl_surface_t;
+
+
+static cairo_bool_t
+_context_acquisition_changed_egl_state (cairo_egl_context_t *ctx,
+ EGLSurface current_surface)
+{
+ return ctx->previous_context != ctx->context ||
+ ctx->previous_surface != current_surface ||
+ ctx->previous_display != ctx->display;
+}
+
+static EGLSurface
+_egl_get_current_surface (cairo_egl_context_t *ctx)
+{
+ if (ctx->base.current_target == NULL ||
+ _cairo_gl_surface_is_texture (ctx->base.current_target)) {
+ return ctx->dummy_surface;
+ }
+
+ return ((cairo_egl_surface_t *) ctx->base.current_target)->egl;
+}
+
+static void
+_egl_query_current_state (cairo_egl_context_t *ctx)
+{
+ ctx->previous_surface = eglGetCurrentSurface (EGL_DRAW);
+ ctx->previous_context = eglGetCurrentContext ();
+ ctx->previous_display = eglGetCurrentDisplay ();
+
+ /* If any of the values were none, assume they are all none. Not all
+ drivers seem well behaved when it comes to using these values across
+ multiple threads. */
+ if (ctx->previous_surface == EGL_NO_SURFACE ||
+ ctx->previous_context == EGL_NO_CONTEXT || ctx->previous_display == EGL_NO_DISPLAY) {
+ ctx->previous_surface = EGL_NO_SURFACE;
+ ctx->previous_context = EGL_NO_CONTEXT;
+ ctx->previous_display = EGL_NO_DISPLAY;
+ }
+}
+
+static void
+_egl_acquire (void *abstract_ctx)
+{
+ cairo_egl_context_t *ctx = abstract_ctx;
+ EGLSurface current_surface = _egl_get_current_surface (ctx);
+
+ _egl_query_current_state (ctx);
+ if (!_context_acquisition_changed_egl_state (ctx, current_surface))
+ return;
+
+ _cairo_gl_context_reset (&ctx->base);
+ eglMakeCurrent (ctx->display,
+ current_surface, current_surface, ctx->context);
+
+ ctx->current_surface = current_surface;
+}
+
+static void
+_egl_release (void *abstract_ctx)
+{
+ cairo_egl_context_t *ctx = abstract_ctx;
+ if (!ctx->base.thread_aware ||
+ !_context_acquisition_changed_egl_state (ctx,
+ _egl_get_current_surface (ctx))) {
+ return;
+ }
+
+ eglMakeCurrent (ctx->display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ ctx->current_surface = EGL_NO_SURFACE;
+}
+
+static void
+_egl_make_current (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{
+ cairo_egl_context_t *ctx = abstract_ctx;
+ cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
+
+ if (surface->egl != ctx->current_surface) {
+ eglMakeCurrent(ctx->display, surface->egl, surface->egl, ctx->context);
+ ctx->current_surface = surface->egl;
+ }
+}
+
+static void
+_egl_swap_buffers (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{
+ cairo_egl_context_t *ctx = abstract_ctx;
+ cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
+
+ eglSwapBuffers (ctx->display, surface->egl);
+}
+
+static void
+_egl_destroy (void *abstract_ctx)
+{
+ cairo_egl_context_t *ctx = abstract_ctx;
+
+ eglMakeCurrent (ctx->display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ if (ctx->dummy_surface != EGL_NO_SURFACE)
+ eglDestroySurface (ctx->display, ctx->dummy_surface);
+}
+
+static cairo_bool_t
+_egl_make_current_surfaceless(cairo_egl_context_t *ctx)
+{
+ const char *extensions;
+
+ extensions = eglQueryString(ctx->display, EGL_EXTENSIONS);
+ if (extensions == NULL)
+ return FALSE;
+
+ if (strstr(extensions, "EGL_KHR_surfaceless_context") == NULL &&
+ strstr(extensions, "EGL_KHR_surfaceless_opengl") == NULL)
+ return FALSE;
+
+ if (!eglMakeCurrent(ctx->display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->context))
+ return FALSE;
+
+ return TRUE;
+}
+
+static cairo_gl_generic_func_t
+_cairo_egl_get_proc_address (void *data, const char *name)
+{
+ int i;
+ struct {
+ cairo_gl_generic_func_t func;
+ const char *name;
+ } func_map[] = {
+ { (cairo_gl_generic_func_t)glActiveTexture, "glActiveTexture" },
+ { (cairo_gl_generic_func_t)glBindTexture, "glBindTexture" },
+ { (cairo_gl_generic_func_t)glBlendFunc, "glBlendFunc" },
+ { (cairo_gl_generic_func_t)glBlendFuncSeparate,"glBlendFuncSeparate"},
+ { (cairo_gl_generic_func_t)glClear, "glClear" },
+ { (cairo_gl_generic_func_t)glClearColor, "glClearColor" },
+ { (cairo_gl_generic_func_t)glClearStencil, "glClearStencil" },
+ { (cairo_gl_generic_func_t)glColorMask, "glColorMask" },
+ { (cairo_gl_generic_func_t)glDeleteTextures,"glDeleteTextures" },
+ { (cairo_gl_generic_func_t)glDepthMask, "glDepthMask" },
+ { (cairo_gl_generic_func_t)glDisable, "glDisable" },
+ { (cairo_gl_generic_func_t)glDrawArrays, "glDrawArrays" },
+ { (cairo_gl_generic_func_t)glDrawElements, "glDrawElements" },
+ { (cairo_gl_generic_func_t)glEnable, "glEnable" },
+ { (cairo_gl_generic_func_t)glGenTextures, "glGenTextures" },
+ { (cairo_gl_generic_func_t)glGetBooleanv, "glGetBooleanv" },
+ { (cairo_gl_generic_func_t)glGetError, "glGetError" },
+ { (cairo_gl_generic_func_t)glGetFloatv, "glGetFloatv" },
+ { (cairo_gl_generic_func_t)glGetIntegerv, "glGetIntegerv" },
+ { (cairo_gl_generic_func_t)glGetString, "glGetString" },
+ { (cairo_gl_generic_func_t)glFlush, "glFlush" },
+ { (cairo_gl_generic_func_t)glPixelStorei, "glPixelStorei" },
+ { (cairo_gl_generic_func_t)glReadPixels, "glReadPixels" },
+ { (cairo_gl_generic_func_t)glScissor, "glScissor" },
+ { (cairo_gl_generic_func_t)glStencilFunc, "glStencilFunc" },
+ { (cairo_gl_generic_func_t)glStencilMask, "glStencilMask" },
+ { (cairo_gl_generic_func_t)glStencilOp, "glStencilOp" },
+ { (cairo_gl_generic_func_t)glTexImage2D, "glTexImage2D" },
+ { (cairo_gl_generic_func_t)glTexSubImage2D, "glTexSubImage2D" },
+ { (cairo_gl_generic_func_t)glTexParameteri, "glTexParameteri" },
+ { (cairo_gl_generic_func_t)glViewport, "glViewport" },
+ { NULL, NULL }
+ };
+
+ for (i = 0; func_map[i].name; i++) {
+ if (! strncmp (func_map[i].name, name, strlen(name)))
+ return func_map[i].func;
+ }
+
+ return eglGetProcAddress (name);
+}
+
+cairo_device_t *
+cairo_egl_device_create (EGLDisplay dpy, EGLContext egl)
+{
+ cairo_egl_context_t *ctx;
+ cairo_status_t status;
+ int attribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE,
+ };
+ EGLConfig config;
+ EGLint numConfigs;
+
+ ctx = calloc (1, sizeof (cairo_egl_context_t));
+ if (unlikely (ctx == NULL))
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ ctx->display = dpy;
+ ctx->context = egl;
+
+ ctx->base.acquire = _egl_acquire;
+ ctx->base.release = _egl_release;
+ ctx->base.make_current = _egl_make_current;
+ ctx->base.swap_buffers = _egl_swap_buffers;
+ ctx->base.destroy = _egl_destroy;
+
+ /* We are about the change the current state of EGL, so we should
+ * query the pre-existing surface now instead of later. */
+ _egl_query_current_state (ctx);
+
+ if (!_egl_make_current_surfaceless (ctx)) {
+ /* Fall back to dummy surface, meh. */
+ EGLint config_attribs[] = {
+ EGL_CONFIG_ID, 0,
+ EGL_NONE
+ };
+
+ /*
+ * In order to be able to make an egl context current when using a
+ * pbuffer surface, that surface must have been created with a config
+ * that is compatible with the context config. For Mesa, this means
+ * that the configs must be the same.
+ */
+ eglQueryContext (dpy, egl, EGL_CONFIG_ID, &config_attribs[1]);
+ eglChooseConfig (dpy, config_attribs, &config, 1, &numConfigs);
+
+ ctx->dummy_surface = eglCreatePbufferSurface (dpy, config, attribs);
+ if (ctx->dummy_surface == NULL) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (!eglMakeCurrent (dpy, ctx->dummy_surface, ctx->dummy_surface, egl)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
+ status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
+ _cairo_egl_get_proc_address, NULL);
+ if (unlikely (status)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ status = _cairo_gl_context_init (&ctx->base);
+ if (unlikely (status)) {
+ if (ctx->dummy_surface != EGL_NO_SURFACE)
+ eglDestroySurface (dpy, ctx->dummy_surface);
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ ctx->current_surface = EGL_NO_SURFACE;
+
+ return &ctx->base.base;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_egl (cairo_device_t *device,
+ EGLSurface egl,
+ int width,
+ int height)
+{
+ cairo_egl_surface_t *surface;
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+ if (width <= 0 || height <= 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ surface = calloc (1, sizeof (cairo_egl_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_gl_surface_init (device, &surface->base,
+ CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ surface->egl = egl;
+
+ return &surface->base.base;
+}
+
+static cairo_bool_t is_egl_device (cairo_device_t *device)
+{
+ return (device->backend != NULL &&
+ device->backend->type == CAIRO_DEVICE_TYPE_GL);
+}
+
+static cairo_egl_context_t *to_egl_context (cairo_device_t *device)
+{
+ return (cairo_egl_context_t *) device;
+}
+
+EGLDisplay
+cairo_egl_device_get_display (cairo_device_t *device)
+{
+ if (! is_egl_device (device)) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return EGL_NO_DISPLAY;
+ }
+
+ return to_egl_context (device)->display;
+}
+
+cairo_public EGLContext
+cairo_egl_device_get_context (cairo_device_t *device)
+{
+ if (! is_egl_device (device)) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return EGL_NO_CONTEXT;
+ }
+
+ return to_egl_context (device)->context;
+}
diff --git a/src/cairo-error-inline.h b/src/cairo-error-inline.h
new file mode 100755
index 000000000..9126c5e61
--- /dev/null
+++ b/src/cairo-error-inline.h
@@ -0,0 +1,52 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_ERROR_INLINE_H_
+#define _CAIRO_ERROR_INLINE_H_
+
+#include "cairo-error-private.h"
+
+CAIRO_BEGIN_DECLS
+
+static inline cairo_status_t
+_cairo_public_status (cairo_int_status_t status)
+{
+ assert (status <= CAIRO_INT_STATUS_LAST_STATUS);
+ return (cairo_status_t) status;
+}
+
+#endif /* _CAIRO_ERROR_INLINE_H_ */
diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h
new file mode 100755
index 000000000..ea9c2ea31
--- /dev/null
+++ b/src/cairo-error-private.h
@@ -0,0 +1,123 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_ERROR_PRIVATE_H_
+#define _CAIRO_ERROR_PRIVATE_H_
+
+#include "cairo.h"
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+#include <assert.h>
+
+CAIRO_BEGIN_DECLS
+
+/* Sure wish C had a real enum type so that this would be distinct
+ * from #cairo_status_t. Oh well, without that, I'll use this bogus 100
+ * offset. We want to keep it fit in int8_t as the compiler may choose
+ * that for #cairo_status_t */
+enum _cairo_int_status {
+ CAIRO_INT_STATUS_SUCCESS = 0,
+
+ CAIRO_INT_STATUS_NO_MEMORY,
+ CAIRO_INT_STATUS_INVALID_RESTORE,
+ CAIRO_INT_STATUS_INVALID_POP_GROUP,
+ CAIRO_INT_STATUS_NO_CURRENT_POINT,
+ CAIRO_INT_STATUS_INVALID_MATRIX,
+ CAIRO_INT_STATUS_INVALID_STATUS,
+ CAIRO_INT_STATUS_NULL_POINTER,
+ CAIRO_INT_STATUS_INVALID_STRING,
+ CAIRO_INT_STATUS_INVALID_PATH_DATA,
+ CAIRO_INT_STATUS_READ_ERROR,
+ CAIRO_INT_STATUS_WRITE_ERROR,
+ CAIRO_INT_STATUS_SURFACE_FINISHED,
+ CAIRO_INT_STATUS_SURFACE_TYPE_MISMATCH,
+ CAIRO_INT_STATUS_PATTERN_TYPE_MISMATCH,
+ CAIRO_INT_STATUS_INVALID_CONTENT,
+ CAIRO_INT_STATUS_INVALID_FORMAT,
+ CAIRO_INT_STATUS_INVALID_VISUAL,
+ CAIRO_INT_STATUS_FILE_NOT_FOUND,
+ CAIRO_INT_STATUS_INVALID_DASH,
+ CAIRO_INT_STATUS_INVALID_DSC_COMMENT,
+ CAIRO_INT_STATUS_INVALID_INDEX,
+ CAIRO_INT_STATUS_CLIP_NOT_REPRESENTABLE,
+ CAIRO_INT_STATUS_TEMP_FILE_ERROR,
+ CAIRO_INT_STATUS_INVALID_STRIDE,
+ CAIRO_INT_STATUS_FONT_TYPE_MISMATCH,
+ CAIRO_INT_STATUS_USER_FONT_IMMUTABLE,
+ CAIRO_INT_STATUS_USER_FONT_ERROR,
+ CAIRO_INT_STATUS_NEGATIVE_COUNT,
+ CAIRO_INT_STATUS_INVALID_CLUSTERS,
+ CAIRO_INT_STATUS_INVALID_SLANT,
+ CAIRO_INT_STATUS_INVALID_WEIGHT,
+ CAIRO_INT_STATUS_INVALID_SIZE,
+ CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED,
+ CAIRO_INT_STATUS_DEVICE_TYPE_MISMATCH,
+ CAIRO_INT_STATUS_DEVICE_ERROR,
+ CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION,
+ CAIRO_INT_STATUS_DEVICE_FINISHED,
+
+ CAIRO_INT_STATUS_LAST_STATUS,
+
+ CAIRO_INT_STATUS_UNSUPPORTED = 100,
+ CAIRO_INT_STATUS_DEGENERATE,
+ CAIRO_INT_STATUS_NOTHING_TO_DO,
+ CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY,
+ CAIRO_INT_STATUS_IMAGE_FALLBACK,
+ CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN,
+};
+
+typedef enum _cairo_int_status cairo_int_status_t;
+
+#define _cairo_status_is_error(status) \
+ (status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS)
+
+#define _cairo_int_status_is_error(status) \
+ (status != CAIRO_INT_STATUS_SUCCESS && status < CAIRO_INT_STATUS_LAST_STATUS)
+
+cairo_private cairo_status_t
+_cairo_error (cairo_status_t status);
+
+/* hide compiler warnings when discarding the return value */
+#define _cairo_error_throw(status) do { \
+ cairo_status_t status__ = _cairo_error (status); \
+ (void) status__; \
+} while (0)
+
+CAIRO_END_DECLS
+
+#endif /* _CAIRO_ERROR_PRIVATE_H_ */
diff --git a/src/cairo-error.c b/src/cairo-error.c
new file mode 100755
index 000000000..1b9bd76be
--- /dev/null
+++ b/src/cairo-error.c
@@ -0,0 +1,73 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-private.h"
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+
+#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));
+
+ return status;
+}
+
+COMPILE_TIME_ASSERT ((int)CAIRO_INT_STATUS_LAST_STATUS == (int)CAIRO_STATUS_LAST_STATUS);
diff --git a/src/cairo-evas-gl-context.c b/src/cairo-evas-gl-context.c
new file mode 100755
index 000000000..3f256cbc2
--- /dev/null
+++ b/src/cairo-evas-gl-context.c
@@ -0,0 +1,438 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2014 Samsung Research America, Inc - Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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>
+ * Henry Song <henry.song@samsung.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+#include "cairo-error-private.h"
+#if CAIRO_HAS_DLSYM
+#include <dlfcn.h>
+#endif
+
+#include "cairo-evas-gl.h"
+
+typedef struct _cairo_evas_gl_context {
+ cairo_gl_context_t base;
+
+ Evas_GL *evas_gl;
+ Evas_GL_Surface *surface;
+ Evas_GL_Context *context;
+
+ Evas_GL_Surface *dummy_surface;
+ Evas_GL_Surface *current_surface;
+
+ Evas_GL_Context *queried_context;
+
+ cairo_bool_t has_multithread_makecurrent;
+} cairo_evas_gl_context_t;
+
+typedef struct _cairo_evas_gl_surface {
+ cairo_gl_surface_t base;
+
+ Evas_GL_Surface *surface;
+} cairo_evas_gl_surface_t;
+
+/* as of efl-1.12.0, the following GL functions are supported, for other
+ GL functions and extensions, we us dlsym to get.
+
+ The following APIs are not supported by efl-1.12
+ glDrawBuffer, glReadBuffer, glMapBuffer, glUnmapBuffer,
+ glBlitFramebuffer{ANGLE}, glRenderbufferStorageMultisample{ANGLE},
+
+ This functions are queried via dlsym. cairo does not use map/unmap
+ buffer. For rest unsupported GL functions, they should not change
+ evas_gl internal context states (I hope).
+ */
+static cairo_gl_generic_func_t
+_cairo_evas_gl_get_proc_addr (void *data, const char *name)
+{
+ static Evas_GL_API *api;
+ Evas_GL *gl = (Evas_GL *) data;
+ int i;
+
+ struct {
+ size_t func;
+ const char *name;
+ } evas_gl_func_map[] = {
+ /* core function, glReadBuffer and glDrawBuffer not supported */
+ { offsetof (Evas_GL_API, glActiveTexture ), "glActiveTexture" },
+ { offsetof (Evas_GL_API, glBindTexture ), "glBindTexture" },
+ { offsetof (Evas_GL_API, glBlendFunc ), "glBlendFunc" },
+ { offsetof (Evas_GL_API, glBlendFuncSeparate ), "glBlendFuncSeparate" },
+ { offsetof (Evas_GL_API, glClear ), "glClear" },
+ { offsetof (Evas_GL_API, glClearColor ), "glClearColor" },
+ { offsetof (Evas_GL_API, glClearStencil ), "glClearStencil" },
+ { offsetof (Evas_GL_API, glColorMask ), "glColorMask" },
+ { offsetof (Evas_GL_API, glDeleteTextures ), "glDeleteTextures" },
+ { offsetof (Evas_GL_API, glDepthMask ), "glDepthMask" },
+ { offsetof (Evas_GL_API, glDisable ), "glDisable" },
+ { offsetof (Evas_GL_API, glDrawArrays ), "glDrawArrays" },
+ { offsetof (Evas_GL_API, glDrawElements ), "glDrawElements" },
+ { offsetof (Evas_GL_API, glEnable ), "glEnable" },
+ { offsetof (Evas_GL_API, glGenTextures ), "glGenTextures" },
+ { offsetof (Evas_GL_API, glGetBooleanv ), "glGetBooleanv" },
+ { offsetof (Evas_GL_API, glGetError ), "glGetError" },
+ { offsetof (Evas_GL_API, glGetFloatv ), "glGetFloatv" },
+ { offsetof (Evas_GL_API, glGetIntegerv ), "glGetIntegerv" },
+ { offsetof (Evas_GL_API, glGetString ), "glGetString" },
+ { offsetof (Evas_GL_API, glPixelStorei ), "glPixelStorei" },
+ { offsetof (Evas_GL_API, glReadPixels ), "glReadPixels" },
+ { offsetof (Evas_GL_API, glScissor ), "glScissor" },
+ { offsetof (Evas_GL_API, glStencilFunc ), "glStencilFunc" },
+ { offsetof (Evas_GL_API, glStencilMask ), "glStencilMask" },
+ { offsetof (Evas_GL_API, glStencilOp ), "glStencilOp" },
+ { offsetof (Evas_GL_API, glTexImage2D ), "glTexImage2D" },
+ { offsetof (Evas_GL_API, glTexSubImage2D ), "glTexSubImage2D" },
+ { offsetof (Evas_GL_API, glTexParameteri ), "glTexParameteri" },
+ { offsetof (Evas_GL_API, glViewport ), "glViewport" },
+ /* buffers functions, glMapBufferOES, glUnmapBufferOES not
+ suported
+ */
+ { offsetof (Evas_GL_API, glGenBuffers ), "glGenBuffers" },
+ { offsetof (Evas_GL_API, glBindBuffer ), "glBindBuffer" },
+ { offsetof (Evas_GL_API, glBufferData ), "glBufferData" },
+ /* shader functions */
+ { offsetof (Evas_GL_API, glCreateShader ), "glCreateShader" },
+ { offsetof (Evas_GL_API, glShaderSource ), "glShaderSource" },
+ { offsetof (Evas_GL_API, glCompileShader ), "glCompileShader" },
+ { offsetof (Evas_GL_API, glGetShaderiv ), "glGetShaderiv" },
+ { offsetof (Evas_GL_API, glGetShaderInfoLog ), "glGetShaderInfoLog" },
+ { offsetof (Evas_GL_API, glDeleteShader ), "glDeleteShader" },
+ /* program functions */
+ { offsetof (Evas_GL_API, glCreateProgram ), "glCreateProgram" },
+ { offsetof (Evas_GL_API, glAttachShader ), "glAttachShader" },
+ { offsetof (Evas_GL_API, glDeleteProgram ), "glDeleteProgram" },
+ { offsetof (Evas_GL_API, glLinkProgram ), "glLinkProgram" },
+ { offsetof (Evas_GL_API, glUseProgram ), "glUseProgram" },
+ { offsetof (Evas_GL_API, glGetProgramiv ), "glGetProgramiv" },
+ { offsetof (Evas_GL_API, glGetProgramInfoLog ), "glGetProgramInfoLog" },
+ /* uniform functions */
+ { offsetof (Evas_GL_API, glGetUniformLocation ), "glGetUniformLocation" },
+ { offsetof (Evas_GL_API, glUniform1f ), "glUniform1f" },
+ { offsetof (Evas_GL_API, glUniform2f ), "glUniform2f" },
+ { offsetof (Evas_GL_API, glUniform3f ), "glUniform3f" },
+ { offsetof (Evas_GL_API, glUniform4f ), "glUniform4f" },
+ { offsetof (Evas_GL_API, glUniform1fv ), "glUniform1fv" },
+ { offsetof (Evas_GL_API, glUniformMatrix3fv ), "glUniformMatrix3fv" },
+ { offsetof (Evas_GL_API, glUniformMatrix4fv ), "glUniformMatrix4fv" },
+ { offsetof (Evas_GL_API, glUniform1i ), "glUniform1i" },
+ /* attribute functions */
+ { offsetof (Evas_GL_API, glBindAttribLocation ), "glBindAttribLocation" },
+ { offsetof (Evas_GL_API, glVertexAttribPointer ), "glVertexAttribPointer" },
+ { offsetof (Evas_GL_API, glEnableVertexAttribArray), "glEnableVertexAttribArray" },
+ { offsetof (Evas_GL_API, glDisableVertexAttribArray), "glDisableVertexAttribArray" },
+ /* fbo functions */
+ { offsetof (Evas_GL_API, glGenFramebuffers ), "glGenFramebuffers" },
+ { offsetof (Evas_GL_API, glBindFramebuffer ), "glBindFramebuffer" },
+ { offsetof (Evas_GL_API, glFramebufferTexture2D ), "glFramebufferTexture2D" },
+ { offsetof (Evas_GL_API, glCheckFramebufferStatus), "glCheckFramebufferStatus" },
+ { offsetof (Evas_GL_API, glDeleteFramebuffers ), "glDeleteFramebuffers" },
+ { offsetof (Evas_GL_API, glGenRenderbuffers ), "glGenRenderbuffers" },
+ { offsetof (Evas_GL_API, glBindRenderbuffer ), "glBindRenderbuffer" },
+ { offsetof (Evas_GL_API, glRenderbufferStorage ), "glRenderbufferStorage" },
+ { offsetof (Evas_GL_API, glFramebufferRenderbuffer), "glFramebufferRenderbuffer" },
+ { offsetof (Evas_GL_API, glDeleteRenderbuffers ), "glDeleteRenderbuffers" },
+ /* multisampling functions, none supported in efl-1.12.0 */
+ { 0, NULL }
+ };
+
+ api = evas_gl_api_get (gl);
+
+ for (i = 0; evas_gl_func_map[i].name; i++) {
+ if (! strncmp (evas_gl_func_map[i].name, name, strlen(name)))
+ return *((cairo_gl_generic_func_t *) (((char *) &api->version) + evas_gl_func_map[i].func));
+ }
+
+ return evas_gl_proc_address_get (gl, name);
+}
+
+static cairo_bool_t
+_context_acquisition_changed_evas_gl_state (cairo_evas_gl_context_t *ctx,
+ Evas_GL_Surface *current_surface)
+{
+ return ctx->queried_context != ctx->context ||
+ ctx->current_surface != current_surface;
+}
+
+static Evas_GL_Surface *
+_evas_gl_get_current_surface (cairo_evas_gl_context_t *ctx)
+{
+ if (ctx->base.current_target == NULL ||
+ _cairo_gl_surface_is_texture (ctx->base.current_target)) {
+ return ctx->dummy_surface;
+ }
+
+ return ((cairo_evas_gl_surface_t *) ctx->base.current_target)->surface;
+}
+
+static void
+_evas_gl_query_current_state (cairo_evas_gl_context_t *ctx)
+{
+ ctx->queried_context = evas_gl_current_context_get (ctx->evas_gl);
+ ctx->current_surface = evas_gl_current_surface_get (ctx->evas_gl);
+}
+
+static void
+_evas_gl_acquire (void *abstract_ctx)
+{
+ cairo_evas_gl_context_t *ctx = abstract_ctx;
+ Evas_GL_Surface *current_surface = _evas_gl_get_current_surface (ctx);
+
+ _evas_gl_query_current_state (ctx);
+ if (!_context_acquisition_changed_evas_gl_state (ctx, current_surface))
+ return;
+
+ _cairo_gl_context_reset (&ctx->base);
+ evas_gl_make_current (ctx->evas_gl, current_surface, ctx->context);
+
+ ctx->current_surface = current_surface;
+ //ctx->base.current_target = &ctx->dummy_surface->base;
+}
+
+static void
+_evas_gl_release (void *abstract_ctx)
+{
+ cairo_evas_gl_context_t *ctx = abstract_ctx;
+ if (!ctx->base.thread_aware || ctx->has_multithread_makecurrent ||
+ !_context_acquisition_changed_evas_gl_state (ctx,
+ _evas_gl_get_current_surface (ctx))) {
+ return;
+ }
+
+ _cairo_gl_composite_flush (&ctx->base);
+ evas_gl_make_current (ctx->evas_gl, NULL, NULL);
+ ctx->current_surface = NULL;
+}
+
+static void
+_evas_gl_make_current (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{
+ cairo_evas_gl_context_t *ctx = abstract_ctx;
+ cairo_evas_gl_surface_t *surface = (cairo_evas_gl_surface_t *) abstract_surface;
+
+ if (surface->surface != ctx->current_surface) {
+ evas_gl_make_current (ctx->evas_gl, surface->surface, ctx->context);
+ ctx->current_surface = surface->surface;
+ }
+}
+
+static void
+_evas_gl_swap_buffers (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{ }
+
+static void
+_evas_gl_destroy (void *abstract_ctx)
+{
+ cairo_evas_gl_context_t *ctx = abstract_ctx;
+
+ evas_gl_make_current (ctx->evas_gl, NULL, NULL);
+ if (! ctx->dummy_surface)
+ evas_gl_surface_destroy (ctx->evas_gl, ctx->dummy_surface);
+}
+
+cairo_device_t *
+cairo_evas_gl_device_create (Evas_GL *evas_gl,
+ Evas_GL_Context *evas_context)
+{
+ Evas_GL_Config *evas_cfg;
+ cairo_evas_gl_context_t *ctx;
+ cairo_status_t status;
+
+ if (! evas_gl ||! evas_context) {
+ fprintf (stderr, "cairo_evas_gl_device_create(): evas_gl or evas_context is NULL\n");
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NULL_POINTER);
+ }
+
+ ctx = calloc (1, sizeof (cairo_evas_gl_context_t));
+ if (unlikely (ctx == NULL))
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ ctx->evas_gl = evas_gl;
+ ctx->context = evas_context;
+ evas_cfg = evas_gl_config_new ();
+
+ ctx->base.acquire = _evas_gl_acquire;
+ ctx->base.release = _evas_gl_release;
+ ctx->base.make_current = _evas_gl_make_current;
+ ctx->base.swap_buffers = _evas_gl_swap_buffers;
+ ctx->base.destroy = _evas_gl_destroy;
+
+ /* We are about the change the current state of evas-gl, so we should
+ * query the pre-existing surface now instead of later. */
+ _evas_gl_query_current_state (ctx);
+
+ ctx->dummy_surface = evas_gl_pbuffer_surface_create (ctx->evas_gl,
+ evas_cfg,
+ 1, 1, NULL);
+ //evas_gl_config_free (evas_cfg);
+
+ if (ctx->dummy_surface == NULL) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (!evas_gl_make_current (ctx->evas_gl, ctx->dummy_surface, evas_context)) {
+ evas_gl_surface_destroy (ctx->evas_gl, ctx->dummy_surface);
+ free (ctx);
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
+ _cairo_evas_gl_get_proc_addr,
+ ctx->evas_gl);
+ if (unlikely (status)) {
+ evas_gl_surface_destroy (ctx->evas_gl, ctx->dummy_surface);
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ status = _cairo_gl_context_init (&ctx->base);
+ if (unlikely (status)) {
+ evas_gl_surface_destroy (ctx->evas_gl, ctx->dummy_surface);
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ if (strstr(evas_gl_string_query (ctx->evas_gl, EVAS_GL_EXTENSIONS),
+ "GLX_MESA_multithread_makecurrent"))
+ ctx->has_multithread_makecurrent = TRUE;
+ else
+ ctx->has_multithread_makecurrent = FALSE;
+
+ evas_gl_make_current (ctx->evas_gl, NULL, NULL);
+ return &ctx->base.base;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_evas_gl (cairo_device_t *device,
+ Evas_GL_Surface *evas_surface,
+ Evas_GL_Config *evas_config,
+ int width,
+ int height)
+{
+ cairo_evas_gl_surface_t *surface;
+
+ if ((! device)||(cairo_device_status(device)!= CAIRO_STATUS_SUCCESS)){
+ fprintf (stderr, "cairo_gl_surface_create_for_evas_gl(): cairo device is NULL or not available\n");
+ return _cairo_surface_create_in_error(CAIRO_STATUS_NULL_POINTER);
+ }
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ if (! evas_surface || ! evas_config) {
+ fprintf (stderr, "cairo_gl_surface_create_for_evas_gl(): evas_surface or evas_config is NULL\n");
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NULL_POINTER);
+ }
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+ if (width <= 0 || height <= 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ surface = calloc (1, sizeof (cairo_evas_gl_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);
+
+ if (evas_config->stencil_bits != EVAS_GL_STENCIL_NONE)
+ surface->base.supports_stencil = TRUE;
+
+ /* does not matter num of samples */
+ if (evas_config->multisample_bits != EVAS_GL_MULTISAMPLE_NONE) {
+ surface->base.num_samples = 2;
+ surface->base.supports_msaa = TRUE;
+ }
+
+ surface->base.stencil_and_msaa_caps_initialized = TRUE;
+
+ surface->surface = evas_surface;
+
+ return &surface->base.base;
+}
+
+static cairo_bool_t is_evas_gl_device (cairo_device_t *device)
+{
+ return (device->backend != NULL &&
+ device->backend->type == CAIRO_DEVICE_TYPE_GL);
+}
+
+static cairo_evas_gl_context_t *to_evas_gl_context (cairo_device_t *device)
+{
+ return (cairo_evas_gl_context_t *) device;
+}
+
+cairo_public Evas_GL *
+cairo_evas_gl_device_get_gl (cairo_device_t *device)
+{
+ if ((! device)||(cairo_device_status(device)!= CAIRO_STATUS_SUCCESS)){
+ fprintf(stderr,"\n cairo_evas_gl_device_get_gl(): cairo device is NULL or not available");
+ _cairo_error_throw(CAIRO_STATUS_DEVICE_ERROR);
+ return NULL;
+ }
+
+ if (! is_evas_gl_device (device)) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return to_evas_gl_context (device)->evas_gl;
+}
+
+cairo_public Evas_GL_Context *
+cairo_evas_gl_device_get_context (cairo_device_t *device)
+{
+ if ((! device)||(cairo_device_status(device)!= CAIRO_STATUS_SUCCESS)){
+ fprintf(stderr,"\n cairo_evas_gl_device_get_context(): cairo device is NULL or not available");
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR);
+ return NULL;
+ }
+
+ if (! is_evas_gl_device (device)) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return to_evas_gl_context (device)->context;
+}
diff --git a/src/cairo-evas-gl.h b/src/cairo-evas-gl.h
new file mode 100755
index 000000000..bfd93ba94
--- /dev/null
+++ b/src/cairo-evas-gl.h
@@ -0,0 +1,253 @@
+/* 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_EVAS_GL_H
+#define CAIRO_EVAS_GL_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_EVASGL_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+#include <Evas_GL.h>
+
+/**
+ * @addtogroup CAPI_CAIRO_EVAS_GL_MODULE
+ * @{
+ */
+
+/* Evas_GL.h does not define GLchar */
+typedef char GLchar;
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+
+#ifndef GL_BACK_LEFT
+#define GL_BACK_LEFT 0x402
+#endif
+
+#ifndef GL_DEPTH_STENCIL
+#define GL_DEPTH_STENCIL 0x84F9
+#endif
+
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 0x88F0
+#endif
+
+#ifndef GL_MAX_SAMPLES
+#define GL_MAX_SAMPLES 0x8D57
+#endif
+
+#ifndef GL_DEPTH_STENCIL_ATTACHMENT
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#endif
+
+/* Cairo-gl API, which is Open-source, can be used at the Cairo_EvasGL backend */
+
+/**
+ * @brief Create a cairo GL surface using the device as the underlying rendering system.
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] device The given cairo_device_t represents the driver interface to underlying rendering system
+ * @param[in] content Type of content in the surface
+ * @param[in] width Width of the surface, in pixels
+ * @param[in] height Height of the surface, in pixels
+ *
+ * @return The created surface or NULL on failure
+ * The error value on failure can be retrieved with cairo_status().
+ */
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create (cairo_device_t *device,
+ cairo_content_t content,
+ int width, int height);
+
+/**
+ * @brief Create a cairo GL surface using the texture as the render target, and the device as the underlying rendering system.\n
+ * The content must match the format of the texture\n
+ * CAIRO_CONTENT_ALPHA <-> GL_ALPHA\n
+ * CAIRO_CONTENT_COLOR <-> GL_RGB/GL_BGR\n
+ * CAIRO_CONTENT_COLOR_ALPHA <-> GL_RGBA/GL_BGRA\n
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] abstract_device The given cairo_device_t represents the driver interface to underlying rendering system
+ * @param[in] content Type of content in the surface
+ * @param[in] tex Name of texture to use for storage of surface pixels
+ * @param[in] width Width of the surface, in pixels
+ * @param[in] height Height of the surface, in pixels
+ *
+ * @return The created surface or NULL on failure
+ * The error value on failure can be retrieved with cairo_status().
+ */
+
+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);
+
+/**
+ * @brief Returns the width of given cairo surface object
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] abstract_surface The given cairo_surface_t object
+ *
+ * @return the surface width or NULL on failure
+ * The error value on failure can be retrieved with cairo_status().
+ */
+
+cairo_public int
+cairo_gl_surface_get_width (cairo_surface_t *abstract_surface);
+
+/**
+ * @brief Returns the height of given cairo surface object
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] abstract_surface The given cairo_surface_t object
+ *
+ * @return the surface height or NULL on failure
+ * The error value on failure can be retrieved with cairo_status().
+ */
+
+cairo_public int
+cairo_gl_surface_get_height (cairo_surface_t *abstract_surface);
+
+/**
+ * @brief Cairo can be used in multithreaded environment.\n
+ * By default, cairo switches out the current GL context after each draw finishes.\n
+ * This API tells cairo not to switch GL context if no other thread uses cairo for rendering.\n
+ * In carefully designed application, there should be a single, dedicated rendering thread.\n
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] device The given cairo_device structure for the interface to underlying rendering system
+ * @param[in] thread_aware Set this value as FALSE to choose non-thread-aware mode
+ */
+
+cairo_public void
+cairo_gl_device_set_thread_aware (cairo_device_t *device,
+ cairo_bool_t thread_aware);
+
+/**
+ * @brief Creates and returns a new cairo_device structure for interface to underlying rendering system.
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] evas_gl The given Evas_gl object
+ * @param[in] evas_context The given Evas GL Context object
+ *
+ * @return The created cairo_device structure, or an error status on failure.
+ * The error value can be retrieved with cairo_device_status().
+ *
+ * @see evas_gl_new
+ * @see evas_gl_context_create
+ * @see cairo_gl_surface_create_for_evas_gl
+ */
+
+cairo_public cairo_device_t *
+cairo_evas_gl_device_create (Evas_GL *evas_gl,
+ Evas_GL_Context *evas_context);
+
+/**
+ * @brief Creates and returns a new cairo_surface structure for representing Evas_GL_Surface object that cairo can render to.
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] device The given cairo_device structure for the interface to underlying rendering system
+ * @param[in] evas_surface The given Evas_GL_Surface object for GL Rendering
+ * @param[in] evas_config The pixel format and configuration of the rendering surface
+ * @param[in] width The width of the surface
+ * @param[in] height The height of the surface
+ *
+ * @return The created cairo_surface structure, or an error status on failure
+ * The error value can be retrieved with cairo_surface_status().
+ *
+ * @see cairo_evas_gl_device_create
+ * @see evas_gl_surface_create
+ * @see evas_gl_config_new
+ */
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_evas_gl (cairo_device_t *device,
+ Evas_GL_Surface *evas_surface,
+ Evas_GL_Config *evas_config,
+ int width,
+ int height);
+
+/**
+ * @brief Returns the underlying Evas_GL object used to create cairo device object
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] device The given cairo_device_t represents the driver interface to underlying rendering system
+ *
+ * @return The created Evas_GL object or NULL on failure
+ * The error value on failure can be retrieved with cairo_status().
+ */
+
+cairo_public Evas_GL *
+cairo_evas_gl_device_get_gl (cairo_device_t *device);
+
+/**
+ * @brief Returns the underlying Evas_GL_Context object used to create cairo device object.
+ *
+ * @since_tizen 2.3.1
+ *
+ * @param[in] device The given cairo_device_t represents the driver interface to underlying rendering system
+ *
+ * @return The created Evas_GL_Context object or NULL on failure
+ * The error value on failure can be retrieved with cairo_status().
+ */
+
+cairo_public Evas_GL_Context *
+cairo_evas_gl_device_get_context (cairo_device_t *device);
+
+/**
+ * @}
+ */
+
+CAIRO_END_DECLS
+#else /* CAIRO_HAS_EVASGL_SURFACE */
+# error Cairo was not compiled with support for the Evas GL backend
+#endif /* CAIRO_HAS_EVASGL_SURFACE */
+
+#endif /* CAIRO_EVAS_GL_H */
diff --git a/src/cairo-fallback-compositor.c b/src/cairo-fallback-compositor.c
new file mode 100755
index 000000000..3f6199fe2
--- /dev/null
+++ b/src/cairo-fallback-compositor.c
@@ -0,0 +1,185 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-offset-private.h"
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_fallback_compositor_paint (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_image_surface_t *image;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+
+ status = _cairo_surface_offset_paint (&image->base,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->op,
+ &extents->source_pattern.base,
+ extents->clip);
+
+ return _cairo_surface_unmap_image (extents->surface, image);
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_mask (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_image_surface_t *image;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+
+ status = _cairo_surface_offset_mask (&image->base,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->op,
+ &extents->source_pattern.base,
+ &extents->mask_pattern.base,
+ extents->clip);
+
+ return _cairo_surface_unmap_image (extents->surface, image);
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_image_surface_t *image;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+
+ status = _cairo_surface_offset_stroke (&image->base,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->op,
+ &extents->source_pattern.base,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ antialias,
+ extents->clip);
+
+ return _cairo_surface_unmap_image (extents->surface, image);
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_image_surface_t *image;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+
+ status = _cairo_surface_offset_fill (&image->base,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->op,
+ &extents->source_pattern.base,
+ path,
+ fill_rule, tolerance, antialias,
+ extents->clip);
+
+ return _cairo_surface_unmap_image (extents->surface, image);
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_glyphs (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_image_surface_t *image;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+
+ status = _cairo_surface_offset_glyphs (&image->base,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->op,
+ &extents->source_pattern.base,
+ scaled_font, glyphs, num_glyphs,
+ extents->clip);
+
+ return _cairo_surface_unmap_image (extents->surface, image);
+}
+
+const cairo_compositor_t _cairo_fallback_compositor = {
+ &__cairo_no_compositor,
+
+ _cairo_fallback_compositor_paint,
+ _cairo_fallback_compositor_mask,
+ _cairo_fallback_compositor_stroke,
+ _cairo_fallback_compositor_fill,
+ _cairo_fallback_compositor_glyphs,
+};
diff --git a/src/cairo-features-uninstalled.pc.in b/src/cairo-features-uninstalled.pc.in
new file mode 100755
index 000000000..b9cd9d3ad
--- /dev/null
+++ b/src/cairo-features-uninstalled.pc.in
@@ -0,0 +1,7 @@
+Name: @FEATURE_PC@
+Description: @FEATURE_NAME@ for cairo graphics library
+Version: @VERSION@
+
+Requires: @FEATURE_BASE@ @FEATURE_REQUIRES@
+Libs: @FEATURE_NONPKGCONFIG_LIBS@ @FEATURE_NONPKGCONFIG_EXTRA_LIBS@
+Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@/src @FEATURE_NONPKGCONFIG_CFLAGS@
diff --git a/src/cairo-features.pc.in b/src/cairo-features.pc.in
new file mode 100755
index 000000000..9a4b657c8
--- /dev/null
+++ b/src/cairo-features.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @FEATURE_PC@
+Description: @FEATURE_NAME@ for cairo graphics library
+Version: @VERSION@
+
+Requires: @FEATURE_BASE@ @FEATURE_REQUIRES@
+Libs: @FEATURE_NONPKGCONFIG_LIBS@ @FEATURE_NONPKGCONFIG_EXTRA_LIBS@
+Cflags: -I${includedir}/cairo @FEATURE_NONPKGCONFIG_CFLAGS@
diff --git a/src/cairo-filters-private.h b/src/cairo-filters-private.h
new file mode 100755
index 000000000..89b7037d6
--- /dev/null
+++ b/src/cairo-filters-private.h
@@ -0,0 +1,57 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2013 Samsung Research America, Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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):
+ * Henry Song <henry.song@samsung.com>
+ */
+
+#ifndef CAIRO_FILTERS_PRIVATE_H
+#define CAIRO_FILTERS_PRIVATE_H
+
+#include "cairoint.h"
+
+cairo_private void
+compute_x_coef_to_float (double *matrix, int row, int col, float *coef);
+
+cairo_private void
+compute_y_coef_to_float (double *matrix, int row, int col, float *coef);
+
+cairo_private void
+compute_x_coef_to_double (double *matrix, int row, int col, double *coef);
+
+cairo_private void
+compute_y_coef_to_double (double *matrix, int row, int col, double *coef);
+
+#endif /* CAIRO_FILTERS_PRIVATE_H */
diff --git a/src/cairo-filters.c b/src/cairo-filters.c
new file mode 100755
index 000000000..dfacda58b
--- /dev/null
+++ b/src/cairo-filters.c
@@ -0,0 +1,85 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2013 Samsung Research America, Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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):
+ * Henry Song <henry.song@samsung.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-filters-private.h"
+
+void
+compute_x_coef_to_float (double *matrix, int row, int col, float *coef)
+{
+ int i, j;
+
+ for (i = 0; i < col; i++) {
+ for (j = 0; j < row; j++)
+ coef[i] += matrix[i + j * col];
+ }
+}
+
+void
+compute_y_coef_to_float (double *matrix, int row, int col, float *coef)
+{
+ int i, j;
+
+ for (i = 0; i < row; i++) {
+ for (j = 0; j < col; j++)
+ coef[i] += matrix[i * col + j];
+ }
+}
+
+void
+compute_x_coef_to_double (double *matrix, int row, int col, double *coef)
+{
+ int i, j;
+
+ for (i = 0; i < col; i++) {
+ for (j = 0; j < row; j++)
+ coef[i] += matrix[i + j * col];
+ }
+}
+
+void
+compute_y_coef_to_double (double *matrix, int row, int col, double *coef)
+{
+ int i, j;
+
+ for (i = 0; i < row; i++) {
+ for (j = 0; j < col; j++)
+ coef[i] += matrix[i * col + j];
+ }
+}
diff --git a/src/cairo-fixed-private.h b/src/cairo-fixed-private.h
new file mode 100755
index 000000000..b6cc6be7d
--- /dev/null
+++ b/src/cairo-fixed-private.h
@@ -0,0 +1,359 @@
+/* -*- 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_EPSILON ((cairo_fixed_t)(1))
+
+#define CAIRO_FIXED_ERROR_DOUBLE (1. / (2 * CAIRO_FIXED_ONE_DOUBLE))
+
+#define CAIRO_FIXED_FRAC_MASK ((cairo_fixed_t)(((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS)))
+#define CAIRO_FIXED_WHOLE_MASK (~CAIRO_FIXED_FRAC_MASK)
+
+static inline cairo_fixed_t
+_cairo_fixed_from_int (int i)
+{
+ return i << CAIRO_FIXED_FRAC_BITS;
+}
+
+/* This is the "magic number" approach to converting a double into fixed
+ * point as described here:
+ *
+ * http://www.stereopsis.com/sree/fpu2006.html (an overview)
+ * http://www.d6.com/users/checker/pdfs/gdmfp.pdf (in detail)
+ *
+ * The basic idea is to add a large enough number to the double that the
+ * literal floating point is moved up to the extent that it forces the
+ * double's value to be shifted down to the bottom of the mantissa (to make
+ * room for the large number being added in). Since the mantissa is, at a
+ * given moment in time, a fixed point integer itself, one can convert a
+ * float to various fixed point representations by moving around the point
+ * of a floating point number through arithmetic operations. This behavior
+ * is reliable on most modern platforms as it is mandated by the IEEE-754
+ * standard for floating point arithmetic.
+ *
+ * For our purposes, a "magic number" must be carefully selected that is
+ * both large enough to produce the desired point-shifting effect, and also
+ * has no lower bits in its representation that would interfere with our
+ * value at the bottom of the mantissa. The magic number is calculated as
+ * follows:
+ *
+ * (2 ^ (MANTISSA_SIZE - FRACTIONAL_SIZE)) * 1.5
+ *
+ * where in our case:
+ * - MANTISSA_SIZE for 64-bit doubles is 52
+ * - FRACTIONAL_SIZE for 16.16 fixed point is 16
+ *
+ * Although this approach provides a very large speedup of this function
+ * on a wide-array of systems, it does come with two caveats:
+ *
+ * 1) It uses banker's rounding as opposed to arithmetic rounding.
+ * 2) It doesn't function properly if the FPU is in single-precision
+ * mode.
+ */
+
+/* The 16.16 number must always be available */
+#define CAIRO_MAGIC_NUMBER_FIXED_16_16 (103079215104.0)
+
+#if CAIRO_FIXED_BITS <= 32
+#define CAIRO_MAGIC_NUMBER_FIXED ((1LL << (52 - CAIRO_FIXED_FRAC_BITS)) * 1.5)
+
+/* For 32-bit fixed point numbers */
+static inline cairo_fixed_t
+_cairo_fixed_from_double (double d)
+{
+ union {
+ double d;
+ int32_t i[2];
+ } u;
+
+ u.d = d + CAIRO_MAGIC_NUMBER_FIXED;
+#ifdef FLOAT_WORDS_BIGENDIAN
+ return u.i[1];
+#else
+ return u.i[0];
+#endif
+}
+
+#else
+# error Please define a magic number for your fixed point type!
+# error See cairo-fixed-private.h for details.
+#endif
+
+static inline cairo_fixed_t
+_cairo_fixed_from_26_6 (uint32_t i)
+{
+#if CAIRO_FIXED_FRAC_BITS > 6
+ return i << (CAIRO_FIXED_FRAC_BITS - 6);
+#else
+ return i >> (6 - CAIRO_FIXED_FRAC_BITS);
+#endif
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_from_16_16 (uint32_t i)
+{
+#if CAIRO_FIXED_FRAC_BITS > 16
+ return i << (CAIRO_FIXED_FRAC_BITS - 16);
+#else
+ return i >> (16 - CAIRO_FIXED_FRAC_BITS);
+#endif
+}
+
+static inline double
+_cairo_fixed_to_double (cairo_fixed_t f)
+{
+ return ((double) f) / CAIRO_FIXED_ONE_DOUBLE;
+}
+
+static inline int
+_cairo_fixed_is_integer (cairo_fixed_t f)
+{
+ return (f & CAIRO_FIXED_FRAC_MASK) == 0;
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_floor (cairo_fixed_t f)
+{
+ return f & ~CAIRO_FIXED_FRAC_MASK;
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_ceil (cairo_fixed_t f)
+{
+ return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK);
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_round (cairo_fixed_t f)
+{
+ return _cairo_fixed_floor (f + (CAIRO_FIXED_FRAC_MASK+1)/2);
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_round_down (cairo_fixed_t f)
+{
+ return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK/2);
+}
+
+static inline int
+_cairo_fixed_integer_part (cairo_fixed_t f)
+{
+ return f >> CAIRO_FIXED_FRAC_BITS;
+}
+
+static inline int
+_cairo_fixed_integer_round (cairo_fixed_t f)
+{
+ return _cairo_fixed_integer_part (f + (CAIRO_FIXED_FRAC_MASK+1)/2);
+}
+
+static inline int
+_cairo_fixed_integer_round_down (cairo_fixed_t f)
+{
+ return _cairo_fixed_integer_part (f + CAIRO_FIXED_FRAC_MASK/2);
+}
+
+static inline int
+_cairo_fixed_fractional_part (cairo_fixed_t f)
+{
+ return f & CAIRO_FIXED_FRAC_MASK;
+}
+
+static inline int
+_cairo_fixed_integer_floor (cairo_fixed_t f)
+{
+ if (f >= 0)
+ return f >> CAIRO_FIXED_FRAC_BITS;
+ else
+ return -((-f - 1) >> CAIRO_FIXED_FRAC_BITS) - 1;
+}
+
+static inline int
+_cairo_fixed_integer_ceil (cairo_fixed_t f)
+{
+ if (f > 0)
+ return ((f - 1)>>CAIRO_FIXED_FRAC_BITS) + 1;
+ else
+ return - (-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/src/cairo-fixed-type-private.h b/src/cairo-fixed-type-private.h
new file mode 100755
index 000000000..2bbd5f786
--- /dev/null
+++ b/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/src/cairo-fixed.c b/src/cairo-fixed.c
new file mode 100755
index 000000000..03e055923
--- /dev/null
+++ b/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/src/cairo-font-face-twin-data.c b/src/cairo-font-face-twin-data.c
new file mode 100755
index 000000000..ff09cb2be
--- /dev/null
+++ b/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/src/cairo-font-face-twin.c b/src/cairo-font-face-twin.c
new file mode 100755
index 000000000..22f87394a
--- /dev/null
+++ b/src/cairo-font-face-twin.c
@@ -0,0 +1,758 @@
+/*
+ * 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 twin_face_properties_t *
+twin_font_face_create_properties (cairo_font_face_t *twin_face)
+{
+ twin_face_properties_t *props;
+
+ props = malloc (sizeof (twin_face_properties_t));
+ if (unlikely (props == NULL))
+ return NULL;
+
+ props->stretch = TWIN_STRETCH_NORMAL;
+ props->slant = CAIRO_FONT_SLANT_NORMAL;
+ props->weight = TWIN_WEIGHT_NORMAL;
+ props->monospace = FALSE;
+ props->smallcaps = FALSE;
+
+ if (unlikely (cairo_font_face_set_user_data (twin_face,
+ &twin_properties_key,
+ props, free))) {
+ free (props);
+ return NULL;
+ }
+
+ return props;
+}
+
+static cairo_status_t
+twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face,
+ cairo_toy_font_face_t *toy_face)
+{
+ twin_face_properties_t *props;
+
+ props = twin_font_face_create_properties (twin_face);
+ if (unlikely (props == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ props->slant = toy_face->slant;
+ props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ?
+ TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD;
+ face_props_parse (props, toy_face->family);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * Scaled properties
+ */
+
+typedef struct _twin_scaled_properties {
+ twin_face_properties_t *face_props;
+
+ cairo_bool_t snap; /* hint outlines */
+
+ double weight; /* unhinted pen width */
+ double penx, peny; /* hinted pen width */
+ double marginl, marginr; /* hinted side margins */
+
+ double stretch; /* stretch factor */
+} twin_scaled_properties_t;
+
+static void
+compute_hinting_scale (cairo_t *cr,
+ double x, double y,
+ double *scale, double *inv)
+{
+ cairo_user_to_device_distance (cr, &x, &y);
+ *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y);
+ *inv = 1 / *scale;
+}
+
+static void
+compute_hinting_scales (cairo_t *cr,
+ double *x_scale, double *x_scale_inv,
+ double *y_scale, double *y_scale_inv)
+{
+ double x, y;
+
+ x = 1; y = 0;
+ compute_hinting_scale (cr, x, y, x_scale, x_scale_inv);
+
+ x = 0; y = 1;
+ compute_hinting_scale (cr, x, y, y_scale, y_scale_inv);
+}
+
+#define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv)
+#define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv)
+
+/* This controls the global font size */
+#define F(g) ((g) / 72.)
+
+static void
+twin_hint_pen_and_margins(cairo_t *cr,
+ double *penx, double *peny,
+ double *marginl, double *marginr)
+{
+ double x_scale, x_scale_inv;
+ double y_scale, y_scale_inv;
+ double margin;
+
+ compute_hinting_scales (cr,
+ &x_scale, &x_scale_inv,
+ &y_scale, &y_scale_inv);
+
+ *penx = SNAPXI (*penx);
+ if (*penx < x_scale_inv)
+ *penx = x_scale_inv;
+
+ *peny = SNAPYI (*peny);
+ if (*peny < y_scale_inv)
+ *peny = y_scale_inv;
+
+ margin = *marginl + *marginr;
+ *marginl = SNAPXI (*marginl);
+ if (*marginl < x_scale_inv)
+ *marginl = x_scale_inv;
+
+ *marginr = margin - *marginl;
+ if (*marginr < 0)
+ *marginr = 0;
+ *marginr = SNAPXI (*marginr);
+}
+
+static cairo_status_t
+twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font,
+ cairo_t *cr)
+{
+ cairo_status_t status;
+ twin_scaled_properties_t *props;
+
+ props = 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);
+ if (props->face_props == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto FREE_PROPS;
+ }
+
+ 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);
+ if (props == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ /* Save glyph space, we need it when stroking */
+ cairo_save (cr);
+
+ /* center the pen */
+ cairo_translate (cr, props->penx * .5, -props->peny * .5);
+
+ /* small-caps */
+ if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') {
+ glyph += 'A' - 'a';
+ /* 28 and 42 are small and capital letter heights of the glyph data */
+ cairo_scale (cr, 1, 28. / 42);
+ }
+
+ /* slant */
+ if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) {
+ cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0};
+ cairo_transform (cr, &shear);
+ }
+
+ b = _cairo_twin_outlines +
+ _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph];
+ g = twin_glyph_draw(b);
+ w = twin_glyph_right(b);
+ gw = F(w);
+
+ marginl = props->marginl;
+
+ /* monospace */
+ if (props->face_props->monospace) {
+ double monow = F(24);
+ double extra = props->penx + props->marginl + props->marginr;
+ cairo_scale (cr, (monow + extra) / (gw + extra), 1);
+ gw = monow;
+
+ /* resnap margin for new transform */
+ {
+ double x, y, x_scale, x_scale_inv;
+ x = 1; y = 0;
+ compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv);
+ marginl = SNAPXI (marginl);
+ }
+ }
+
+ cairo_translate (cr, marginl, 0);
+
+ /* stretch */
+ cairo_scale (cr, props->stretch, 1);
+
+ if (props->snap)
+ twin_compute_snap (cr, &info, b);
+ else
+ info.n_snap_x = info.n_snap_y = 0;
+
+ /* advance width */
+ metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr;
+
+ /* glyph shape */
+ for (;;) {
+ switch (*g++) {
+ case 'M':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'm':
+ x1 = SNAPX(*g++);
+ y1 = SNAPY(*g++);
+ cairo_move_to (cr, x1, y1);
+ continue;
+ case 'L':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'l':
+ x1 = SNAPX(*g++);
+ y1 = SNAPY(*g++);
+ cairo_line_to (cr, x1, y1);
+ continue;
+ case 'C':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'c':
+ x1 = SNAPX(*g++);
+ y1 = SNAPY(*g++);
+ x2 = SNAPX(*g++);
+ y2 = SNAPY(*g++);
+ x3 = SNAPX(*g++);
+ y3 = SNAPY(*g++);
+ cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+ continue;
+ case 'E':
+ cairo_close_path (cr);
+ /* fall through */
+ case 'e':
+ cairo_restore (cr); /* restore glyph space */
+ cairo_set_tolerance (cr, 0.01);
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width (cr, 1);
+ cairo_scale (cr, props->penx, props->peny);
+ cairo_stroke (cr);
+ break;
+ case 'X':
+ /* filler */
+ continue;
+ }
+ break;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
+ unsigned long unicode,
+ unsigned long *glyph)
+{
+ /* We use an identity charmap. Which means we could live
+ * with no unicode_to_glyph method too. But we define this
+ * to map all unknown chars to a single unknown glyph to
+ * reduce pressure on cache. */
+
+ if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap)))
+ *glyph = unicode;
+ else
+ *glyph = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * Face constructor
+ */
+
+static cairo_font_face_t *
+_cairo_font_face_twin_create_internal (void)
+{
+ cairo_font_face_t *twin_font_face;
+
+ twin_font_face = cairo_user_font_face_create ();
+ cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init);
+ cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph);
+ cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph);
+
+ return twin_font_face;
+}
+
+cairo_font_face_t *
+_cairo_font_face_twin_create_fallback (void)
+{
+ cairo_font_face_t *twin_font_face;
+
+ twin_font_face = _cairo_font_face_twin_create_internal ();
+ if (! twin_font_face_create_properties (twin_font_face)) {
+ cairo_font_face_destroy (twin_font_face);
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+ }
+
+ return twin_font_face;
+}
+
+cairo_status_t
+_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face,
+ cairo_font_face_t **font_face)
+{
+ cairo_status_t status;
+ cairo_font_face_t *twin_font_face;
+
+ twin_font_face = _cairo_font_face_twin_create_internal ();
+ status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face);
+ if (status) {
+ cairo_font_face_destroy (twin_font_face);
+ return status;
+ }
+
+ *font_face = twin_font_face;
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-font-face.c b/src/cairo-font-face.c
new file mode 100755
index 000000000..b93bd8caa
--- /dev/null
+++ b/src/cairo-font-face.c
@@ -0,0 +1,318 @@
+/* -*- 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
+ * <function>cairo_<emphasis>backend</emphasis>_font_face_create(<!-- -->)</function>,
+ * 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.
+ *
+ * Since: 1.0
+ **/
+cairo_font_face_t *
+cairo_font_face_reference (cairo_font_face_t *font_face)
+{
+ if (font_face == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+ return font_face;
+
+ /* We would normally assert that we have a reference here but we
+ * can't get away with that due to the zombie case as documented
+ * in _cairo_ft_font_face_destroy. */
+
+ _cairo_reference_count_inc (&font_face->ref_count);
+
+ return font_face;
+}
+slim_hidden_def (cairo_font_face_reference);
+
+/**
+ * cairo_font_face_destroy:
+ * @font_face: a #cairo_font_face_t
+ *
+ * Decreases the reference count on @font_face by one. If the result
+ * is zero, then @font_face and all associated resources are freed.
+ * See cairo_font_face_reference().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_font_face_destroy (cairo_font_face_t *font_face)
+{
+ if (font_face == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+ return;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count));
+
+ 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.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_font_face_status (cairo_font_face_t *font_face)
+{
+ return font_face->status;
+}
+
+/**
+ * cairo_font_face_get_user_data:
+ * @font_face: a #cairo_font_face_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @font_face using the specified
+ * key. If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.0
+ **/
+void *
+cairo_font_face_get_user_data (cairo_font_face_t *font_face,
+ const cairo_user_data_key_t *key)
+{
+ return _cairo_user_data_array_get_data (&font_face->user_data,
+ key);
+}
+slim_hidden_def (cairo_font_face_get_user_data);
+
+/**
+ * cairo_font_face_set_user_data:
+ * @font_face: a #cairo_font_face_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the font face
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * font face is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @font_face. To remove user data from a font face,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_font_face_set_user_data (cairo_font_face_t *font_face,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy)
+{
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+ return font_face->status;
+
+ return _cairo_user_data_array_set_data (&font_face->user_data,
+ key, user_data, destroy);
+}
+slim_hidden_def (cairo_font_face_set_user_data);
+
+void
+_cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font,
+ const cairo_unscaled_font_backend_t *backend)
+{
+ CAIRO_REFERENCE_COUNT_INIT (&unscaled_font->ref_count, 1);
+ unscaled_font->backend = backend;
+}
+
+cairo_unscaled_font_t *
+_cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font)
+{
+ if (unscaled_font == NULL)
+ return NULL;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count));
+
+ _cairo_reference_count_inc (&unscaled_font->ref_count);
+
+ return unscaled_font;
+}
+
+void
+_cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font)
+{
+ if (unscaled_font == NULL)
+ return;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count))
+ return;
+
+ unscaled_font->backend->destroy (unscaled_font);
+
+ free (unscaled_font);
+}
diff --git a/src/cairo-font-options.c b/src/cairo-font-options.c
new file mode 100755
index 000000000..7b26853ad
--- /dev/null
+++ b/src/cairo-font-options.c
@@ -0,0 +1,560 @@
+/* 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;
+ options->color = CAIRO_FONT_COLOR_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;
+ options->color = other->color;
+}
+
+/**
+ * cairo_font_options_create:
+ *
+ * Allocates a new font options object with all options initialized
+ * to default values.
+ *
+ * Return value: a newly allocated #cairo_font_options_t. Free with
+ * cairo_font_options_destroy(). This function always returns a
+ * valid pointer; if memory cannot be allocated, then a special
+ * error object is returned where all operations on the object do nothing.
+ * You can check for this with cairo_font_options_status().
+ *
+ * Since: 1.0
+ **/
+cairo_font_options_t *
+cairo_font_options_create (void)
+{
+ cairo_font_options_t *options;
+
+ options = malloc (sizeof (cairo_font_options_t));
+ if (!options) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_options_t *) &_cairo_font_options_nil;
+ }
+
+ _cairo_font_options_init_default (options);
+
+ return options;
+}
+
+/**
+ * cairo_font_options_copy:
+ * @original: a #cairo_font_options_t
+ *
+ * Allocates a new font options object copying the option values from
+ * @original.
+ *
+ * Return value: a newly allocated #cairo_font_options_t. Free with
+ * cairo_font_options_destroy(). This function always returns a
+ * valid pointer; if memory cannot be allocated, then a special
+ * error object is returned where all operations on the object do nothing.
+ * You can check for this with cairo_font_options_status().
+ *
+ * Since: 1.0
+ **/
+cairo_font_options_t *
+cairo_font_options_copy (const cairo_font_options_t *original)
+{
+ cairo_font_options_t *options;
+
+ if (cairo_font_options_status ((cairo_font_options_t *) original))
+ return (cairo_font_options_t *) &_cairo_font_options_nil;
+
+ options = 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().
+ *
+ * Since: 1.0
+ **/
+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
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_font_options_status (cairo_font_options_t *options)
+{
+ if (options == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ else if (options == (cairo_font_options_t *) &_cairo_font_options_nil)
+ return CAIRO_STATUS_NO_MEMORY;
+ else
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_font_options_status);
+
+/**
+ * cairo_font_options_merge:
+ * @options: a #cairo_font_options_t
+ * @other: another #cairo_font_options_t
+ *
+ * Merges non-default options from @other into @options, replacing
+ * existing values. This operation can be thought of as somewhat
+ * similar to compositing @other onto @options with the operation
+ * of %CAIRO_OPERATOR_OVER.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_font_options_merge (cairo_font_options_t *options,
+ const cairo_font_options_t *other)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ if (cairo_font_options_status ((cairo_font_options_t *) other))
+ return;
+
+ if (other->antialias != CAIRO_ANTIALIAS_DEFAULT)
+ options->antialias = other->antialias;
+ if (other->subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT)
+ options->subpixel_order = other->subpixel_order;
+ if (other->lcd_filter != CAIRO_LCD_FILTER_DEFAULT)
+ options->lcd_filter = other->lcd_filter;
+ if (other->hint_style != CAIRO_HINT_STYLE_DEFAULT)
+ options->hint_style = other->hint_style;
+ if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT)
+ options->hint_metrics = other->hint_metrics;
+ if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT)
+ options->round_glyph_positions = other->round_glyph_positions;
+ if (other->color != CAIRO_FONT_COLOR_DEFAULT)
+ options->color = other->color;
+}
+slim_hidden_def (cairo_font_options_merge);
+
+/**
+ * cairo_font_options_equal:
+ * @options: a #cairo_font_options_t
+ * @other: another #cairo_font_options_t
+ *
+ * Compares two font options objects for equality.
+ *
+ * Return value: %TRUE if all fields of the two font options objects match.
+ * Note that this function will return %FALSE if either object is in
+ * error.
+ *
+ * Since: 1.0
+ **/
+cairo_bool_t
+cairo_font_options_equal (const cairo_font_options_t *options,
+ const cairo_font_options_t *other)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return FALSE;
+ if (cairo_font_options_status ((cairo_font_options_t *) other))
+ return FALSE;
+
+ if (options == other)
+ return TRUE;
+
+ return (options->antialias == other->antialias &&
+ options->subpixel_order == other->subpixel_order &&
+ options->lcd_filter == other->lcd_filter &&
+ options->hint_style == other->hint_style &&
+ options->hint_metrics == other->hint_metrics &&
+ options->round_glyph_positions == other->round_glyph_positions &&
+ options->color == other->color);
+}
+slim_hidden_def (cairo_font_options_equal);
+
+/**
+ * cairo_font_options_hash:
+ * @options: a #cairo_font_options_t
+ *
+ * Compute a hash for the font options object; this value will
+ * be useful when storing an object containing a #cairo_font_options_t
+ * in a hash table.
+ *
+ * Return value: the hash value for the font options object.
+ * The return value can be cast to a 32-bit type if a
+ * 32-bit hash value is needed.
+ *
+ * Since: 1.0
+ **/
+unsigned long
+cairo_font_options_hash (const cairo_font_options_t *options)
+{
+ 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) |
+ (options->color << 20));
+}
+slim_hidden_def (cairo_font_options_hash);
+
+/**
+ * cairo_font_options_set_antialias:
+ * @options: a #cairo_font_options_t
+ * @antialias: the new antialiasing mode
+ *
+ * Sets the antialiasing mode for the font options object. This
+ * specifies the type of antialiasing to do when rendering text.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_font_options_set_antialias (cairo_font_options_t *options,
+ cairo_antialias_t antialias)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->antialias = antialias;
+}
+slim_hidden_def (cairo_font_options_set_antialias);
+
+/**
+ * cairo_font_options_get_antialias:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the antialiasing mode for the font options object.
+ *
+ * Return value: the antialiasing mode
+ *
+ * Since: 1.0
+ **/
+cairo_antialias_t
+cairo_font_options_get_antialias (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_ANTIALIAS_DEFAULT;
+
+ return options->antialias;
+}
+
+/**
+ * cairo_font_options_set_subpixel_order:
+ * @options: a #cairo_font_options_t
+ * @subpixel_order: the new subpixel order
+ *
+ * Sets the subpixel order for the font options object. The subpixel
+ * order specifies the order of color elements within each pixel on
+ * the display device when rendering with an antialiasing mode of
+ * %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for
+ * #cairo_subpixel_order_t for full details.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_font_options_set_subpixel_order (cairo_font_options_t *options,
+ cairo_subpixel_order_t subpixel_order)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->subpixel_order = subpixel_order;
+}
+slim_hidden_def (cairo_font_options_set_subpixel_order);
+
+/**
+ * cairo_font_options_get_subpixel_order:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the subpixel order for the font options object.
+ * See the documentation for #cairo_subpixel_order_t for full details.
+ *
+ * Return value: the subpixel order for the font options object
+ *
+ * Since: 1.0
+ **/
+cairo_subpixel_order_t
+cairo_font_options_get_subpixel_order (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_SUBPIXEL_ORDER_DEFAULT;
+
+ return options->subpixel_order;
+}
+
+/**
+ * _cairo_font_options_set_lcd_filter:
+ * @options: a #cairo_font_options_t
+ * @lcd_filter: the new LCD filter
+ *
+ * Sets the LCD filter for the font options object. The LCD filter
+ * specifies how pixels are filtered when rendered with an antialiasing
+ * mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for
+ * #cairo_lcd_filter_t for full details.
+ **/
+void
+_cairo_font_options_set_lcd_filter (cairo_font_options_t *options,
+ cairo_lcd_filter_t lcd_filter)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->lcd_filter = lcd_filter;
+}
+
+/**
+ * _cairo_font_options_get_lcd_filter:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the LCD filter for the font options object.
+ * See the documentation for #cairo_lcd_filter_t for full details.
+ *
+ * Return value: the LCD filter for the font options object
+ **/
+cairo_lcd_filter_t
+_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_LCD_FILTER_DEFAULT;
+
+ return options->lcd_filter;
+}
+
+/**
+ * _cairo_font_options_set_round_glyph_positions:
+ * @options: a #cairo_font_options_t
+ * @round: the new rounding value
+ *
+ * Sets the rounding options for the font options object. If rounding is set, a
+ * glyph's position will be rounded to integer values.
+ **/
+void
+_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options,
+ cairo_round_glyph_positions_t round)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->round_glyph_positions = round;
+}
+
+/**
+ * _cairo_font_options_get_round_glyph_positions:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the glyph position rounding option for the font options object.
+ *
+ * Return value: The round glyph posistions flag for the font options object.
+ **/
+cairo_round_glyph_positions_t
+_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_ROUND_GLYPH_POS_DEFAULT;
+
+ return options->round_glyph_positions;
+}
+
+/**
+ * cairo_font_options_set_hint_style:
+ * @options: a #cairo_font_options_t
+ * @hint_style: the new hint style
+ *
+ * Sets the hint style for font outlines for the font options object.
+ * This controls whether to fit font outlines to the pixel grid,
+ * and if so, whether to optimize for fidelity or contrast.
+ * See the documentation for #cairo_hint_style_t for full details.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_font_options_set_hint_style (cairo_font_options_t *options,
+ cairo_hint_style_t hint_style)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->hint_style = hint_style;
+}
+slim_hidden_def (cairo_font_options_set_hint_style);
+
+/**
+ * cairo_font_options_get_hint_style:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the hint style for font outlines for the font options object.
+ * See the documentation for #cairo_hint_style_t for full details.
+ *
+ * Return value: the hint style for the font options object
+ *
+ * Since: 1.0
+ **/
+cairo_hint_style_t
+cairo_font_options_get_hint_style (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_HINT_STYLE_DEFAULT;
+
+ return options->hint_style;
+}
+
+/**
+ * cairo_font_options_set_hint_metrics:
+ * @options: a #cairo_font_options_t
+ * @hint_metrics: the new metrics hinting mode
+ *
+ * Sets the metrics hinting mode for the font options object. This
+ * controls whether metrics are quantized to integer values in
+ * device units.
+ * See the documentation for #cairo_hint_metrics_t for full details.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_font_options_set_hint_metrics (cairo_font_options_t *options,
+ cairo_hint_metrics_t hint_metrics)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->hint_metrics = hint_metrics;
+}
+slim_hidden_def (cairo_font_options_set_hint_metrics);
+
+/**
+ * cairo_font_options_get_hint_metrics:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the metrics hinting mode for the font options object.
+ * See the documentation for #cairo_hint_metrics_t for full details.
+ *
+ * Return value: the metrics hinting mode for the font options object
+ *
+ * Since: 1.0
+ **/
+cairo_hint_metrics_t
+cairo_font_options_get_hint_metrics (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_HINT_METRICS_DEFAULT;
+
+ return options->hint_metrics;
+}
+void
+cairo_font_options_set_font_color (cairo_font_options_t *options,
+ cairo_font_color_t font_color)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->color = font_color;
+}
+slim_hidden_def (cairo_font_options_set_font_color);
+
+cairo_font_color_t
+cairo_font_options_get_font_color (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_FONT_COLOR_DEFAULT;
+
+ return options->color;
+}
diff --git a/src/cairo-fontconfig-private.h b/src/cairo-fontconfig-private.h
new file mode 100755
index 000000000..ea873abe7
--- /dev/null
+++ b/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/src/cairo-freed-pool-private.h b/src/cairo-freed-pool-private.h
new file mode 100755
index 000000000..0ec6de3d1
--- /dev/null
+++ b/src/cairo-freed-pool-private.h
@@ -0,0 +1,139 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_FREED_POOL_H
+#define CAIRO_FREED_POOL_H
+
+#include "cairoint.h"
+#include "cairo-atomic-private.h"
+
+CAIRO_BEGIN_DECLS
+
+#define DISABLE_FREED_POOLS 0
+
+#if HAS_ATOMIC_OPS && ! DISABLE_FREED_POOLS
+/* Keep a stash of recently freed clip_paths, since we need to
+ * reallocate them frequently.
+ */
+#define MAX_FREED_POOL_SIZE 16
+typedef struct {
+ void *pool[MAX_FREED_POOL_SIZE];
+ int 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 = pool->top - 1;
+ if (i < 0)
+ i = 0;
+
+ ptr = _atomic_fetch (&pool->pool[i]);
+ if (likely (ptr != NULL)) {
+ 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 = pool->top;
+ if (likely (i < ARRAY_LENGTH (pool->pool) &&
+ _atomic_store (&pool->pool[i], ptr)))
+ {
+ pool->top = i + 1;
+ return;
+ }
+
+ /* either full or contended */
+ _freed_pool_put_search (pool, ptr);
+}
+
+cairo_private void
+_freed_pool_reset (freed_pool_t *pool);
+
+#define HAS_FREED_POOL 1
+
+#else
+
+/* A warning about an unused freed-pool in a build without atomics
+ * enabled usually indicates a missing _freed_pool_reset() in the
+ * static reset function */
+
+typedef int freed_pool_t;
+
+#define _freed_pool_get(pool) NULL
+#define _freed_pool_put(pool, ptr) free(ptr)
+#define _freed_pool_reset(ptr)
+
+#endif
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_FREED_POOL_PRIVATE_H */
diff --git a/src/cairo-freed-pool.c b/src/cairo-freed-pool.c
new file mode 100755
index 000000000..cfdc8e96b
--- /dev/null
+++ b/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) {
+ pool->top = i;
+ return ptr;
+ }
+ }
+
+ /* empty */
+ 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)) {
+ pool->top = i + 1;
+ return;
+ }
+ }
+
+ /* full */
+ 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;
+ }
+
+ pool->top = 0;
+}
+
+#endif
diff --git a/src/cairo-freelist-private.h b/src/cairo-freelist-private.h
new file mode 100755
index 000000000..8fe6516f3
--- /dev/null
+++ b/src/cairo-freelist-private.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2006 Joonas Pihlaja
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+#ifndef CAIRO_FREELIST_H
+#define CAIRO_FREELIST_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-freelist-type-private.h"
+
+/* for stand-alone compilation*/
+#ifndef VG
+#define VG(x)
+#endif
+
+#ifndef NULL
+#define NULL (void *) 0
+#endif
+
+/* Initialise a freelist that will be responsible for allocating
+ * nodes of size nodesize. */
+cairo_private void
+_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize);
+
+/* Deallocate any nodes in the freelist. */
+cairo_private void
+_cairo_freelist_fini (cairo_freelist_t *freelist);
+
+/* Allocate a new node from the freelist. If the freelist contains no
+ * nodes, a new one will be allocated using malloc(). The caller is
+ * responsible for calling _cairo_freelist_free() or free() on the
+ * returned node. Returns %NULL on memory allocation error. */
+cairo_private void *
+_cairo_freelist_alloc (cairo_freelist_t *freelist);
+
+/* Allocate a new node from the freelist. If the freelist contains no
+ * nodes, a new one will be allocated using calloc(). The caller is
+ * responsible for calling _cairo_freelist_free() or free() on the
+ * returned node. Returns %NULL on memory allocation error. */
+cairo_private void *
+_cairo_freelist_calloc (cairo_freelist_t *freelist);
+
+/* Return a node to the freelist. This does not deallocate the memory,
+ * but makes it available for later reuse by
+ * _cairo_freelist_alloc(). */
+cairo_private void
+_cairo_freelist_free (cairo_freelist_t *freelist, void *node);
+
+
+cairo_private void
+_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize);
+
+cairo_private void
+_cairo_freepool_fini (cairo_freepool_t *freepool);
+
+static inline void
+_cairo_freepool_reset (cairo_freepool_t *freepool)
+{
+ while (freepool->pools != &freepool->embedded_pool) {
+ cairo_freelist_pool_t *pool = freepool->pools;
+ freepool->pools = pool->next;
+ pool->next = freepool->freepools;
+ freepool->freepools = pool;
+ }
+
+ freepool->embedded_pool.rem = sizeof (freepool->embedded_data);
+ freepool->embedded_pool.data = freepool->embedded_data;
+}
+
+cairo_private void *
+_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool);
+
+static inline void *
+_cairo_freepool_alloc_from_pool (cairo_freepool_t *freepool)
+{
+ cairo_freelist_pool_t *pool;
+ uint8_t *ptr;
+
+ pool = freepool->pools;
+ if (unlikely (freepool->nodesize > pool->rem))
+ return _cairo_freepool_alloc_from_new_pool (freepool);
+
+ ptr = pool->data;
+ pool->data += freepool->nodesize;
+ pool->rem -= freepool->nodesize;
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (ptr, freepool->nodesize));
+ return ptr;
+}
+
+static inline void *
+_cairo_freepool_alloc (cairo_freepool_t *freepool)
+{
+ cairo_freelist_node_t *node;
+
+ node = freepool->first_free_node;
+ if (node == NULL)
+ return _cairo_freepool_alloc_from_pool (freepool);
+
+ VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
+ freepool->first_free_node = node->next;
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize));
+
+ return node;
+}
+
+cairo_private cairo_status_t
+_cairo_freepool_alloc_array (cairo_freepool_t *freepool,
+ int count,
+ void **array);
+
+static inline void
+_cairo_freepool_free (cairo_freepool_t *freepool, void *ptr)
+{
+ cairo_freelist_node_t *node = ptr;
+
+ node->next = freepool->first_free_node;
+ freepool->first_free_node = node;
+ VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize));
+}
+
+#endif /* CAIRO_FREELIST_H */
diff --git a/src/cairo-freelist-type-private.h b/src/cairo-freelist-type-private.h
new file mode 100755
index 000000000..4dd056461
--- /dev/null
+++ b/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/src/cairo-freelist.c b/src/cairo-freelist.c
new file mode 100755
index 000000000..d596eab81
--- /dev/null
+++ b/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/src/cairo-ft-font.c b/src/cairo-ft-font.c
new file mode 100755
index 000000000..55ee02c1a
--- /dev/null
+++ b/src/cairo-ft-font.c
@@ -0,0 +1,3558 @@
+/* -*- 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-image-surface-private.h"
+#include "cairo-ft-private.h"
+#include "cairo-pattern-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_TRUETYPE_TABLES_H
+#include FT_XFREE86_H
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+#include FT_SYNTHESIS_H
+#define USE_FT_OUTLINE_EMBOLDEN 1
+#endif
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+#include FT_LCD_FILTER_H
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#define access(p, m) 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
+
+#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
+
+/**
+ * SECTION:cairo-ft
+ * @Title: FreeType Fonts
+ * @Short_Description: Font support for FreeType
+ * @See_Also: #cairo_font_face_t
+ *
+ * The FreeType font backend is primarily used to render text on GNU/Linux
+ * systems, but can be used on other platforms too.
+ **/
+
+/**
+ * CAIRO_HAS_FT_FONT:
+ *
+ * Defined if the FreeType font backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * CAIRO_HAS_FC_FONT:
+ *
+ * Defined if the Fontconfig-specific functions of the FreeType font backend
+ * are available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.10
+ **/
+
+/*
+ * The simple 2x2 matrix is converted into separate scale and shape
+ * factors so that hinting works right
+ */
+
+typedef struct _cairo_ft_font_transform {
+ double x_scale, y_scale;
+ double shape[2][2];
+} cairo_ft_font_transform_t;
+
+/*
+ * We create an object that corresponds to a single font on the disk;
+ * (identified by a filename/id pair) these are shared between all
+ * fonts using that file. For cairo_ft_font_face_create_for_ft_face(), we
+ * just create a one-off version with a permanent face value.
+ */
+
+typedef struct _cairo_ft_font_face cairo_ft_font_face_t;
+
+struct _cairo_ft_unscaled_font {
+ cairo_unscaled_font_t base;
+
+ cairo_bool_t from_face; /* was the FT_Face provided by user? */
+ FT_Face face; /* provided or cached face */
+
+ /* 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 struct _cairo_ft_options {
+ cairo_font_options_t base;
+ unsigned int load_flags; /* flags for FT_Load_Glyph */
+ unsigned int synth_flags;
+} cairo_ft_options_t;
+
+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 FT_Face
+_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled);
+
+static void
+_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled);
+
+static cairo_bool_t
+_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font);
+
+
+static void
+_font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map,
+ cairo_ft_unscaled_font_t *unscaled)
+{
+ if (unscaled->face) {
+ FT_Done_Face (unscaled->face);
+ unscaled->face = NULL;
+ unscaled->have_scale = FALSE;
+
+ font_map->num_open_faces--;
+ }
+}
+
+static cairo_status_t
+_cairo_ft_unscaled_font_map_create (void)
+{
+ cairo_ft_unscaled_font_map_t *font_map;
+
+ /* This function is only intended to be called from
+ * _cairo_ft_unscaled_font_map_lock. So we'll crash if we can
+ * detect some other call path. */
+ assert (cairo_ft_unscaled_font_map == NULL);
+
+ font_map = 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);
+
+ 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 (access (filename, R_OK) == 0) {
+ /* If FC_INDEX is not set, we just use 0 */
+ ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id);
+ if (ret == FcResultOutOfMemory)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ goto DONE;
+ } else
+ return _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND);
+ }
+
+ /* The pattern contains neither a face nor a filename, resolve it later. */
+ *out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+
+DONE:
+ return _cairo_ft_unscaled_font_create_internal (font_face != NULL,
+ filename, id, font_face,
+ 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.
+ */
+static cairo_warn FT_Face
+_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled)
+{
+ cairo_ft_unscaled_font_map_t *font_map;
+ FT_Face face = NULL;
+
+ 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
+ */
+static 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;
+ 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].y_ppem / 64.;
+ double y_size = unscaled->face->available_sizes[i].y_ppem / 64.;
+ double distance = fabs (y_size - y_scale);
+
+ if (distance <= min_distance) {
+ min_distance = 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;
+
+ memset (sf.shape, 0, sizeof (sf.shape[0][0]) * 2 * 2);
+
+ assert (unscaled->face != NULL);
+
+ if (unscaled->have_scale &&
+ scale->xx == unscaled->current_scale.xx &&
+ scale->yx == unscaled->current_scale.yx &&
+ scale->xy == unscaled->current_scale.xy &&
+ scale->yy == unscaled->current_scale.yy)
+ return CAIRO_STATUS_SUCCESS;
+
+ unscaled->have_scale = TRUE;
+ unscaled->current_scale = *scale;
+
+ status = _compute_transform (&sf, scale, unscaled);
+ if (unlikely (status))
+ return status;
+
+ unscaled->x_scale = sf.x_scale;
+ unscaled->y_scale = sf.y_scale;
+
+ mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]);
+ mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]);
+ mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]);
+ mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]);
+
+ unscaled->have_shape = (mat.xx != 0x10000 ||
+ mat.yx != 0x00000 ||
+ mat.xy != 0x00000 ||
+ mat.yy != 0x10000);
+
+ unscaled->Current_Shape = mat;
+ cairo_matrix_init (&unscaled->current_shape,
+ sf.shape[0][0], sf.shape[0][1],
+ sf.shape[1][0], sf.shape[1][1],
+ 0.0, 0.0);
+
+ FT_Set_Transform(unscaled->face, &mat, NULL);
+
+ error = FT_Set_Char_Size (unscaled->face,
+ sf.x_scale * 64.0 + .5,
+ sf.y_scale * 64.0 + .5,
+ 0, 0);
+ if (error)
+ return _cairo_error (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,
+ 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 = TRUE;
+
+ width = bitmap->width;
+ height = bitmap->rows;
+
+ if (width == 0 || height == 0) {
+ *surface = (cairo_image_surface_t *)
+ cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
+ return (*surface)->base.status;
+ }
+
+ switch (bitmap->pixel_mode) {
+ case FT_PIXEL_MODE_MONO:
+ stride = (((width + 31) & ~31) >> 3);
+ if (own_buffer) {
+ data = bitmap->buffer;
+ assert (stride == bitmap->pitch);
+ } else {
+ data = _cairo_malloc_ab (height, stride);
+ if (!data)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (stride == bitmap->pitch) {
+ memcpy (data, bitmap->buffer, stride * height);
+ } else {
+ int i;
+ unsigned char *source, *dest;
+
+ source = bitmap->buffer;
+ dest = data;
+ for (i = height; i; i--) {
+ memcpy (dest, source, bitmap->pitch);
+ memset (dest + bitmap->pitch, '\0', stride - bitmap->pitch);
+
+ source += bitmap->pitch;
+ dest += stride;
+ }
+ }
+ }
+
+#ifndef WORDS_BIGENDIAN
+ {
+ uint8_t *d = data;
+ int count = stride * height;
+
+ while (count--) {
+ *d = CAIRO_BITSWAP8 (*d);
+ d++;
+ }
+ }
+#endif
+ format = CAIRO_FORMAT_A1;
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ case FT_PIXEL_MODE_LCD_V:
+ case FT_PIXEL_MODE_GRAY:
+ if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) {
+ stride = bitmap->pitch;
+ 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 {
+ /* color glyph is rendered as bitmap, does not come from
+ * _fill_xrender_bitmap */
+ if (! own_buffer) {
+ stride = bitmap->pitch;
+ 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 {
+ /* if we get there, the data from the source bitmap
+ * really comes from _fill_xrender_bitmap, and is
+ * made of 32-bit ARGB or ABGR values */
+ assert (own_buffer != 0);
+ assert (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY);
+
+ data = bitmap->buffer;
+ stride = bitmap->pitch;
+ format = CAIRO_FORMAT_ARGB32;
+ }
+ }
+ break;
+ // color font
+ case FT_PIXEL_MODE_BGRA:
+ stride = bitmap->pitch;
+ 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;
+ component_alpha = FALSE;
+ break;
+ case FT_PIXEL_MODE_GRAY2:
+ case FT_PIXEL_MODE_GRAY4:
+ /* 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 (format == CAIRO_FORMAT_ARGB32 && 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:
+ case CAIRO_ANTIALIAS_BEST:
+ switch (font_options->subpixel_order) {
+ case CAIRO_SUBPIXEL_ORDER_DEFAULT:
+ case CAIRO_SUBPIXEL_ORDER_RGB:
+ case CAIRO_SUBPIXEL_ORDER_BGR:
+ render_mode = FT_RENDER_MODE_LCD;
+ break;
+
+ case CAIRO_SUBPIXEL_ORDER_VRGB:
+ case CAIRO_SUBPIXEL_ORDER_VBGR:
+ render_mode = FT_RENDER_MODE_LCD_V;
+ break;
+ }
+
+ switch (font_options->lcd_filter) {
+ case CAIRO_LCD_FILTER_NONE:
+ lcd_filter = FT_LCD_FILTER_NONE;
+ break;
+ case CAIRO_LCD_FILTER_DEFAULT:
+ case CAIRO_LCD_FILTER_INTRA_PIXEL:
+ lcd_filter = FT_LCD_FILTER_LEGACY;
+ break;
+ case CAIRO_LCD_FILTER_FIR3:
+ lcd_filter = FT_LCD_FILTER_LIGHT;
+ break;
+ case CAIRO_LCD_FILTER_FIR5:
+ lcd_filter = FT_LCD_FILTER_DEFAULT;
+ break;
+ }
+
+ break;
+
+ case CAIRO_ANTIALIAS_DEFAULT:
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_GOOD:
+ case CAIRO_ANTIALIAS_FAST:
+ render_mode = FT_RENDER_MODE_NORMAL;
+ }
+
+ FT_Outline_Get_CBox (outline, &cbox);
+
+ cbox.xMin &= -64;
+ cbox.yMin &= -64;
+ cbox.xMax = (cbox.xMax + 63) & -64;
+ cbox.yMax = (cbox.yMax + 63) & -64;
+
+ width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6);
+ height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6);
+
+ if (width * height == 0) {
+ cairo_format_t format;
+ /* Looks like fb handles zero-sized images just fine */
+ switch (render_mode) {
+ case FT_RENDER_MODE_MONO:
+ format = CAIRO_FORMAT_A1;
+ break;
+ case FT_RENDER_MODE_LCD:
+ case FT_RENDER_MODE_LCD_V:
+ format= CAIRO_FORMAT_ARGB32;
+ break;
+ case FT_RENDER_MODE_LIGHT:
+ case FT_RENDER_MODE_NORMAL:
+ case FT_RENDER_MODE_MAX:
+ default:
+ format = CAIRO_FORMAT_A8;
+ break;
+ }
+
+ (*surface) = (cairo_image_surface_t *)
+ cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
+ if ((*surface)->base.status)
+ return (*surface)->base.status;
+ } else {
+
+ int bitmap_size;
+
+ switch (render_mode) {
+ case FT_RENDER_MODE_LCD:
+ if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR)
+ rgba = FC_RGBA_BGR;
+ else
+ rgba = FC_RGBA_RGB;
+ break;
+
+ case FT_RENDER_MODE_LCD_V:
+ if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR)
+ rgba = FC_RGBA_VBGR;
+ else
+ rgba = FC_RGBA_VRGB;
+ break;
+
+ case FT_RENDER_MODE_MONO:
+ case FT_RENDER_MODE_LIGHT:
+ case FT_RENDER_MODE_NORMAL:
+ case FT_RENDER_MODE_MAX:
+ default:
+ break;
+ }
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+ FT_Library_SetLcdFilter (library, lcd_filter);
+#endif
+
+ fterror = FT_Render_Glyph (face->glyph, render_mode);
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+ FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE);
+#endif
+
+ 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, 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,
+ 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;
+
+ image = cairo_image_surface_create ((*surface)->format, width, height);
+ if (unlikely (image->status)) {
+ status = image->status;
+ cairo_surface_destroy (image);
+ return 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.synth_flags = 0;
+
+#ifndef FC_EMBEDDED_BITMAP
+#define FC_EMBEDDED_BITMAP "embeddedbitmap"
+#endif
+
+ /* Check whether to force use of embedded bitmaps */
+ if (FcPatternGetBool (pattern,
+ FC_EMBEDDED_BITMAP, 0, &bitmap) != FcResultMatch)
+ bitmap = FcFalse;
+
+ /* disable antialiasing if requested */
+ if (FcPatternGetBool (pattern,
+ FC_ANTIALIAS, 0, &antialias) != FcResultMatch)
+ antialias = FcTrue;
+
+ if (antialias) {
+ cairo_subpixel_order_t subpixel_order;
+ int lcd_filter;
+
+ /* disable hinting if requested */
+ if (FcPatternGetBool (pattern,
+ FC_HINTING, 0, &hinting) != FcResultMatch)
+ hinting = FcTrue;
+
+ if (FcPatternGetInteger (pattern,
+ FC_RGBA, 0, &rgba) != FcResultMatch)
+ rgba = FC_RGBA_UNKNOWN;
+
+ switch (rgba) {
+ case FC_RGBA_RGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
+ break;
+ case FC_RGBA_BGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
+ break;
+ case FC_RGBA_VRGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
+ break;
+ case FC_RGBA_VBGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
+ break;
+ case FC_RGBA_UNKNOWN:
+ case FC_RGBA_NONE:
+ default:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+ break;
+ }
+
+ if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) {
+ ft_options.base.subpixel_order = subpixel_order;
+ ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+ }
+
+ if (FcPatternGetInteger (pattern,
+ FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch)
+ {
+ switch (lcd_filter) {
+ case FC_LCD_NONE:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE;
+ break;
+ case FC_LCD_DEFAULT:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5;
+ break;
+ case FC_LCD_LIGHT:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3;
+ break;
+ case FC_LCD_LEGACY:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
+ break;
+ }
+ }
+
+#ifdef FC_HINT_STYLE
+ if (FcPatternGetInteger (pattern,
+ FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch)
+ hintstyle = FC_HINT_FULL;
+
+ if (!hinting)
+ hintstyle = FC_HINT_NONE;
+
+ switch (hintstyle) {
+ case FC_HINT_NONE:
+ ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE;
+ break;
+ case FC_HINT_SLIGHT:
+ ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT;
+ break;
+ case FC_HINT_MEDIUM:
+ default:
+ ft_options.base.hint_style = CAIRO_HINT_STYLE_MEDIUM;
+ break;
+ case FC_HINT_FULL:
+ ft_options.base.hint_style = CAIRO_HINT_STYLE_FULL;
+ break;
+ }
+#else /* !FC_HINT_STYLE */
+ if (!hinting) {
+ ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE;
+ }
+#endif /* FC_HINT_STYLE */
+
+ /* Force embedded bitmaps off if no hinting requested */
+ if (ft_options.base.hint_style == CAIRO_HINT_STYLE_NONE)
+ bitmap = FcFalse;
+
+ if (!bitmap)
+ ft_options.load_flags |= FT_LOAD_NO_BITMAP;
+
+ } else {
+ ft_options.base.antialias = CAIRO_ANTIALIAS_NONE;
+ }
+
+ /* force autohinting if requested */
+ if (FcPatternGetBool (pattern,
+ FC_AUTOHINT, 0, &autohint) != FcResultMatch)
+ autohint = FcFalse;
+
+ if (autohint)
+ ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT;
+
+ if (FcPatternGetBool (pattern,
+ FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch)
+ vertical_layout = FcFalse;
+
+ if (vertical_layout)
+ ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT;
+
+#ifndef FC_EMBOLDEN
+#define FC_EMBOLDEN "embolden"
+#endif
+ if (FcPatternGetBool (pattern,
+ FC_EMBOLDEN, 0, &embolden) != FcResultMatch)
+ embolden = FcFalse;
+
+ if (embolden)
+ ft_options.synth_flags |= CAIRO_FT_SYNTHESIZE_BOLD;
+
+ *ret = ft_options;
+}
+#endif
+
+static void
+_cairo_ft_options_merge (cairo_ft_options_t *options,
+ cairo_ft_options_t *other)
+{
+ int load_flags = other->load_flags;
+ int load_target = FT_LOAD_TARGET_NORMAL;
+
+ /* clear load target mode */
+ load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags)));
+
+ if (load_flags & FT_LOAD_NO_HINTING)
+ other->base.hint_style = CAIRO_HINT_STYLE_NONE;
+
+ if (other->base.antialias == CAIRO_ANTIALIAS_NONE ||
+ options->base.antialias == CAIRO_ANTIALIAS_NONE) {
+ options->base.antialias = CAIRO_ANTIALIAS_NONE;
+ options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+ }
+
+ if (other->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL &&
+ (options->base.antialias == CAIRO_ANTIALIAS_DEFAULT ||
+ options->base.antialias == CAIRO_ANTIALIAS_GRAY)) {
+ options->base.antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+ options->base.subpixel_order = other->base.subpixel_order;
+ }
+
+ if (options->base.hint_style == CAIRO_HINT_STYLE_DEFAULT)
+ options->base.hint_style = other->base.hint_style;
+
+ if (other->base.hint_style == CAIRO_HINT_STYLE_NONE)
+ options->base.hint_style = CAIRO_HINT_STYLE_NONE;
+
+ if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT)
+ options->base.lcd_filter = other->base.lcd_filter;
+
+ if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE)
+ options->base.lcd_filter = CAIRO_LCD_FILTER_NONE;
+
+ if (options->base.antialias == CAIRO_ANTIALIAS_NONE) {
+ if (options->base.hint_style == CAIRO_HINT_STYLE_NONE)
+ load_flags |= FT_LOAD_NO_HINTING;
+ else
+ load_target = FT_LOAD_TARGET_MONO;
+ load_flags |= FT_LOAD_MONOCHROME;
+ } else {
+ switch (options->base.hint_style) {
+ case CAIRO_HINT_STYLE_NONE:
+ load_flags |= FT_LOAD_NO_HINTING;
+ break;
+ case CAIRO_HINT_STYLE_SLIGHT:
+ load_target = FT_LOAD_TARGET_LIGHT;
+ break;
+ case CAIRO_HINT_STYLE_MEDIUM:
+ break;
+ case CAIRO_HINT_STYLE_FULL:
+ case CAIRO_HINT_STYLE_DEFAULT:
+ if (options->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
+ switch (options->base.subpixel_order) {
+ case CAIRO_SUBPIXEL_ORDER_DEFAULT:
+ case CAIRO_SUBPIXEL_ORDER_RGB:
+ case CAIRO_SUBPIXEL_ORDER_BGR:
+ load_target = FT_LOAD_TARGET_LCD;
+ break;
+ case CAIRO_SUBPIXEL_ORDER_VRGB:
+ case CAIRO_SUBPIXEL_ORDER_VBGR:
+ load_target = FT_LOAD_TARGET_LCD_V;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ options->load_flags = load_flags | load_target;
+ options->synth_flags = other->synth_flags;
+}
+
+static cairo_status_t
+_cairo_ft_font_face_scaled_font_create (void *abstract_font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **font_out)
+{
+ cairo_ft_font_face_t *font_face = abstract_font_face;
+ cairo_ft_scaled_font_t *scaled_font;
+ FT_Face face;
+ FT_Size_Metrics *metrics;
+ cairo_font_extents_t fs_metrics;
+ cairo_status_t status;
+ cairo_ft_unscaled_font_t *unscaled;
+
+ assert (font_face->unscaled);
+
+ face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled);
+ if (unlikely (face == NULL)) /* backend error */
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ scaled_font = 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 */
+ load_flags |= FT_LOAD_COLOR;
+ #endif
+
+ error = FT_Load_Glyph (face,
+ _cairo_scaled_glyph_index(scaled_glyph),
+ load_flags);
+ /* XXX ignoring all other errors for now. They are not fatal, typically
+ * just a glyph-not-found. */
+ if (error == FT_Err_Out_Of_Memory) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+
+ glyph = face->glyph;
+
+ /*
+ * synthesize glyphs if requested
+ */
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+ if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD)
+#if USE_FT_OUTLINE_EMBOLDEN
+ {
+ /*UX team request us to use 64 instead 34*/
+ FT_Pos xstr;
+ if(face && face->size)
+ xstr = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale) / 64;
+ else
+ goto FAIL;
+ if(face->glyph)
+ FT_Outline_Embolden(&face->glyph->outline, xstr);
+ else
+ goto FAIL;
+ }
+#else
+ FT_GlyphSlot_Embolden (glyph);
+#endif // USE_FT_OUTLINE_EMBOLDEN
+#endif
+
+#if HAVE_FT_GLYPHSLOT_OBLIQUE
+ if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE)
+ FT_GlyphSlot_Oblique (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);
+ 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
+ if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD)
+#if USE_FT_OUTLINE_EMBOLDEN
+ {
+ /*UX team request us to use 64 instead 34*/
+ FT_Pos xstr;
+ if(face && face->size)
+ xstr = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale) / 64;
+ else
+ goto FAIL;
+ if(face->glyph)
+ FT_Outline_Embolden(&face->glyph->outline, xstr);
+ else
+ goto FAIL;
+ }
+#else
+ FT_GlyphSlot_Embolden (glyph);
+#endif // USE_FT_OUTLINE_EMBOLDEN
+#endif
+#if HAVE_FT_GLYPHSLOT_OBLIQUE
+ if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE)
+ FT_GlyphSlot_Oblique (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;
+
+ /* We don't support the FreeType feature of loading a table
+ * without specifying the size since this may overflow our
+ * buffer. */
+ assert (length != NULL);
+
+ if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+#if HAVE_FT_LOAD_SFNT_TABLE
+ face = _cairo_ft_unscaled_font_lock_face (unscaled);
+ if (!face)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (FT_IS_SFNT (face)) {
+ if (buffer == NULL)
+ *length = 0;
+
+ if (FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0)
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_ft_unscaled_font_unlock_face (unscaled);
+#endif
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_ft_index_to_ucs4(void *abstract_font,
+ unsigned long index,
+ uint32_t *ucs4)
+{
+ cairo_ft_scaled_font_t *scaled_font = abstract_font;
+ cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+ FT_Face face;
+ FT_ULong charcode;
+ FT_UInt gindex;
+
+ face = _cairo_ft_unscaled_font_lock_face (unscaled);
+ if (!face)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *ucs4 = (uint32_t) -1;
+ charcode = FT_Get_First_Char(face, &gindex);
+ while (gindex != 0) {
+ if (gindex == index) {
+ *ucs4 = charcode;
+ break;
+ }
+ charcode = FT_Get_Next_Char (face, charcode, &gindex);
+ }
+
+ _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_ft_is_synthetic (void *abstract_font)
+{
+ cairo_ft_scaled_font_t *scaled_font = abstract_font;
+ return scaled_font->ft_options.synth_flags != 0;
+}
+
+static cairo_int_status_t
+_cairo_index_to_glyph_name (void *abstract_font,
+ char **glyph_names,
+ int num_glyph_names,
+ unsigned long glyph_index,
+ unsigned long *glyph_array_index)
+{
+ cairo_ft_scaled_font_t *scaled_font = abstract_font;
+ cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+ FT_Face face;
+ char buffer[256]; /* PLRM spcifies max name length of 127 */
+ FT_Error error;
+ int i;
+
+ face = _cairo_ft_unscaled_font_lock_face (unscaled);
+ if (!face)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ error = FT_Get_Glyph_Name (face, glyph_index, buffer, sizeof buffer);
+
+ _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+ if (error != FT_Err_Ok) {
+ /* propagate fatal errors from FreeType */
+ if (error == FT_Err_Out_Of_Memory)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* FT first numbers the glyphs in the order they are read from the
+ * Type 1 font. Then if .notdef is not the first glyph, the first
+ * glyph is swapped with .notdef to ensure that .notdef is at
+ * glyph index 0.
+ *
+ * As all but two glyphs in glyph_names already have the same
+ * index as the FT glyph index, we first check if
+ * glyph_names[glyph_index] is the name we are looking for. If not
+ * we fall back to searching the entire array.
+ */
+
+ if ((long)glyph_index < num_glyph_names &&
+ strcmp (glyph_names[glyph_index], buffer) == 0)
+ {
+ *glyph_array_index = glyph_index;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ for (i = 0; i < num_glyph_names; i++) {
+ if (strcmp (glyph_names[i], buffer) == 0) {
+ *glyph_array_index = i;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_bool_t
+_ft_is_type1 (FT_Face face)
+{
+#if HAVE_FT_GET_X11_FONT_FORMAT
+ const char *font_format = FT_Get_X11_Font_Format (face);
+ if (font_format &&
+ (strcmp (font_format, "Type 1") == 0 ||
+ strcmp (font_format, "CFF") == 0))
+ {
+ return TRUE;
+ }
+#endif
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_ft_load_type1_data (void *abstract_font,
+ long offset,
+ unsigned char *buffer,
+ unsigned long *length)
+{
+ cairo_ft_scaled_font_t *scaled_font = abstract_font;
+ cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+ FT_Face face;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ unsigned long available_length;
+ unsigned long ret;
+
+ assert (length != NULL);
+
+ if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ face = _cairo_ft_unscaled_font_lock_face (unscaled);
+ if (!face)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+#if HAVE_FT_LOAD_SFNT_TABLE
+ if (FT_IS_SFNT (face)) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto unlock;
+ }
+#endif
+
+ if (! _ft_is_type1 (face)) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto unlock;
+ }
+
+ available_length = MAX (face->stream->size - offset, 0);
+ if (!buffer) {
+ *length = available_length;
+ } else {
+ if (*length > available_length) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else if (face->stream->read != NULL) {
+ /* Note that read() may be implemented as a macro, thanks POSIX!, so we
+ * need to wrap the following usage in parentheses in order to
+ * disambiguate it for the pre-processor - using the verbose function
+ * pointer dereference for clarity.
+ */
+ ret = (* face->stream->read) (face->stream,
+ offset,
+ buffer,
+ *length);
+ if (ret != *length)
+ status = _cairo_error (CAIRO_STATUS_READ_ERROR);
+ } else {
+ memcpy (buffer, face->stream->base + offset, *length);
+ }
+ }
+
+ unlock:
+ _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+ return status;
+}
+
+static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
+ CAIRO_FONT_TYPE_FT,
+ _cairo_ft_scaled_font_fini,
+ _cairo_ft_scaled_glyph_init,
+ NULL, /* text_to_glyphs */
+ _cairo_ft_ucs4_to_index,
+ _cairo_ft_load_truetype_table,
+ _cairo_ft_index_to_ucs4,
+ _cairo_ft_is_synthetic,
+ _cairo_index_to_glyph_name,
+ _cairo_ft_load_type1_data
+};
+
+/* #cairo_ft_font_face_t */
+
+#if CAIRO_HAS_FC_FONT
+static cairo_font_face_t *
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
+
+static cairo_status_t
+_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
+ cairo_font_face_t **font_face_out)
+{
+ cairo_font_face_t *font_face = (cairo_font_face_t *) &_cairo_font_face_nil;
+ FcPattern *pattern;
+ int fcslant;
+ int fcweight;
+
+ pattern = FcPatternCreate ();
+ if (!pattern) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return font_face->status;
+ }
+
+ if (!FcPatternAddString (pattern,
+ FC_FAMILY, (unsigned char *) toy_face->family))
+ {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ goto FREE_PATTERN;
+ }
+
+ switch (toy_face->slant)
+ {
+ case CAIRO_FONT_SLANT_ITALIC:
+ fcslant = FC_SLANT_ITALIC;
+ break;
+ case CAIRO_FONT_SLANT_OBLIQUE:
+ fcslant = FC_SLANT_OBLIQUE;
+ break;
+ case CAIRO_FONT_SLANT_NORMAL:
+ default:
+ fcslant = FC_SLANT_ROMAN;
+ break;
+ }
+
+ if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ goto FREE_PATTERN;
+ }
+
+ switch (toy_face->weight)
+ {
+ case CAIRO_FONT_WEIGHT_BOLD:
+ fcweight = FC_WEIGHT_BOLD;
+ break;
+ case CAIRO_FONT_WEIGHT_NORMAL:
+ default:
+ fcweight = FC_WEIGHT_MEDIUM;
+ break;
+ }
+
+ if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ goto FREE_PATTERN;
+ }
+
+ font_face = _cairo_ft_font_face_create_for_pattern (pattern);
+
+ FREE_PATTERN:
+ FcPatternDestroy (pattern);
+
+ *font_face_out = font_face;
+ return font_face->status;
+}
+#endif
+
+static 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_font_face_t *
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
+{
+ cairo_ft_font_face_t *font_face;
+
+ font_face = malloc (sizeof (cairo_ft_font_face_t));
+ if (unlikely (font_face == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+ }
+
+ font_face->unscaled = NULL;
+ font_face->next = NULL;
+
+ font_face->pattern = FcPatternDuplicate (pattern);
+ if (unlikely (font_face->pattern == NULL)) {
+ free (font_face);
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+ }
+
+ font_face->resolved_font_face = NULL;
+ font_face->resolved_config = NULL;
+
+ _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);
+
+ return &font_face->base;
+}
+#endif
+
+static cairo_font_face_t *
+_cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled,
+ cairo_ft_options_t *ft_options)
+{
+ cairo_ft_font_face_t *font_face, **prev_font_face;
+
+ /* Looked for an existing matching font face */
+ for (font_face = unscaled->faces, prev_font_face = &unscaled->faces;
+ font_face;
+ prev_font_face = &font_face->next, font_face = font_face->next)
+ {
+ if (font_face->ft_options.load_flags == ft_options->load_flags &&
+ font_face->ft_options.synth_flags == ft_options->synth_flags &&
+ cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base))
+ {
+ if (font_face->base.status) {
+ /* The font_face has been left in an error state, abandon it. */
+ *prev_font_face = font_face->next;
+ break;
+ }
+
+ if (font_face->unscaled == NULL) {
+ /* Resurrect this "zombie" font_face (from
+ * _cairo_ft_font_face_destroy), switching its unscaled_font
+ * from owner to ownee. */
+ font_face->unscaled = unscaled;
+ _cairo_unscaled_font_reference (&unscaled->base);
+ 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().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+ FcPattern *pattern)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return;
+
+ _cairo_ft_font_options_substitute (options, pattern);
+}
+
+static cairo_font_face_t *
+_cairo_ft_resolve_pattern (FcPattern *pattern,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *font_options)
+{
+ cairo_status_t status;
+
+ cairo_matrix_t scale;
+ FcPattern *resolved;
+ cairo_ft_font_transform_t sf;
+ FcResult result;
+ cairo_ft_unscaled_font_t *unscaled;
+ cairo_ft_options_t ft_options;
+ cairo_font_face_t *font_face;
+
+ scale = *ctm;
+ scale.x0 = scale.y0 = 0;
+ cairo_matrix_multiply (&scale,
+ font_matrix,
+ &scale);
+
+ status = _compute_transform (&sf, &scale, NULL);
+ if (unlikely (status))
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+
+ pattern = FcPatternDuplicate (pattern);
+ if (pattern == NULL)
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+
+ if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) {
+ font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+ goto FREE_PATTERN;
+ }
+
+ if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) {
+ font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+ goto FREE_PATTERN;
+ }
+
+ status = _cairo_ft_font_options_substitute (font_options, pattern);
+ if (status) {
+ font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+ goto FREE_PATTERN;
+ }
+
+ FcDefaultSubstitute (pattern);
+
+ status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
+ if (unlikely (status)) {
+ font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+ goto FREE_PATTERN;
+ }
+
+ if (unscaled == NULL) {
+ resolved = FcFontMatch (NULL, pattern, &result);
+ if (!resolved) {
+ /* We failed to find any font. Substitute twin so that the user can
+ * see something (and hopefully recognise that the font is missing)
+ * and not just receive a NO_MEMORY error during rendering.
+ */
+ font_face = _cairo_font_face_twin_create_fallback ();
+ goto FREE_PATTERN;
+ }
+
+ status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled);
+ if (unlikely (status || unscaled == NULL)) {
+ font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+ goto FREE_RESOLVED;
+ }
+ } else
+ resolved = pattern;
+
+ _get_pattern_ft_options (resolved, &ft_options);
+ font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
+ _cairo_unscaled_font_destroy (&unscaled->base);
+
+FREE_RESOLVED:
+ if (resolved != pattern)
+ FcPatternDestroy (resolved);
+
+FREE_PATTERN:
+ FcPatternDestroy (pattern);
+
+ return font_face;
+}
+
+/**
+ * cairo_ft_font_face_create_for_pattern:
+ * @pattern: A fontconfig pattern. Cairo makes a copy of the pattern
+ * if it needs to. You are free to modify or free @pattern after this call.
+ *
+ * Creates a new font face for the FreeType font backend based on a
+ * fontconfig pattern. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create(). The
+ * #cairo_scaled_font_t returned from cairo_scaled_font_create() is
+ * also for the FreeType backend and can be used with functions such
+ * as cairo_ft_scaled_font_lock_face().
+ *
+ * Font rendering options are represented both here and when you
+ * call cairo_scaled_font_create(). Font options that have a representation
+ * in a #FcPattern must be passed in here; to modify #FcPattern
+ * appropriately to reflect the options in a #cairo_font_options_t, call
+ * cairo_ft_font_options_substitute().
+ *
+ * The pattern's FC_FT_FACE element is inspected first and if that is set,
+ * that will be the FreeType font face associated with the returned cairo
+ * font face. Otherwise the FC_FILE element is checked. If it's set,
+ * that and the value of the FC_INDEX element (defaults to zero) of @pattern
+ * are used to load a font face from file.
+ *
+ * If both steps from the previous paragraph fails, @pattern will be passed
+ * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch,
+ * and the resulting font pattern is used.
+ *
+ * If the FC_FT_FACE element of @pattern is set, the user is responsible
+ * for making sure that the referenced FT_Face remains valid for the life
+ * time of the returned #cairo_font_face_t. See
+ * cairo_ft_font_face_create_for_ft_face() for an example of how to couple
+ * the life time of the FT_Face to that of the cairo font-face.
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.0
+ **/
+cairo_font_face_t *
+cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
+{
+ cairo_ft_unscaled_font_t *unscaled;
+ cairo_font_face_t *font_face;
+ cairo_ft_options_t ft_options;
+ cairo_status_t status;
+
+ status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
+ if (unlikely (status))
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+ if (unlikely (unscaled == NULL)) {
+ /* Store the pattern. We will resolve it and create unscaled
+ * font when creating scaled fonts */
+ return _cairo_ft_font_face_create_for_pattern (pattern);
+ }
+
+ _get_pattern_ft_options (pattern, &ft_options);
+ font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
+ _cairo_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.
+ *
+ * Since: 1.0
+ **/
+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.synth_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_font_face_set_synthesize:
+ * @font_face: The #cairo_ft_font_face_t object to modify
+ * @synth_flags: the set of synthesis options to enable
+ *
+ * FreeType provides the ability to synthesize different glyphs from a base
+ * font, which is useful if you lack those glyphs from a true bold or oblique
+ * font. See also #cairo_ft_synthesize_t.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face,
+ unsigned int synth_flags)
+{
+ cairo_ft_font_face_t *ft;
+
+ if (font_face->backend->type != CAIRO_FONT_TYPE_FT)
+ return;
+
+ ft = (cairo_ft_font_face_t *) font_face;
+ ft->ft_options.synth_flags |= synth_flags;
+}
+
+/**
+ * cairo_ft_font_face_unset_synthesize:
+ * @font_face: The #cairo_ft_font_face_t object to modify
+ * @synth_flags: the set of synthesis options to disable
+ *
+ * See cairo_ft_font_face_set_synthesize().
+ *
+ * Since: 1.12
+ **/
+void
+cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face,
+ unsigned int synth_flags)
+{
+ cairo_ft_font_face_t *ft;
+
+ if (font_face->backend->type != CAIRO_FONT_TYPE_FT)
+ return;
+
+ ft = (cairo_ft_font_face_t *) font_face;
+ ft->ft_options.synth_flags &= ~synth_flags;
+}
+
+/**
+ * cairo_ft_font_face_get_synthesize:
+ * @font_face: The #cairo_ft_font_face_t object to query
+ *
+ * See #cairo_ft_synthesize_t.
+ *
+ * Returns: the current set of synthesis options.
+ *
+ * Since: 1.12
+ **/
+unsigned int
+cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face)
+{
+ cairo_ft_font_face_t *ft;
+
+ if (font_face->backend->type != CAIRO_FONT_TYPE_FT)
+ return 0;
+
+ ft = (cairo_ft_font_face_t *) font_face;
+ return ft->ft_options.synth_flags;
+}
+
+/**
+ * cairo_ft_scaled_font_lock_face:
+ * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an
+ * object can be created by calling cairo_scaled_font_create() on a
+ * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(),
+ * cairo_ft_font_face_create_for_ft_face()).
+ *
+ * cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType
+ * backend font and scales it appropriately for the font. You must
+ * release the face with cairo_ft_scaled_font_unlock_face()
+ * when you are done using it. Since the #FT_Face object can be
+ * shared between multiple #cairo_scaled_font_t objects, you must not
+ * lock any other font objects until you unlock this one. A count is
+ * kept of the number of times cairo_ft_scaled_font_lock_face() is
+ * called. cairo_ft_scaled_font_unlock_face() must be called the same number
+ * of times.
+ *
+ * You must be careful when using this function in a library or in a
+ * threaded application, because freetype's design makes it unsafe to
+ * call freetype functions simultaneously from multiple threads, (even
+ * if using distinct FT_Face objects). Because of this, application
+ * code that acquires an FT_Face object with this call must add its
+ * own locking to protect any use of that object, (and which also must
+ * protect any other calls into cairo as almost any cairo function
+ * might result in a call into the freetype library).
+ *
+ * Return value: The #FT_Face object for @font, scaled appropriately,
+ * or %NULL if @scaled_font is in an error state (see
+ * cairo_scaled_font_status()) or there is insufficient memory.
+ *
+ * Since: 1.0
+ **/
+FT_Face
+cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font)
+{
+ cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font;
+ FT_Face face;
+ cairo_status_t status;
+
+ if (! _cairo_scaled_font_is_ft (abstract_font)) {
+ _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ if (scaled_font->base.status)
+ return NULL;
+
+ face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled);
+ if (unlikely (face == NULL)) {
+ status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled,
+ &scaled_font->base.scale);
+ if (unlikely (status)) {
+ _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
+ status = _cairo_scaled_font_set_error (&scaled_font->base, status);
+ return NULL;
+ }
+
+ /* 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().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font)
+{
+ cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font;
+
+ if (! _cairo_scaled_font_is_ft (abstract_font)) {
+ _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+ return;
+ }
+
+ if (scaled_font->base.status)
+ return;
+
+ /* Note: We released the unscaled font's mutex at the end of
+ * cairo_ft_scaled_font_lock_face, so we have to acquire it again
+ * as _cairo_ft_unscaled_font_unlock_face expects it to be held
+ * when we call into it. */
+ CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex);
+
+ _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
+}
+
+static cairo_bool_t
+_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font)
+{
+ cairo_ft_scaled_font_t *ft_scaled_font;
+
+ if (!_cairo_scaled_font_is_ft (scaled_font))
+ return FALSE;
+
+ ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font;
+ if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT)
+ return TRUE;
+ return FALSE;
+}
+
+unsigned int
+_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font)
+{
+ cairo_ft_scaled_font_t *ft_scaled_font;
+
+ if (! _cairo_scaled_font_is_ft (scaled_font))
+ return 0;
+
+ ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font;
+ return ft_scaled_font->ft_options.load_flags;
+}
+
+void
+_cairo_ft_font_reset_static_data (void)
+{
+ _cairo_ft_unscaled_font_map_destroy ();
+}
diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h
new file mode 100755
index 000000000..392151866
--- /dev/null
+++ b/src/cairo-ft-private.h
@@ -0,0 +1,61 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Graydon Hoare <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 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/src/cairo-ft.h b/src/cairo-ft.h
new file mode 100755
index 000000000..29c43c965
--- /dev/null
+++ b/src/cairo-ft.h
@@ -0,0 +1,118 @@
+/* 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_ft_synthesize_t:
+ * @CAIRO_FT_SYNTHESIZE_BOLD: Embolden the glyphs (redraw with a pixel offset)
+ * @CAIRO_FT_SYNTHESIZE_OBLIQUE: Slant the glyph outline by 12 degrees to the
+ * right.
+ *
+ * A set of synthesis options to control how FreeType renders the glyphs
+ * for a particular font face.
+ *
+ * Individual synthesis features of a #cairo_ft_font_face_t can be set
+ * using cairo_ft_font_face_set_synthesize(), or disabled using
+ * cairo_ft_font_face_unset_synthesize(). The currently enabled set of
+ * synthesis options can be queried with cairo_ft_font_face_get_synthesize().
+ *
+ * Note: that when synthesizing glyphs, the font metrics returned will only
+ * be estimates.
+ *
+ * Since: 1.12
+ **/
+typedef enum {
+ CAIRO_FT_SYNTHESIZE_BOLD = 1 << 0,
+ CAIRO_FT_SYNTHESIZE_OBLIQUE = 1 << 1
+} cairo_ft_synthesize_t;
+
+cairo_public void
+cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face,
+ unsigned int synth_flags);
+
+cairo_public void
+cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face,
+ unsigned int synth_flags);
+
+cairo_public unsigned int
+cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face);
+
+
+cairo_public FT_Face
+cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font);
+
+cairo_public void
+cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font);
+
+#if CAIRO_HAS_FC_FONT
+
+cairo_public cairo_font_face_t *
+cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
+
+cairo_public void
+cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+ FcPattern *pattern);
+
+#endif
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_FT_FONT */
+# error Cairo was not compiled with support for the freetype font backend
+#endif /* CAIRO_HAS_FT_FONT */
+
+#endif /* CAIRO_FT_H */
diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c
new file mode 100755
index 000000000..c9f7782d1
--- /dev/null
+++ b/src/cairo-gl-composite.c
@@ -0,0 +1,1605 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2011 Samsung Electronics
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ * Henry Song <hsong@sisa.samsung.com>
+ * Martin Robinson <mrobinson@igalia.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+
+cairo_int_status_t
+_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen,
+ cairo_bool_t encode_color_as_attribute)
+{
+ _cairo_gl_operand_destroy (&setup->src);
+ return _cairo_gl_operand_init (&setup->src, pattern, setup->dst,
+ sample, extents, use_texgen,
+ encode_color_as_attribute);
+}
+
+void
+_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup,
+ const cairo_gl_operand_t *source)
+{
+ cairo_int_status_t status;
+
+ _cairo_gl_operand_destroy (&setup->src);
+ _cairo_gl_operand_copy (&setup->src, source);
+ if (source->type == CAIRO_GL_OPERAND_TEXTURE ||
+ source->type == CAIRO_GL_OPERAND_GAUSSIAN)
+ status = _cairo_gl_surface_resolve_multisampling (source->texture.surface);
+}
+
+void
+_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup,
+ const cairo_color_t *color)
+{
+ _cairo_gl_operand_destroy (&setup->src);
+ _cairo_gl_solid_operand_init (&setup->src, color);
+}
+
+cairo_int_status_t
+_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen)
+{
+ _cairo_gl_operand_destroy (&setup->mask);
+ if (pattern == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_gl_operand_init (&setup->mask, pattern, setup->dst,
+ sample, extents, use_texgen, FALSE);
+}
+
+void
+_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup,
+ const cairo_gl_operand_t *mask)
+{
+ cairo_int_status_t status;
+ _cairo_gl_operand_destroy (&setup->mask);
+ if (mask) {
+ _cairo_gl_operand_copy (&setup->mask, mask);
+ if (mask->type == CAIRO_GL_OPERAND_TEXTURE ||
+ mask->type == CAIRO_GL_OPERAND_GAUSSIAN)
+ status = _cairo_gl_surface_resolve_multisampling (mask->texture.surface);
+ }
+}
+
+void
+_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup)
+{
+ setup->spans = TRUE;
+}
+
+void
+_cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup)
+{
+ setup->multisample = TRUE;
+}
+
+void
+_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
+ cairo_region_t *clip_region)
+{
+ setup->clip_region = clip_region;
+}
+
+void
+_cairo_gl_composite_set_clip (cairo_gl_composite_t *setup,
+ cairo_clip_t *clip)
+{
+ setup->clip = clip;
+}
+
+static void
+_cairo_gl_composite_bind_to_shader (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup)
+{
+ _cairo_gl_shader_bind_matrix4f(ctx, CAIRO_GL_UNIFORM_PROJECTION_MATRIX,
+ ctx->modelviewprojection_matrix);
+ _cairo_gl_operand_bind_to_shader (ctx, &setup->src, CAIRO_GL_TEX_SOURCE);
+ _cairo_gl_operand_bind_to_shader (ctx, &setup->mask, CAIRO_GL_TEX_MASK);
+}
+
+static void
+_cairo_gl_texture_set_filter (cairo_gl_context_t *ctx,
+ GLuint target,
+ cairo_filter_t filter)
+{
+ switch (filter) {
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ ctx->dispatch.TexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ ctx->dispatch.TexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ break;
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ ctx->dispatch.TexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ ctx->dispatch.TexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+_cairo_gl_texture_set_extend (cairo_gl_context_t *ctx,
+ GLuint target,
+ cairo_extend_t extend,
+ cairo_bool_t use_atlas)
+{
+ GLint wrap_mode;
+ assert (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base) ||
+ (extend != CAIRO_EXTEND_REPEAT && extend != CAIRO_EXTEND_REFLECT));
+
+ switch (extend) {
+ case CAIRO_EXTEND_NONE:
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3)
+ wrap_mode = GL_CLAMP_TO_EDGE;
+ else
+ wrap_mode = GL_CLAMP_TO_BORDER;
+ break;
+ case CAIRO_EXTEND_PAD:
+ wrap_mode = GL_CLAMP_TO_EDGE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ if (ctx->has_npot_repeat)
+ wrap_mode = GL_REPEAT;
+ else
+ wrap_mode = GL_CLAMP_TO_EDGE;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ if (ctx->has_npot_repeat)
+ wrap_mode = GL_MIRRORED_REPEAT;
+ else
+ wrap_mode = GL_CLAMP_TO_EDGE;
+ break;
+ default:
+ wrap_mode = 0;
+ }
+
+ if (likely (wrap_mode)) {
+ ctx->dispatch.TexParameteri (target, GL_TEXTURE_WRAP_S, wrap_mode);
+ ctx->dispatch.TexParameteri (target, GL_TEXTURE_WRAP_T, wrap_mode);
+ }
+}
+
+
+static void
+_cairo_gl_context_setup_operand (cairo_gl_context_t *ctx,
+ cairo_gl_tex_t tex_unit,
+ cairo_gl_operand_t *operand,
+ unsigned int vertex_offset,
+ cairo_bool_t vertex_size_changed)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ cairo_bool_t needs_setup;
+ unsigned int offset = vertex_offset;
+
+ /* XXX: we need to do setup when switching from shaders
+ * to no shaders (or back) */
+ needs_setup = vertex_size_changed;
+ needs_setup |= _cairo_gl_operand_needs_setup (&ctx->operands[tex_unit],
+ operand,
+ vertex_offset);
+
+ if (needs_setup) {
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_context_destroy_operand (ctx, tex_unit);
+ }
+
+ memcpy (&ctx->operands[tex_unit], operand, sizeof (cairo_gl_operand_t));
+ ctx->operands[tex_unit].vertex_offset = vertex_offset;
+
+ if (! needs_setup)
+ return;
+
+ switch (operand->type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ break;
+ /* fall through */
+ case CAIRO_GL_OPERAND_CONSTANT:
+ if (operand->constant.encode_as_attribute) {
+ dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4,
+ GL_FLOAT, GL_FALSE, ctx->vertex_size,
+ ctx->vb + vertex_offset);
+ dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
+ }
+ break;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (ctx->states_cache.active_texture != GL_TEXTURE0 + tex_unit) {
+ dispatch->ActiveTexture (GL_TEXTURE0 + tex_unit);
+ ctx->states_cache.active_texture = GL_TEXTURE0 + tex_unit;
+ }
+ dispatch->BindTexture (ctx->tex_target, operand->texture.tex);
+ _cairo_gl_texture_set_extend (ctx, ctx->tex_target,
+ operand->texture.attributes.extend,
+ operand->texture.use_atlas);
+ _cairo_gl_texture_set_filter (ctx, ctx->tex_target,
+ operand->texture.attributes.filter);
+ if (! operand->texture.texgen) {
+ dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2,
+ GL_FLOAT, GL_FALSE, ctx->vertex_size,
+ ctx->vb + offset);
+ dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+ offset += 2 * sizeof (GLfloat);
+ }
+
+ if (operand->texture.use_atlas) {
+ dispatch->VertexAttribPointer (CAIRO_GL_START_COORD0_ATTRIB_INDEX + tex_unit,
+ 2, GL_FLOAT, GL_FALSE,
+ ctx->vertex_size,
+ ctx->vb + offset);
+ dispatch->EnableVertexAttribArray (CAIRO_GL_START_COORD0_ATTRIB_INDEX + tex_unit);
+ dispatch->VertexAttribPointer (CAIRO_GL_STOP_COORD0_ATTRIB_INDEX + tex_unit,
+ 2, GL_FLOAT, GL_FALSE,
+ ctx->vertex_size,
+ ctx->vb + offset + 2 * sizeof (float));
+ dispatch->EnableVertexAttribArray (CAIRO_GL_STOP_COORD0_ATTRIB_INDEX + tex_unit);
+ }
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ if(ctx->states_cache.active_texture != GL_TEXTURE0 + tex_unit) {
+ dispatch->ActiveTexture (GL_TEXTURE0 + tex_unit);
+ ctx->states_cache.active_texture = GL_TEXTURE0 + tex_unit;
+ }
+ dispatch->BindTexture (ctx->tex_target, operand->gradient.gradient->tex);
+ _cairo_gl_texture_set_extend (ctx, ctx->tex_target,
+ operand->gradient.extend, FALSE);
+ _cairo_gl_texture_set_filter (ctx, ctx->tex_target, CAIRO_FILTER_BILINEAR);
+
+ if (! operand->gradient.texgen) {
+ dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2,
+ GL_FLOAT, GL_FALSE, ctx->vertex_size,
+ ctx->vb + vertex_offset);
+ dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+ }
+ break;
+ }
+}
+
+static void
+_cairo_gl_context_setup_spans (cairo_gl_context_t *ctx,
+ cairo_bool_t encode_src_as_attribute,
+ cairo_bool_t spans_enabled,
+ unsigned int vertex_size,
+ unsigned int vertex_offset)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+ if (! spans_enabled) {
+ ctx->spans = FALSE;
+
+ /* If we are going to encode the source as an attribute, we don't want
+ * to disable the attribute array here only to enable it again later. */
+ if (! encode_src_as_attribute)
+ dispatch->DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
+
+ return;
+ }
+
+ dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4,
+ GL_UNSIGNED_BYTE, GL_TRUE, vertex_size,
+ ctx->vb + vertex_offset);
+ dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
+ ctx->spans = TRUE;
+}
+
+void
+_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx,
+ cairo_gl_tex_t tex_unit)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+ if (!_cairo_gl_context_is_flushed (ctx))
+ _cairo_gl_composite_flush (ctx);
+
+ switch (ctx->operands[tex_unit].type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ break;
+ /* fall through */
+ case CAIRO_GL_OPERAND_CONSTANT:
+ if (ctx->operands[tex_unit].constant.encode_as_attribute)
+ dispatch->DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
+ break;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+ if (ctx->operands[tex_unit].texture.use_atlas) {
+ dispatch->DisableVertexAttribArray (CAIRO_GL_START_COORD0_ATTRIB_INDEX + tex_unit);
+ dispatch->DisableVertexAttribArray (CAIRO_GL_STOP_COORD0_ATTRIB_INDEX + tex_unit);
+ }
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+ break;
+ }
+
+ memset (&ctx->operands[tex_unit], 0, sizeof (cairo_gl_operand_t));
+}
+
+static void
+_cairo_gl_set_operator (cairo_gl_context_t *ctx,
+ cairo_operator_t op,
+ cairo_bool_t component_alpha)
+{
+ struct {
+ GLenum src;
+ GLenum dst;
+ } blend_factors[] = {
+ { GL_ZERO, GL_ZERO }, /* Clear */
+ { GL_ONE, GL_ZERO }, /* Source */
+ { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */
+ { GL_DST_ALPHA, GL_ZERO }, /* In */
+ { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */
+ { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */
+
+ { GL_ZERO, GL_ONE }, /* Dest */
+ { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */
+ { GL_ZERO, GL_SRC_ALPHA }, /* DestIn */
+ { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */
+ { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */
+
+ { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */
+ { GL_ONE, GL_ONE }, /* Add */
+ };
+ GLenum src_factor, dst_factor;
+
+ assert (op < ARRAY_LENGTH (blend_factors));
+ /* different dst and component_alpha changes cause flushes elsewhere */
+ if (ctx->current_operator != op)
+ _cairo_gl_composite_flush (ctx);
+ ctx->current_operator = op;
+
+ src_factor = blend_factors[op].src;
+ dst_factor = blend_factors[op].dst;
+
+ /* Even when the user requests CAIRO_CONTENT_COLOR, we use GL_RGBA
+ * due to texture filtering of GL_CLAMP_TO_BORDER. So fix those
+ * bits in that case.
+ */
+ if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) {
+ if (src_factor == GL_ONE_MINUS_DST_ALPHA)
+ src_factor = GL_ZERO;
+ if (src_factor == GL_DST_ALPHA)
+ src_factor = GL_ONE;
+ }
+
+ if (component_alpha) {
+ if (dst_factor == GL_ONE_MINUS_SRC_ALPHA)
+ dst_factor = GL_ONE_MINUS_SRC_COLOR;
+ if (dst_factor == GL_SRC_ALPHA)
+ dst_factor = GL_SRC_COLOR;
+ }
+
+ if (ctx->current_target->base.content == CAIRO_CONTENT_ALPHA) {
+ /* cache BlendFunc, src factor and dst factor, alpha factor */
+ if (ctx->states_cache.src_color_factor != GL_ZERO ||
+ ctx->states_cache.dst_color_factor != GL_ZERO ||
+ ctx->states_cache.src_alpha_factor != src_factor ||
+ ctx->states_cache.dst_alpha_factor != dst_factor) {
+ ctx->dispatch.BlendFuncSeparate (GL_ZERO, GL_ZERO, src_factor, dst_factor);
+ ctx->states_cache.src_color_factor = GL_ZERO;
+ ctx->states_cache.dst_color_factor = GL_ZERO;
+ ctx->states_cache.src_alpha_factor = src_factor;
+ ctx->states_cache.dst_alpha_factor = dst_factor;
+ }
+ } else if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) {
+ if (ctx->states_cache.src_color_factor != src_factor ||
+ ctx->states_cache.dst_color_factor != dst_factor ||
+ ctx->states_cache.src_alpha_factor != GL_ONE ||
+ ctx->states_cache.dst_alpha_factor != GL_ONE) {
+ ctx->dispatch.BlendFuncSeparate (src_factor, dst_factor, GL_ONE, GL_ONE);
+ ctx->states_cache.src_color_factor = src_factor;
+ ctx->states_cache.dst_color_factor = dst_factor;
+ ctx->states_cache.src_alpha_factor = GL_ONE;
+ ctx->states_cache.dst_alpha_factor = GL_ONE;
+ }
+ } else {
+ if (ctx->states_cache.src_color_factor != src_factor ||
+ ctx->states_cache.dst_color_factor != dst_factor) {
+ ctx->dispatch.BlendFunc (src_factor, dst_factor);
+ ctx->states_cache.src_color_factor = src_factor;
+ ctx->states_cache.dst_color_factor = dst_factor;
+ }
+ }
+}
+
+static cairo_status_t
+_cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup)
+{
+ cairo_gl_shader_t *pre_shader = NULL;
+ cairo_status_t status;
+
+ /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
+ * 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 (setup->op == CAIRO_OPERATOR_CLEAR) {
+ _cairo_gl_solid_operand_init (&setup->src, CAIRO_COLOR_WHITE);
+ setup->op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ /*
+ * implements component-alpha %CAIRO_OPERATOR_OVER using two passes of
+ * the simpler operations %CAIRO_OPERATOR_DEST_OUT and %CAIRO_OPERATOR_ADD.
+ *
+ * From http://anholt.livejournal.com/32058.html:
+ *
+ * The trouble is that component-alpha rendering requires two different sources
+ * for blending: one for the source value to the blender, which is the
+ * per-channel multiplication of source and mask, and one for the source alpha
+ * for multiplying with the destination channels, which is the multiplication
+ * of the source channels by the mask alpha. So the equation for Over is:
+ *
+ * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A
+ * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R
+ * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G
+ * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B
+ *
+ * But we can do some simpler operations, right? How about PictOpOutReverse,
+ * which has a source factor of 0 and dest factor of (1 - source alpha). We
+ * can get the source alpha value (srca.X = src.A * mask.X) out of the texture
+ * blenders pretty easily. So we can do a component-alpha OutReverse, which
+ * gets us:
+ *
+ * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A
+ * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R
+ * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G
+ * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B
+ *
+ * OK. And if an op doesn't use the source alpha value for the destination
+ * factor, then we can do the channel multiplication in the texture blenders
+ * to get the source value, and ignore the source alpha that we wouldn't use.
+ * We've supported this in the Radeon driver for a long time. An example would
+ * be PictOpAdd, which does:
+ *
+ * dst.A = src.A * mask.A + dst.A
+ * dst.R = src.R * mask.R + dst.R
+ * dst.G = src.G * mask.G + dst.G
+ * dst.B = src.B * mask.B + dst.B
+ *
+ * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right
+ * after it, we get:
+ *
+ * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A)
+ * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R)
+ * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G)
+ * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B)
+ *
+ * This two-pass trickery could be avoided using a new GL extension that
+ * lets two values come out of the shader and into the blend unit.
+ */
+ if (setup->op == CAIRO_OPERATOR_OVER) {
+ setup->op = CAIRO_OPERATOR_ADD;
+ status = _cairo_gl_get_shader_by_type (ctx,
+ &setup->src,
+ &setup->mask,
+ setup->spans,
+ CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA,
+ &pre_shader);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (ctx->pre_shader != pre_shader)
+ _cairo_gl_composite_flush (ctx);
+ ctx->pre_shader = pre_shader;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_scissor_to_doubles (cairo_gl_surface_t *surface,
+ double x1, double y1,
+ double x2, double y2)
+{
+ double height;
+
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *) cairo_surface_get_device ((cairo_surface_t *) surface);
+
+ height = y2 - y1;
+ if (_cairo_gl_surface_is_texture (surface) == FALSE)
+ y1 = surface->height - (y1 + height);
+ ctx->dispatch.Scissor (x1, y1, x2 - x1, height);
+}
+
+void
+_cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface,
+ const cairo_rectangle_int_t *r)
+{
+ _scissor_to_doubles (surface, r->x, r->y, r->x+r->width, r->y+r->height);
+}
+
+static void
+_scissor_to_box (cairo_gl_surface_t *surface,
+ const cairo_box_t *box)
+{
+ double x1, y1, x2, y2;
+ _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
+ _scissor_to_doubles (surface, x1, y1, x2, y2);
+}
+
+static cairo_bool_t
+_cairo_gl_composite_setup_vbo (cairo_gl_context_t *ctx,
+ unsigned int size_per_vertex)
+{
+ cairo_bool_t vertex_size_changed = ctx->vertex_size != size_per_vertex;
+ if (vertex_size_changed) {
+ ctx->vertex_size = size_per_vertex;
+ _cairo_gl_composite_flush (ctx);
+ }
+
+ if (_cairo_gl_context_is_flushed (ctx)) {
+ ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2,
+ GL_FLOAT, GL_FALSE, size_per_vertex,
+ ctx->vb);
+ ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX);
+ }
+
+ return vertex_size_changed;
+}
+
+static cairo_int_status_t
+_cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup,
+ cairo_gl_context_t *ctx,
+ int vertex_size,
+ cairo_bool_t clip_is_equal)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ cairo_gl_surface_t *dst = setup->dst;
+ cairo_clip_t *clip = setup->clip;
+ cairo_clip_t *old_clip = setup->dst->clip_on_stencil_buffer;
+
+ if (clip->num_boxes == 1 && clip->path == NULL) {
+ _scissor_to_box (dst, &clip->boxes[0]);
+ _enable_scissor_buffer (ctx);
+ goto disable_stencil_buffer_and_return;
+ }
+
+ if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto disable_stencil_buffer_and_return;
+ }
+
+ /* The clip is not rectangular, so use the stencil buffer. */
+ if (! ctx->states_cache.depth_mask ) {
+ ctx->dispatch.DepthMask (GL_TRUE);
+ ctx->states_cache.depth_mask = TRUE;
+ }
+
+ _enable_stencil_buffer (ctx);
+ _enable_scissor_buffer (ctx);
+ _cairo_gl_scissor_to_rectangle (dst, _cairo_clip_get_extents (clip));
+
+ if (clip_is_equal)
+ goto activate_stencil_buffer_and_return;
+
+ /* Clear the stencil buffer, but only the areas that we are
+ * going to be drawing to. */
+ if (old_clip) {
+ _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer);
+ }
+
+ setup->dst->clip_on_stencil_buffer = _cairo_clip_copy (setup->clip);
+
+ ctx->dispatch.ClearStencil (0);
+ ctx->dispatch.Clear (GL_STENCIL_BUFFER_BIT);
+
+ ctx->dispatch.StencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ ctx->dispatch.StencilFunc (GL_EQUAL, 1, 0xffffffff);
+ ctx->dispatch.ColorMask (0, 0, 0, 0);
+
+ status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip);
+
+ if (unlikely (status)) {
+ ctx->dispatch.ColorMask (1, 1, 1, 1);
+ goto disable_stencil_buffer_and_return;
+ }
+
+ /* We want to only render to the stencil buffer, so draw everything now.
+ Flushing also unbinds the VBO, which we want to rebind for regular
+ drawing. */
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_composite_setup_vbo (ctx, vertex_size);
+
+activate_stencil_buffer_and_return:
+ ctx->dispatch.ColorMask (1, 1, 1, 1);
+
+ ctx->dispatch.StencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
+ ctx->dispatch.StencilFunc (GL_EQUAL, 1, 0xffffffff);
+ return CAIRO_INT_STATUS_SUCCESS;
+
+disable_stencil_buffer_and_return:
+ _disable_stencil_buffer (ctx);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_composite_setup_clipping (cairo_gl_composite_t *setup,
+ cairo_gl_context_t *ctx,
+ int vertex_size)
+{
+ cairo_bool_t clip_region_changing = TRUE;
+ cairo_bool_t clip_is_equal = TRUE;
+
+ if (! _cairo_clip_equal (setup->dst->clip_on_stencil_buffer, setup->clip)) {
+ _cairo_gl_composite_flush (ctx);
+ clip_is_equal = FALSE;
+ }
+
+ if (! setup->clip && ! setup->clip_region && ! ctx->clip_region)
+ goto disable_all_clipping;
+
+ clip_region_changing = ! cairo_region_equal (ctx->clip_region, setup->clip_region);
+ if (! _cairo_gl_context_is_flushed (ctx) &&
+ clip_region_changing)
+ _cairo_gl_composite_flush (ctx);
+
+ assert (!setup->clip_region || !setup->clip);
+
+ /* setup->clip is only used by the msaa compositor and setup->clip_region
+ * only by the other compositors, so it's safe to wait to clean up obsolete
+ * clips. */
+ if (clip_region_changing) {
+ cairo_region_destroy (ctx->clip_region);
+ ctx->clip_region = cairo_region_reference (setup->clip_region);
+ }
+
+ /* For clip regions, we scissor right before drawing. */
+ if (setup->clip_region)
+ goto disable_all_clipping;
+
+ if (setup->clip)
+ return _cairo_gl_composite_setup_painted_clipping (setup, ctx,
+ vertex_size,
+ clip_is_equal);
+disable_all_clipping:
+ _disable_stencil_buffer (ctx);
+ _disable_scissor_buffer (ctx);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup,
+ cairo_gl_context_t *ctx)
+{
+ unsigned int dst_size, src_size, mask_size, vertex_size;
+ cairo_status_t status;
+ cairo_gl_shader_t *shader;
+ cairo_bool_t component_alpha;
+ cairo_bool_t vertex_size_changed;
+
+ component_alpha =
+ setup->mask.type == CAIRO_GL_OPERAND_TEXTURE &&
+ setup->mask.texture.attributes.has_component_alpha;
+
+ /* Do various magic for component alpha */
+ if (component_alpha) {
+ status = _cairo_gl_composite_begin_component_alpha (ctx, setup);
+ if (unlikely (status))
+ return status;
+ } else {
+ if (ctx->pre_shader) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->pre_shader = NULL;
+ }
+ }
+
+ status = _cairo_gl_get_shader_by_type (ctx,
+ &setup->src,
+ &setup->mask,
+ setup->spans,
+ component_alpha ?
+ CAIRO_GL_SHADER_IN_CA_SOURCE :
+ CAIRO_GL_SHADER_IN_NORMAL,
+ &shader);
+ if (unlikely (status)) {
+ ctx->pre_shader = NULL;
+ return status;
+ }
+ if (ctx->current_shader != shader)
+ _cairo_gl_composite_flush (ctx);
+
+ status = CAIRO_STATUS_SUCCESS;
+
+ dst_size = 2 * sizeof (GLfloat);
+ src_size = _cairo_gl_operand_get_vertex_size (&setup->src);
+ mask_size = _cairo_gl_operand_get_vertex_size (&setup->mask);
+ vertex_size = dst_size + src_size + mask_size;
+
+ if (setup->spans)
+ vertex_size += sizeof (GLfloat);
+
+ if (setup->src.type != CAIRO_GL_OPERAND_CONSTANT ||
+ ! setup->src.constant.encode_as_attribute)
+ _cairo_gl_context_setup_spans (ctx,
+ setup->spans,
+ setup->src.type == CAIRO_GL_OPERAND_CONSTANT ||
+ ! setup->src.constant.encode_as_attribute,
+ vertex_size,
+ dst_size + src_size + mask_size);
+
+ vertex_size_changed = _cairo_gl_composite_setup_vbo (ctx, vertex_size);
+
+ _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src,
+ dst_size, vertex_size_changed);
+ _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask,
+ dst_size + src_size, vertex_size_changed);
+
+ _cairo_gl_set_operator (ctx, setup->op, component_alpha);
+
+ if (_cairo_gl_context_is_flushed (ctx)) {
+ if (ctx->pre_shader) {
+ _cairo_gl_set_shader (ctx, ctx->pre_shader);
+ _cairo_gl_composite_bind_to_shader (ctx, setup);
+ }
+ _cairo_gl_set_shader (ctx, shader);
+ _cairo_gl_composite_bind_to_shader (ctx, setup);
+ }
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gl_composite_begin (cairo_gl_composite_t *setup,
+ cairo_gl_context_t **ctx_out)
+{
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+
+ assert (setup->dst);
+
+ status = _cairo_gl_context_acquire (setup->dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ setup->dst->content_cleared = FALSE;
+
+ _cairo_gl_context_set_destination (ctx, setup->dst, setup->multisample);
+ if (ctx->states_cache.blend_enabled == FALSE) {
+ ctx->dispatch.Enable (GL_BLEND);
+ ctx->states_cache.blend_enabled = TRUE;
+ }
+ status = _cairo_gl_set_operands_and_operator (setup, ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _cairo_gl_composite_setup_clipping (setup, ctx, ctx->vertex_size);
+ if (unlikely (status))
+ goto FAIL;
+
+ *ctx_out = ctx;
+
+FAIL:
+ if (unlikely (status))
+ status = _cairo_gl_context_release (ctx, status);
+
+ return status;
+}
+
+static inline void
+_cairo_gl_composite_draw_tristrip (cairo_gl_context_t *ctx)
+{
+ cairo_array_t* indices = &ctx->tristrip_indices;
+ const unsigned short *indices_array = _cairo_array_index_const (indices, 0);
+
+ if (ctx->pre_shader) {
+ cairo_gl_shader_t *prev_shader = ctx->current_shader;
+
+ _cairo_gl_set_shader (ctx, ctx->pre_shader);
+ _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE);
+ ctx->dispatch.DrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array);
+
+ _cairo_gl_set_shader (ctx, prev_shader);
+ _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE);
+ }
+
+ ctx->dispatch.DrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array);
+ _cairo_array_truncate (indices, 0);
+}
+
+static inline void
+_cairo_gl_composite_draw_line (cairo_gl_context_t *ctx)
+{
+ GLenum type = GL_LINE_STRIP;
+ cairo_array_t* indices = &ctx->tristrip_indices;
+ const unsigned short *indices_array = _cairo_array_index_const (indices, 0);
+
+ if (ctx->draw_mode == CAIRO_GL_LINES)
+ type = GL_LINES;
+
+ if (ctx->pre_shader) {
+ cairo_gl_shader_t *prev_shader = ctx->current_shader;
+
+ _cairo_gl_set_shader (ctx, ctx->pre_shader);
+ _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE);
+ ctx->dispatch.DrawElements (type, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array);
+
+ _cairo_gl_set_shader (ctx, prev_shader);
+ _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE);
+ }
+
+ ctx->dispatch.DrawElements (type, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array);
+ _cairo_array_truncate (indices, 0);
+}
+
+static inline void
+_cairo_gl_composite_draw_triangles (cairo_gl_context_t *ctx,
+ unsigned int count)
+{
+ if (! ctx->pre_shader) {
+ ctx->dispatch.DrawArrays (GL_TRIANGLES, 0, count);
+ } else {
+ cairo_gl_shader_t *prev_shader = ctx->current_shader;
+
+ _cairo_gl_set_shader (ctx, ctx->pre_shader);
+ _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE);
+ ctx->dispatch.DrawArrays (GL_TRIANGLES, 0, count);
+
+ _cairo_gl_set_shader (ctx, prev_shader);
+ _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE);
+ ctx->dispatch.DrawArrays (GL_TRIANGLES, 0, count);
+ }
+}
+
+static void
+_cairo_gl_composite_draw_triangles_with_clip_region (cairo_gl_context_t *ctx,
+ unsigned int count)
+{
+ int i, num_rectangles;
+
+ if (!ctx->clip_region) {
+ _cairo_gl_composite_draw_triangles (ctx, count);
+ return;
+ }
+
+ num_rectangles = cairo_region_num_rectangles (ctx->clip_region);
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (ctx->clip_region, i, &rect);
+
+ _cairo_gl_scissor_to_rectangle (ctx->current_target, &rect);
+ _enable_scissor_buffer (ctx);
+ _cairo_gl_composite_draw_triangles (ctx, count);
+ }
+}
+
+static void
+_cairo_gl_composite_unmap_vertex_buffer (cairo_gl_context_t *ctx)
+{
+ ctx->vb_offset = 0;
+}
+
+void
+_cairo_gl_composite_flush (cairo_gl_context_t *ctx)
+{
+ unsigned int count;
+ int i;
+
+ if (_cairo_gl_context_is_flushed (ctx))
+ return;
+
+ count = ctx->vb_offset / ctx->vertex_size;
+ _cairo_gl_composite_unmap_vertex_buffer (ctx);
+
+ if (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS) {
+ if (ctx->draw_mode == CAIRO_GL_LINE_STRIP ||
+ ctx->draw_mode == CAIRO_GL_LINES)
+ _cairo_gl_composite_draw_line (ctx);
+ else
+ _cairo_gl_composite_draw_tristrip (ctx);
+ } else {
+ assert (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
+ _cairo_gl_composite_draw_triangles_with_clip_region (ctx, count);
+ }
+
+ for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++)
+ _cairo_gl_glyph_cache_unlock (&ctx->glyph_cache[i]);
+
+ _cairo_gl_image_cache_unlock (ctx);
+}
+
+static void
+_cairo_gl_composite_prepare_buffer (cairo_gl_context_t *ctx,
+ unsigned int n_vertices,
+ cairo_gl_primitive_type_t primitive_type)
+{
+ if (ctx->primitive_type != primitive_type) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->primitive_type = primitive_type;
+ }
+
+ if (ctx->vb_offset + n_vertices * ctx->vertex_size > CAIRO_GL_VBO_SIZE)
+ _cairo_gl_composite_flush (ctx);
+}
+
+static inline void
+_cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx,
+ GLfloat x, GLfloat y)
+{
+ GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+
+ *vb++ = x;
+ *vb++ = y;
+
+ _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
+ _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y);
+
+ ctx->vb_offset += ctx->vertex_size;
+}
+
+static inline void
+_cairo_gl_composite_emit_alpha_vertex (cairo_gl_context_t *ctx,
+ GLfloat x, GLfloat y, uint8_t alpha)
+{
+ GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+ union fi {
+ float f;
+ GLbyte bytes[4];
+ } fi;
+
+ *vb++ = x;
+ *vb++ = y;
+
+ _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
+ _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y);
+
+ fi.bytes[0] = 0;
+ fi.bytes[1] = 0;
+ fi.bytes[2] = 0;
+ fi.bytes[3] = alpha;
+ *vb++ = fi.f;
+
+ ctx->vb_offset += ctx->vertex_size;
+}
+
+static void
+_cairo_gl_composite_emit_point (cairo_gl_context_t *ctx,
+ const cairo_point_t *point)
+{
+ _cairo_gl_composite_emit_vertex (ctx,
+ _cairo_fixed_to_double (point->x),
+ _cairo_fixed_to_double (point->y));
+}
+
+static void
+_cairo_gl_composite_emit_int (cairo_gl_context_t *ctx,
+ int x, int y)
+{
+ float fx = x;
+ float fy = y;
+
+ _cairo_gl_composite_emit_vertex (ctx, fx, fy);
+}
+
+static void
+_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2)
+{
+ _cairo_gl_composite_prepare_buffer (ctx, 6,
+ CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
+
+ _cairo_gl_composite_emit_vertex (ctx, x1, y1);
+ _cairo_gl_composite_emit_vertex (ctx, x2, y1);
+ _cairo_gl_composite_emit_vertex (ctx, x1, y2);
+
+ _cairo_gl_composite_emit_vertex (ctx, x2, y1);
+ _cairo_gl_composite_emit_vertex (ctx, x2, y2);
+ _cairo_gl_composite_emit_vertex (ctx, x1, y2);
+}
+
+cairo_gl_emit_rect_t
+_cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx)
+{
+ return _cairo_gl_composite_emit_rect;
+}
+
+void
+_cairo_gl_context_emit_rect (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2)
+{
+ _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2);
+}
+
+static void
+_cairo_gl_composite_emit_span (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2,
+ uint8_t alpha)
+{
+ if (ctx->draw_mode != CAIRO_GL_VERTEX) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->draw_mode = CAIRO_GL_VERTEX;
+ }
+
+ _cairo_gl_composite_prepare_buffer (ctx, 6,
+ CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
+
+ _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y1, alpha);
+ _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha);
+ _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha);
+
+ _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha);
+ _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y2, alpha);
+ _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha);
+}
+
+static void
+_cairo_gl_composite_emit_solid_span (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2,
+ uint8_t alpha)
+{
+ GLfloat *v;
+ int src_use_atlas = 0;
+ int mask_use_atlas = 0;
+ union fi {
+ float f;
+ GLbyte bytes[4];
+ } fi;
+
+ _cairo_gl_composite_prepare_buffer (ctx, 6,
+ CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
+
+ if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE ||
+ ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_GAUSSIAN) &&
+ ctx->operands[CAIRO_GL_TEX_SOURCE].texture.use_atlas)
+ src_use_atlas = TRUE;
+ if ((ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE ||
+ ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_GAUSSIAN) &&
+ ctx->operands[CAIRO_GL_TEX_MASK].texture.use_atlas)
+ mask_use_atlas = TRUE;
+
+ v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+
+ v[15 + 20*src_use_atlas + 20*mask_use_atlas] =
+ v[ 6 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[ 0 ] = x1;
+ v[10 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[ 4 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[ 1 ] = y1;
+ v[12 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[ 9 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[ 3 + 4*src_use_atlas + 4*mask_use_atlas] = x2;
+ v[16 + 20*src_use_atlas + 20*mask_use_atlas] =
+ v[13 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[ 7 + 8*src_use_atlas + 8*mask_use_atlas] = y2;
+
+ fi.bytes[0] = 0;
+ fi.bytes[1] = 0;
+ fi.bytes[2] = 0;
+ fi.bytes[3] = alpha;
+ v[17 + 24*src_use_atlas + 24*mask_use_atlas] =
+ v[14 + 20*src_use_atlas + 20*mask_use_atlas] =
+ v[11 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[ 8 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[ 5 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[ 2 + 4*src_use_atlas + 4*mask_use_atlas ] = fi.f;
+
+ if (src_use_atlas) {
+ v[ 2 ] =
+ v[ 5 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[ 8 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[11 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[14 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[17 + 20*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_SOURCE].texture.p1.x;
+
+ v[ 3 ] =
+ v[ 6 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[ 9 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[12 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[15 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[18 + 20*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_SOURCE].texture.p1.y;
+
+ v[ 4 ] =
+ v[ 7 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[10 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[13 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[16 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[19 + 20*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_SOURCE].texture.p2.x;
+
+ v[ 5 ] =
+ v[ 8 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[11 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[14 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[17 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[20 + 20*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_SOURCE].texture.p2.y;
+ }
+
+ if (mask_use_atlas) {
+ v[ 2 + 4*src_use_atlas ] =
+ v[ 5 + 8*src_use_atlas + 4*mask_use_atlas] =
+ v[ 8 + 12*src_use_atlas + 8*mask_use_atlas] =
+ v[11 + 16*src_use_atlas + 12*mask_use_atlas] =
+ v[14 + 20*src_use_atlas + 16*mask_use_atlas] =
+ v[17 + 24*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_MASK].texture.p1.x;
+
+ v[ 3 + 4*src_use_atlas ] =
+ v[ 6 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[ 9 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[12 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[15 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[18 + 20*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_MASK].texture.p1.y;
+
+ v[ 4 + 4*src_use_atlas ] =
+ v[ 7 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[10 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[13 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[16 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[19 + 20*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_MASK].texture.p2.x;
+
+ v[ 5 + 4*src_use_atlas ] =
+ v[ 8 + 4*src_use_atlas + 4*mask_use_atlas] =
+ v[11 + 8*src_use_atlas + 8*mask_use_atlas] =
+ v[14 + 12*src_use_atlas + 12*mask_use_atlas] =
+ v[17 + 16*src_use_atlas + 16*mask_use_atlas] =
+ v[20 + 20*src_use_atlas + 20*mask_use_atlas] =
+ ctx->operands[CAIRO_GL_TEX_MASK].texture.p2.y;
+ }
+
+ ctx->vb_offset += 6*(3 + 4*src_use_atlas + 4*mask_use_atlas) * sizeof(GLfloat);
+}
+
+cairo_gl_emit_span_t
+_cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx)
+{
+ if (ctx->operands[CAIRO_GL_TEX_MASK].type != CAIRO_GL_OPERAND_NONE) {
+ switch (ctx->operands[CAIRO_GL_TEX_MASK].type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ break;
+
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ if (!ctx->operands[CAIRO_GL_TEX_MASK].gradient.texgen)
+ return _cairo_gl_composite_emit_span;
+ break;
+
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (!ctx->operands[CAIRO_GL_TEX_MASK].texture.texgen)
+ return _cairo_gl_composite_emit_span;
+ break;
+ }
+ }
+
+ switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ break;
+
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ if (!ctx->operands[CAIRO_GL_TEX_SOURCE].gradient.texgen)
+ return _cairo_gl_composite_emit_span;
+ break;
+
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (!ctx->operands[CAIRO_GL_TEX_SOURCE].texture.texgen)
+ return _cairo_gl_composite_emit_span;
+ }
+
+ return _cairo_gl_composite_emit_solid_span;
+}
+
+static inline void
+_cairo_gl_composite_emit_glyph_vertex (cairo_gl_context_t *ctx,
+ GLfloat x, GLfloat y,
+ GLfloat glyph_x, GLfloat glyph_y)
+{
+ GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+
+ *vb++ = x;
+ *vb++ = y;
+
+ _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
+
+ *vb++ = glyph_x;
+ *vb++ = glyph_y;
+
+ ctx->vb_offset += ctx->vertex_size;
+}
+
+static inline void
+_cairo_gl_composite_emit_color_glyph_vertex (cairo_gl_context_t *ctx,
+ GLfloat x, GLfloat y,
+ GLfloat glyph_x, GLfloat glyph_y)
+{
+ GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+
+ *vb++ = x;
+ *vb++ = y;
+ *vb++ = glyph_x;
+ *vb++ = glyph_y;
+
+ ctx->vb_offset += ctx->vertex_size;
+}
+
+static void
+_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2,
+ GLfloat glyph_x1, GLfloat glyph_y1,
+ GLfloat glyph_x2, GLfloat glyph_y2)
+{
+ if (ctx->draw_mode != CAIRO_GL_VERTEX) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->draw_mode = CAIRO_GL_VERTEX;
+ }
+
+ _cairo_gl_composite_prepare_buffer (ctx, 6,
+ CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
+
+ _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y1, glyph_x1, glyph_y1);
+ _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
+ _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
+
+ _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
+ _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y2, glyph_x2, glyph_y2);
+ _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
+}
+
+static void
+_cairo_gl_composite_emit_color_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)
+{
+ if (ctx->draw_mode != CAIRO_GL_VERTEX) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->draw_mode = CAIRO_GL_VERTEX;
+ }
+
+ _cairo_gl_composite_prepare_buffer (ctx, 6,
+ CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
+
+ _cairo_gl_composite_emit_color_glyph_vertex (ctx, x1, y1, glyph_x1, glyph_y1);
+ _cairo_gl_composite_emit_color_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
+ _cairo_gl_composite_emit_color_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
+
+ _cairo_gl_composite_emit_color_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
+ _cairo_gl_composite_emit_color_glyph_vertex (ctx, x2, y2, glyph_x2, glyph_y2);
+ _cairo_gl_composite_emit_color_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
+}
+
+static void
+_cairo_gl_composite_emit_solid_glyph (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2,
+ GLfloat glyph_x1, GLfloat glyph_y1,
+ GLfloat glyph_x2, GLfloat glyph_y2)
+{
+ GLfloat *v;
+
+ _cairo_gl_composite_prepare_buffer (ctx, 6,
+ CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
+
+ v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+
+ v[20] = v[ 8] = v[0] = x1;
+ v[13] = v[ 5] = v[1] = y1;
+ v[22] = v[10] = v[2] = glyph_x1;
+ v[15] = v[ 7] = v[3] = glyph_y1;
+
+ v[16] = v[12] = v[4] = x2;
+ v[18] = v[14] = v[6] = glyph_x2;
+
+ v[21] = v[17] = v[ 9] = y2;
+ v[23] = v[19] = v[11] = glyph_y2;
+
+ ctx->vb_offset += 4 * 6 * sizeof (GLfloat);
+}
+
+cairo_gl_emit_glyph_t
+_cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx,
+ const cairo_bool_t is_color_glyph)
+{
+
+ if ( is_color_glyph) {
+ /* color glyph ignore all source and mask */
+ return _cairo_gl_composite_emit_color_glyph;
+ }
+
+ switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ if (! ctx->operands[CAIRO_GL_TEX_SOURCE].constant.encode_as_attribute)
+ return _cairo_gl_composite_emit_solid_glyph;
+
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ return _cairo_gl_composite_emit_glyph;
+ }
+}
+
+void
+_cairo_gl_composite_fini (cairo_gl_composite_t *setup)
+{
+ _cairo_gl_operand_destroy (&setup->src);
+ _cairo_gl_operand_destroy (&setup->mask);
+}
+
+cairo_status_t
+_cairo_gl_composite_set_operator (cairo_gl_composite_t *setup,
+ cairo_operator_t op,
+ cairo_bool_t assume_component_alpha)
+{
+ if (assume_component_alpha) {
+ if (op != CAIRO_OPERATOR_CLEAR &&
+ op != CAIRO_OPERATOR_OVER &&
+ op != CAIRO_OPERATOR_ADD)
+ return UNSUPPORTED ("unsupported component alpha operator");
+ } else {
+ if (! _cairo_gl_operator_is_supported (op))
+ return UNSUPPORTED ("unsupported operator");
+ }
+
+ setup->op = op;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_composite_init (cairo_gl_composite_t *setup,
+ cairo_operator_t op,
+ cairo_gl_surface_t *dst,
+ cairo_bool_t assume_component_alpha)
+{
+ cairo_status_t status;
+
+ memset (setup, 0, sizeof (cairo_gl_composite_t));
+
+ status = _cairo_gl_composite_set_operator (setup, op,
+ assume_component_alpha);
+ if (status)
+ return status;
+
+ setup->dst = dst;
+ setup->clip_region = dst->clip_region;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_gl_composite_append_vertex_indices (cairo_gl_context_t *ctx,
+ int number_of_new_indices,
+ cairo_bool_t is_connected)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+ cairo_array_t *indices = &ctx->tristrip_indices;
+ int number_of_indices = _cairo_array_num_elements (indices);
+ unsigned short current_vertex_index = 0;
+ int i;
+
+ assert (number_of_new_indices > 0);
+
+ /* If any preexisting triangle triangle strip indices exist on this
+ context, we insert a set of degenerate triangles from the last
+ preexisting vertex to our first one. */
+ if (number_of_indices > 0 && is_connected) {
+ const unsigned short *indices_array = _cairo_array_index_const (indices, 0);
+ if (indices_array == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ current_vertex_index = indices_array[number_of_indices - 1];
+
+ status = _cairo_array_append (indices, &current_vertex_index);
+ if (unlikely (status))
+ return status;
+
+ current_vertex_index++;
+ status =_cairo_array_append (indices, &current_vertex_index);
+ if (unlikely (status))
+ return status;
+ } else
+ current_vertex_index = (unsigned short) number_of_indices;
+
+ for (i = 0; i < number_of_new_indices; i++) {
+ status = _cairo_array_append (indices, &current_vertex_index);
+ current_vertex_index++;
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_gl_composite_emit_int_quad_as_tristrip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const int quad[8])
+{
+ if (ctx->draw_mode != CAIRO_GL_VERTEX) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->draw_mode = CAIRO_GL_VERTEX;
+ }
+
+ _cairo_gl_composite_prepare_buffer (ctx, 4,
+ CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS);
+
+ _cairo_gl_composite_emit_int (ctx, quad[0], quad[1]);
+ _cairo_gl_composite_emit_int (ctx, quad[2], quad[3]);
+
+ /* Cairo stores quad vertices in counter-clockwise order, but we need to
+ emit them from top to bottom in the triangle strip, so we need to reverse
+ the order of the last two vertices. */
+ _cairo_gl_composite_emit_int (ctx, quad[6], quad[7]);
+ _cairo_gl_composite_emit_int (ctx, quad[4], quad[5]);
+
+ return _cairo_gl_composite_append_vertex_indices (ctx, 4, TRUE);
+}
+
+cairo_int_status_t
+_cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const cairo_point_t quad[4])
+{
+ if (ctx->draw_mode != CAIRO_GL_VERTEX) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->draw_mode = CAIRO_GL_VERTEX;
+ }
+
+ _cairo_gl_composite_prepare_buffer (ctx, 4,
+ CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS);
+
+ _cairo_gl_composite_emit_point (ctx, &quad[0]);
+ _cairo_gl_composite_emit_point (ctx, &quad[1]);
+
+ /* Cairo stores quad vertices in counter-clockwise order, but we need to
+ emit them from top to bottom in the triangle strip, so we need to reverse
+ the order of the last two vertices. */
+ _cairo_gl_composite_emit_point (ctx, &quad[3]);
+ _cairo_gl_composite_emit_point (ctx, &quad[2]);
+
+ return _cairo_gl_composite_append_vertex_indices (ctx, 4, TRUE);
+}
+
+cairo_int_status_t
+_cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const cairo_point_t triangle[3])
+{
+ if (ctx->draw_mode != CAIRO_GL_VERTEX) {
+ _cairo_gl_composite_flush (ctx);
+ ctx->draw_mode = CAIRO_GL_VERTEX;
+ }
+
+ _cairo_gl_composite_prepare_buffer (ctx, 3,
+ CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS);
+
+ _cairo_gl_composite_emit_point (ctx, &triangle[0]);
+ _cairo_gl_composite_emit_point (ctx, &triangle[1]);
+ _cairo_gl_composite_emit_point (ctx, &triangle[2]);
+ return _cairo_gl_composite_append_vertex_indices (ctx, 3, TRUE);
+}
+
+cairo_int_status_t
+_cairo_gl_composite_emit_point_as_single_line (cairo_gl_context_t *ctx,
+ const cairo_point_t point[2])
+{
+ int num_indices = 2;
+ if (ctx->draw_mode != CAIRO_GL_LINES)
+ _cairo_gl_composite_flush (ctx);
+
+ ctx->draw_mode = CAIRO_GL_LINES;
+
+ _cairo_gl_composite_prepare_buffer (ctx, 2,
+ CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS);
+
+ _cairo_gl_composite_emit_point (ctx, &point[0]);
+ _cairo_gl_composite_emit_point (ctx, &point[1]);
+ return _cairo_gl_composite_append_vertex_indices (ctx, num_indices, FALSE);
+}
diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c
new file mode 100755
index 000000000..a22ac63bb
--- /dev/null
+++ b/src/cairo-gl-device.c
@@ -0,0 +1,1092 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-gl-private.h"
+#include "cairo-rtree-private.h"
+
+#if CAIRO_HAS_EVASGL_SURFACE
+#include "cairo-evas-gl.h"
+#endif
+
+#define MAX_MSAA_SAMPLES 4
+
+cairo_int_status_t
+_cairo_gl_image_cache_init (cairo_gl_context_t *ctx, int width, int height,
+ cairo_gl_image_cache_t **image_cache)
+{
+ cairo_surface_t *cache_surface = _cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
+ if (unlikely (cache_surface->status)) {
+ cairo_surface_destroy (cache_surface);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ _cairo_surface_release_device_reference (cache_surface);
+ *image_cache = _cairo_malloc (sizeof (cairo_gl_image_cache_t));
+ if (*image_cache == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ (*image_cache)->surface = (cairo_gl_surface_t *)cache_surface;
+ (*image_cache)->surface->supports_msaa = FALSE;
+
+ _cairo_rtree_init (&((*image_cache)->rtree), width, height,
+ IMAGE_CACHE_MIN_SIZE,
+ sizeof (cairo_gl_image_t),
+ _cairo_gl_image_node_destroy);
+
+ (*image_cache)->copy_success = TRUE;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_image_cache_fini (cairo_gl_context_t *ctx)
+{
+ if (ctx->image_cache) {
+ _cairo_rtree_fini (&ctx->image_cache->rtree);
+ cairo_surface_destroy (&ctx->image_cache->surface->base);
+ }
+ free (ctx->image_cache);
+}
+
+static void
+_gl_lock (void *device)
+{
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
+
+ ctx->acquire (ctx);
+}
+
+static void
+_gl_unlock (void *device)
+{
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
+
+ ctx->release (ctx);
+}
+
+static cairo_status_t
+_gl_flush (void *device)
+{
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+
+ status = _cairo_gl_context_acquire (device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ _cairo_gl_composite_flush (ctx);
+
+ _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
+ _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
+
+ if (ctx->clip_region) {
+ cairo_region_destroy (ctx->clip_region);
+ ctx->clip_region = NULL;
+ }
+
+ ctx->current_target = NULL;
+ ctx->current_operator = -1;
+ ctx->vertex_size = 0;
+ ctx->pre_shader = NULL;
+ _cairo_gl_set_shader (ctx, NULL);
+
+ ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, 0);
+
+ _cairo_gl_context_reset (ctx);
+
+ _disable_scissor_buffer (ctx);
+
+ if (ctx->states_cache.blend_enabled == TRUE ) {
+ ctx->dispatch.Disable (GL_BLEND);
+ ctx->states_cache.blend_enabled = FALSE;
+ }
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+static void
+_gl_finish (void *device)
+{
+ cairo_gl_context_t *ctx = device;
+ int n;
+
+ _gl_lock (device);
+
+ _cairo_cache_fini (&ctx->gradients);
+
+ _cairo_gl_context_fini_shaders (ctx);
+
+ for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+ _cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]);
+
+
+ _cairo_gl_image_cache_fini (ctx);
+
+ _gl_unlock (device);
+}
+
+static void
+_gl_destroy (void *device)
+{
+ int n;
+
+ cairo_gl_context_t *ctx = device;
+
+ ctx->acquire (ctx);
+
+ if(ctx->glyph_mask) {
+ cairo_surface_destroy (&ctx->glyph_mask->base);
+ ctx->glyph_mask = NULL;
+ }
+
+ for (n = 0; n < 2; n++) {
+ if (ctx->source_scratch_surfaces[n])
+ cairo_surface_destroy (&ctx->source_scratch_surfaces[n]->base);
+ if (ctx->mask_scratch_surfaces[n])
+ cairo_surface_destroy (&ctx->mask_scratch_surfaces[n]->base);
+ if (ctx->shadow_scratch_surfaces[n])
+ cairo_surface_destroy (&ctx->shadow_scratch_surfaces[n]->base);
+ }
+ if (ctx->shadow_scratch_surfaces[2])
+ cairo_surface_destroy (&ctx->shadow_scratch_surfaces[2]->base);
+
+ for (n = 0; n < 4; n++) {
+ if (ctx->shadow_masks[n])
+ cairo_surface_destroy (&ctx->shadow_masks[n]->base);
+ }
+
+ while (! cairo_list_is_empty (&ctx->fonts)) {
+ cairo_gl_font_t *font;
+
+ font = cairo_list_first_entry (&ctx->fonts,
+ cairo_gl_font_t,
+ link);
+
+ cairo_list_del (&font->base.link);
+ cairo_list_del (&font->link);
+ free (font);
+ }
+
+ _cairo_array_fini (&ctx->tristrip_indices);
+
+ cairo_region_destroy (ctx->clip_region);
+
+ free (ctx->vb);
+
+ ctx->destroy (ctx);
+
+ free (ctx);
+}
+
+static const cairo_device_backend_t _cairo_gl_device_backend = {
+ CAIRO_DEVICE_TYPE_GL,
+
+ _gl_lock,
+ _gl_unlock,
+
+ _gl_flush, /* flush */
+ _gl_finish,
+ _gl_destroy,
+};
+
+static cairo_bool_t
+_cairo_gl_msaa_compositor_enabled (void)
+{
+ const char *env = getenv ("CAIRO_GL_COMPOSITOR");
+ return env && strcmp(env, "msaa") == 0;
+}
+
+static cairo_bool_t
+test_can_read_bgra (cairo_gl_context_t *ctx, cairo_gl_flavor_t gl_flavor)
+{
+ /* Desktop GL always supports BGRA formats. */
+ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return TRUE;
+
+ assert (gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ gl_flavor == CAIRO_GL_FLAVOR_ES3);
+
+ /* For OpenGL ES we have to look for the specific extension and BGRA only
+ * matches cairo's integer packed bytes on little-endian machines. */
+ if (!_cairo_is_little_endian())
+ return FALSE;
+ return _cairo_gl_has_extension (&ctx->dispatch, "EXT_read_format_bgra");
+}
+
+cairo_status_t
+_cairo_gl_context_init (cairo_gl_context_t *ctx)
+{
+ cairo_status_t status;
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ int gl_version = _cairo_gl_get_version (dispatch);
+ cairo_gl_flavor_t gl_flavor = _cairo_gl_get_flavor (dispatch);
+ int n;
+
+ cairo_bool_t is_desktop = gl_flavor == CAIRO_GL_FLAVOR_DESKTOP;
+ cairo_bool_t is_gles = (gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ gl_flavor == CAIRO_GL_FLAVOR_ES3);
+
+ _cairo_device_init (&ctx->base, &_cairo_gl_device_backend);
+
+ /* XXX The choice of compositor should be made automatically at runtime.
+ * However, it is useful to force one particular compositor whilst
+ * testing.
+ */
+ if (_cairo_gl_msaa_compositor_enabled ())
+ ctx->compositor = _cairo_gl_msaa_compositor_get ();
+ else
+ ctx->compositor = _cairo_gl_span_compositor_get ();
+
+
+ ctx->thread_aware = TRUE;
+ ctx->has_angle_multisampling = FALSE;
+
+ memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
+ cairo_list_init (&ctx->fonts);
+
+ /* Support only GL version >= 1.3 */
+ if (gl_version < CAIRO_GL_VERSION_ENCODE (1, 3))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ /* Check for required extensions */
+ if (is_desktop) {
+ if (_cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_texture_non_power_of_two")) {
+ ctx->tex_target = GL_TEXTURE_2D;
+ ctx->has_npot_repeat = TRUE;
+ } else if (_cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_texture_rectangle")) {
+ ctx->tex_target = GL_TEXTURE_RECTANGLE;
+ ctx->has_npot_repeat = FALSE;
+ } else
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+ } else {
+ ctx->tex_target = GL_TEXTURE_2D;
+ if (_cairo_gl_has_extension (&ctx->dispatch, "GL_OES_texture_npot") ||
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_IMG_texture_npot"))
+ ctx->has_npot_repeat = TRUE;
+ else
+ ctx->has_npot_repeat = FALSE;
+ }
+
+ if (is_desktop && gl_version < CAIRO_GL_VERSION_ENCODE (2, 1) &&
+ ! _cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_pixel_buffer_object"))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ if (is_gles && ! _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_texture_format_BGRA8888"))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ ctx->has_map_buffer =
+ is_desktop || (is_gles && _cairo_gl_has_extension (&ctx->dispatch, "GL_OES_mapbuffer"));
+
+ ctx->can_read_bgra = test_can_read_bgra (ctx, gl_flavor);
+
+ ctx->has_mesa_pack_invert =
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_MESA_pack_invert");
+
+ ctx->has_packed_depth_stencil =
+ (is_desktop && _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_packed_depth_stencil")) ||
+ (is_gles && _cairo_gl_has_extension (&ctx->dispatch, "GL_OES_packed_depth_stencil"));
+
+ ctx->num_samples = 1;
+ ctx->msaa_type = CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE;
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (is_desktop && ctx->has_packed_depth_stencil &&
+ (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) ||
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_framebuffer_object") ||
+ (_cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_framebuffer_blit") &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_framebuffer_multisample")))) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples);
+ }
+#endif
+
+#if (CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE) && GL_MAX_SAMPLES_EXT
+ if (is_gles && ctx->has_packed_depth_stencil &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_multisampled_render_to_texture")) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples);
+ ctx->msaa_type = CAIRO_GL_EXT_MULTISAMPLE_TO_TEXTURE;
+ }
+#endif
+
+#if (CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE) && GL_MAX_SAMPLES_IMG
+ if (ctx->msaa_type == CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE &&
+ is_gles &&
+ ctx->has_packed_depth_stencil &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_IMG_multisampled_render_to_texture")) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_IMG, &ctx->num_samples);
+ ctx->msaa_type = CAIRO_GL_IMG_MULTISAMPLE_TO_TEXTURE;
+ }
+#endif
+
+#if (CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE) && GL_MAX_SAMPLES_ANGLE
+ if (ctx->msaa_type == CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE &&
+ is_gles &&
+ ctx->has_packed_depth_stencil &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ANGLE_framebuffer_blit") &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ANGLE_framebuffer_multisample")) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_ANGLE, &ctx->num_samples);
+ ctx->has_angle_multisampling = TRUE;
+ }
+#endif
+
+#if CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->msaa_type == CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE &&
+ is_gles && ctx->has_packed_depth_stencil) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES, &ctx->num_samples);
+ /* this is work around for evasgl. At this moment, if
+ we still get samples == 1, it means gles2 does not have any
+ support for extensions we have supported
+ */
+ if (gl_flavor == CAIRO_GL_FLAVOR_ES2)
+ ctx->num_samples = 1;
+ }
+#endif
+
+ /* we always use renderbuffer for rendering in glesv3 */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 ||
+ (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ctx->has_angle_multisampling))
+ ctx->supports_msaa = TRUE;
+ else
+ ctx->supports_msaa = ctx->num_samples > 1;
+ if (ctx->num_samples > MAX_MSAA_SAMPLES)
+ ctx->num_samples = MAX_MSAA_SAMPLES;
+
+
+ ctx->current_operator = -1;
+ ctx->gl_flavor = gl_flavor;
+
+ status = _cairo_gl_context_init_shaders (ctx);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_cache_init (&ctx->gradients,
+ _cairo_gl_gradient_equal,
+ NULL,
+ (cairo_destroy_func_t) _cairo_gl_gradient_destroy,
+ CAIRO_GL_GRADIENT_CACHE_SIZE);
+ if (unlikely (status))
+ return status;
+
+ ctx->vb = malloc (CAIRO_GL_VBO_SIZE);
+ if (unlikely (ctx->vb == NULL)) {
+ _cairo_cache_fini (&ctx->gradients);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ ctx->primitive_type = CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES;
+ _cairo_array_init (&ctx->tristrip_indices, sizeof (unsigned short));
+
+ /* PBO for any sort of texture upload */
+ dispatch->GenBuffers (1, &ctx->texture_load_pbo);
+
+ ctx->max_framebuffer_size = 0;
+ ctx->dispatch.GetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
+ ctx->max_texture_size = 0;
+ ctx->dispatch.GetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+ ctx->max_textures = 0;
+ ctx->dispatch.GetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &ctx->max_textures);
+
+ for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+ _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
+
+ ctx->image_cache = NULL;
+
+ for (n = 0; n < 2; n++) {
+ ctx->source_scratch_surfaces[n] = NULL;
+ ctx->mask_scratch_surfaces[n] = NULL;
+ ctx->shadow_scratch_surfaces[n] = NULL;
+ }
+
+ for (n = 0; n < 4; n++)
+ ctx->shadow_masks[n] = NULL;
+
+ ctx->source_scratch_in_use = FALSE;
+
+ _cairo_gl_context_reset (ctx);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_context_activate (cairo_gl_context_t *ctx,
+ cairo_gl_tex_t tex_unit)
+{
+ if (ctx->max_textures <= (GLint) tex_unit) {
+ if (tex_unit < 2) {
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1);
+ }
+ if (ctx->states_cache.active_texture != ctx->max_textures - 1) {
+ ctx->dispatch.ActiveTexture (ctx->max_textures - 1);
+ ctx->states_cache.active_texture = ctx->max_textures - 1;
+ }
+ } else {
+ if (ctx->states_cache.active_texture != GL_TEXTURE0 + tex_unit) {
+ ctx->dispatch.ActiveTexture (GL_TEXTURE0 + tex_unit);
+ ctx->states_cache.active_texture = GL_TEXTURE0 + tex_unit;
+ }
+ }
+}
+
+static GLenum
+_get_depth_stencil_format (cairo_gl_context_t *ctx)
+{
+ /* This is necessary to properly handle the situation where both
+ OpenGL and OpenGLES are active and returning a sane default. */
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return GL_DEPTH_STENCIL;
+#endif
+
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return GL_DEPTH24_STENCIL8_OES;
+#endif
+
+#if CAIRO_HAS_GL_SURFACE
+ return GL_DEPTH_STENCIL;
+#elif CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ return GL_DEPTH24_STENCIL8_OES;
+#elif CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ return GL_DEPTH24_STENCIL8;
+#endif
+}
+
+static void
+_cairo_gl_clear_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return;
+
+ if (_cairo_gl_surface_is_scratch (ctx, surface)) {
+ _disable_scissor_buffer (ctx);
+ _disable_stencil_buffer (ctx);
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ }
+}
+
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+static void
+_cairo_gl_ensure_msaa_gles_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ if (ctx->has_angle_multisampling)
+ return;
+
+ if (surface->msaa_active)
+ return;
+
+ ctx->dispatch.FramebufferTexture2DMultisample(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ ctx->tex_target,
+ surface->tex,
+ 0,
+ ctx->num_samples);
+
+ /* From now on MSAA will always be active on this surface. */
+ surface->msaa_active = TRUE;
+}
+#endif
+
+void
+_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ GLenum status;
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+ if (likely (surface->fb))
+ return;
+
+ /* Create a framebuffer object wrapping the texture so that we can render
+ * to it.
+ */
+ dispatch->GenFramebuffers (1, &surface->fb);
+ dispatch->BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+
+ /* Unlike for desktop GL we only maintain one multisampling framebuffer
+ for OpenGLES since the EXT_multisampled_render_to_texture extension
+ does not require an explicit multisample resolution. */
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (surface->supports_msaa && _cairo_gl_msaa_compositor_enabled () &&
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling) {
+ _cairo_gl_ensure_msaa_gles_framebuffer (ctx, surface);
+ } else
+#endif
+ dispatch->FramebufferTexture2D (GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ ctx->tex_target,
+ surface->tex,
+ 0);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP &&
+ ctx->dispatch.DrawBuffer &&
+ ctx->dispatch.ReadBuffer) {
+ ctx->dispatch.DrawBuffer (GL_COLOR_ATTACHMENT0);
+ ctx->dispatch.ReadBuffer (GL_COLOR_ATTACHMENT0);
+ }
+#endif
+
+ status = dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ const char *str;
+ switch (status) {
+ //case GL_FRAMEBUFFER_UNDEFINED: str= "undefined"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: str= "incomplete attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: str= "incomplete/missing attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: str= "incomplete draw buffer"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: str= "incomplete read buffer"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: str= "unsupported"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: str= "incomplete multiple"; break;
+ default: str = "unknown error"; break;
+ }
+
+ fprintf (stderr,
+ "destination is framebuffer incomplete: %s [%#x]\n",
+ str, status);
+ }
+}
+
+static void
+_cairo_gl_ensure_multisampling (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ GLenum rgba;
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling)
+ return;
+
+ assert (surface->supports_msaa);
+
+ if (surface->msaa_fb)
+ return;
+
+ /* We maintain a separate framebuffer for multisampling operations.
+ This allows us to do a fast paint to the non-multisampling framebuffer
+ when mulitsampling is disabled. */
+ ctx->dispatch.GenFramebuffers (1, &surface->msaa_fb);
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
+ ctx->dispatch.GenRenderbuffers (1, &surface->msaa_rb);
+ ctx->dispatch.BindRenderbuffer (GL_RENDERBUFFER, surface->msaa_rb);
+
+ /* FIXME: For now we assume that textures passed from the outside have GL_RGBA
+ format, but eventually we need to expose a way for the API consumer to pass
+ this information. */
+#if CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE
+ rgba = GL_RGBA8;
+#else
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ rgba = GL_RGBA;
+ else
+ rgba = GL_RGBA8;
+#endif
+#else
+ rgba = GL_RGBA;
+#endif
+ ctx->dispatch.RenderbufferStorageMultisample (GL_RENDERBUFFER,
+ ctx->num_samples,
+ rgba,
+ surface->width,
+ surface->height);
+
+ ctx->dispatch.FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ surface->msaa_rb);
+
+ if (ctx->dispatch.CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb);
+ surface->msaa_rb = 0;
+ ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_fb);
+ surface->msaa_fb = 0;
+ return;
+ }
+
+ /* Cairo surfaces start out initialized to transparent (black) */
+ _disable_scissor_buffer (ctx);
+ ctx->dispatch.ClearColor (0, 0, 0, 0);
+ // reset cached clear colors
+ memset (&ctx->states_cache.clear_red, 0, sizeof (GLclampf) * 4);
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT);
+}
+
+static cairo_bool_t
+_cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ if (surface->msaa_depth_stencil)
+ return TRUE;
+
+ _cairo_gl_ensure_multisampling (ctx, surface);
+
+ dispatch->GenRenderbuffers (1, &surface->msaa_depth_stencil);
+ dispatch->BindRenderbuffer (GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+
+ dispatch->RenderbufferStorageMultisample (GL_RENDERBUFFER,
+ ctx->num_samples,
+ _get_depth_stencil_format (ctx),
+ surface->width,
+ surface->height);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) {
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+ }
+#endif
+
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) {
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+ }
+#endif
+
+ if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ dispatch->DeleteRenderbuffers (1, &surface->msaa_depth_stencil);
+ surface->msaa_depth_stencil = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_gl_ensure_depth_stencil_buffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+ if (surface->depth_stencil)
+ return TRUE;
+
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+
+ dispatch->GenRenderbuffers (1, &surface->depth_stencil);
+ dispatch->BindRenderbuffer (GL_RENDERBUFFER, surface->depth_stencil);
+ dispatch->RenderbufferStorage (GL_RENDERBUFFER,
+ _get_depth_stencil_format (ctx),
+ surface->width, surface->height);
+
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, surface->depth_stencil);
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, surface->depth_stencil);
+ if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ dispatch->DeleteRenderbuffers (1, &surface->depth_stencil);
+ surface->depth_stencil = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ if (! _cairo_gl_surface_is_texture (surface))
+ return TRUE; /* best guess for now, will check later */
+ if (! ctx->has_packed_depth_stencil)
+ return FALSE;
+
+ if (surface->msaa_active)
+ return _cairo_gl_ensure_msaa_depth_stencil_buffer (ctx, surface);
+ else
+ return _cairo_gl_ensure_depth_stencil_buffer (ctx, surface);
+}
+
+/*
+ * Stores a parallel projection transformation in matrix 'm',
+ * using column-major order.
+ *
+ * This is equivalent to:
+ *
+ * glLoadIdentity()
+ * gluOrtho2D()
+ *
+ * The calculation for the ortho tranformation was taken from the
+ * mesa source code.
+ */
+static void
+_gl_identity_ortho (GLfloat *m,
+ GLfloat left, GLfloat right,
+ GLfloat bottom, GLfloat top)
+{
+#define M(row,col) m[col*4+row]
+ M(0,0) = 2.f / (right - left);
+ M(0,1) = 0.f;
+ M(0,2) = 0.f;
+ M(0,3) = -(right + left) / (right - left);
+
+ M(1,0) = 0.f;
+ M(1,1) = 2.f / (top - bottom);
+ M(1,2) = 0.f;
+ M(1,3) = -(top + bottom) / (top - bottom);
+
+ M(2,0) = 0.f;
+ M(2,1) = 0.f;
+ M(2,2) = -1.f;
+ M(2,3) = 0.f;
+
+ M(3,0) = 0.f;
+ M(3,1) = 0.f;
+ M(3,2) = 0.f;
+ M(3,3) = 1.f;
+#undef M
+}
+
+static void
+bind_multisample_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ cairo_bool_t stencil_test_enabled, scissor_test_enabled;
+ cairo_bool_t has_stencil_cache;
+ GLbitfield mask;
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ stencil_test_enabled = ctx->states_cache.stencil_test_enabled;
+ scissor_test_enabled = ctx->states_cache.scissor_test_enabled;
+
+ has_stencil_cache = surface->clip_on_stencil_buffer ? TRUE : FALSE;
+ mask = GL_COLOR_BUFFER_BIT;
+ }
+#endif
+ assert (surface->supports_msaa);
+
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+ _cairo_gl_ensure_multisampling (ctx, surface);
+
+ if (surface->msaa_active) {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ ctx->dispatch.Enable (GL_MULTISAMPLE);
+
+#endif
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
+ return;
+ }
+
+ _cairo_gl_composite_flush (ctx);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ /* we must disable scissor and stencil test */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ _disable_stencil_buffer (ctx);
+ _disable_scissor_buffer (ctx);
+
+ ctx->dispatch.Enable (GL_MULTISAMPLE);
+
+ if (has_stencil_cache)
+ mask |= GL_STENCIL_BUFFER_BIT;
+
+ /* The last time we drew to the surface, we were not using multisampling,
+ so we need to blit from the non-multisampling framebuffer into the
+ multisampling framebuffer. */
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->msaa_fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->fb);
+ ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height,
+ 0, 0, surface->width, surface->height,
+ mask, GL_NEAREST);
+ surface->content_synced = TRUE;
+ }
+#endif
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ /* re-enable stencil and scissor test */
+ if (scissor_test_enabled)
+ _enable_scissor_buffer (ctx);
+ if (stencil_test_enabled)
+ _enable_stencil_buffer (ctx);
+ }
+#endif
+}
+
+static void
+bind_singlesample_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ cairo_bool_t has_stencil_cache = surface->clip_on_stencil_buffer ? TRUE : FALSE;
+ cairo_bool_t stencil_test_enabled = ctx->states_cache.stencil_test_enabled;
+ cairo_bool_t scissor_test_enabled = ctx->states_cache.scissor_test_enabled;
+ GLbitfield mask = GL_COLOR_BUFFER_BIT;
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling)
+ return;
+
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+
+ if (! surface->msaa_active) {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_FLAVOR
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ ctx->dispatch.Disable (GL_MULTISAMPLE);
+#endif
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+ return;
+ }
+
+ _cairo_gl_composite_flush (ctx);
+
+ /* we must disable scissor and stencil test */
+ _disable_stencil_buffer (ctx);
+ _disable_scissor_buffer (ctx);
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_FLAVOR
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ ctx->dispatch.Disable (GL_MULTISAMPLE);
+#endif
+
+ if (has_stencil_cache)
+ mask |= GL_STENCIL_BUFFER_BIT;
+
+ /* The last time we drew to the surface, we were using multisampling,
+ so we need to blit from the multisampling framebuffer into the
+ non-multisampling framebuffer. */
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) {
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER_ANGLE, surface->fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER_ANGLE, surface->msaa_fb);
+ }
+#if CAIRO_HAS_EVASGL_SURFACE
+ else {
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->msaa_fb);
+ }
+#endif
+#else
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->msaa_fb);
+#endif
+ ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height,
+ 0, 0, surface->width, surface->height,
+ mask, GL_NEAREST);
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+
+ surface->content_synced = TRUE;
+ /* re-enable stencil and scissor test */
+ if (scissor_test_enabled)
+ _enable_scissor_buffer (ctx);
+ if (stencil_test_enabled)
+ _enable_stencil_buffer (ctx);
+}
+
+void
+_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface,
+ cairo_bool_t multisampling)
+{
+ if (_cairo_gl_surface_is_texture (surface)) {
+ /* OpenGL ES surfaces only have either a multisample framebuffer or a
+ * singlesample framebuffer, so we cannot switch back and forth. */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling) {
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+ _cairo_gl_clear_framebuffer (ctx, surface);
+ return;
+ }
+
+ if (multisampling)
+ bind_multisample_framebuffer (ctx, surface);
+ else
+ bind_singlesample_framebuffer (ctx, surface);
+ } else {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, 0);
+#endif
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ if (multisampling)
+ ctx->dispatch.Enable (GL_MULTISAMPLE);
+ else
+ ctx->dispatch.Disable (GL_MULTISAMPLE);
+ }
+#endif
+ }
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 ||
+ (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ctx->has_angle_multisampling))
+ surface->msaa_active = multisampling;
+
+ if (ctx->gl_flavor != CAIRO_GL_FLAVOR_DESKTOP && multisampling)
+ _cairo_gl_clear_framebuffer (ctx, surface);
+}
+
+void
+_cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface,
+ cairo_bool_t multisampling)
+{
+ cairo_bool_t changing_surface, changing_sampling;
+
+ /* The decision whether or not to use multisampling happens when
+ * we create an OpenGL ES surface, so we can never switch modes. */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling)
+ multisampling = surface->msaa_active;
+
+ changing_surface = ctx->current_target != surface || surface->size_changed;
+ changing_sampling = surface->msaa_active != multisampling;
+
+ if (! changing_surface && ! changing_sampling) {
+ if (surface->needs_update)
+ _cairo_gl_composite_flush (ctx);
+ return;
+ }
+ if (! changing_surface) {
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling);
+ return;
+ }
+
+ _cairo_gl_composite_flush (ctx);
+
+ ctx->current_target = surface;
+ surface->needs_update = FALSE;
+
+ if (! _cairo_gl_surface_is_texture (surface)) {
+ ctx->make_current (ctx, surface);
+ }
+
+ _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling);
+
+ if (! _cairo_gl_surface_is_texture (surface)) {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP &&
+ ctx->dispatch.DrawBuffer &&
+ ctx->dispatch.ReadBuffer) {
+ ctx->dispatch.DrawBuffer (GL_BACK_LEFT);
+ ctx->dispatch.ReadBuffer (GL_BACK_LEFT);
+ }
+#endif
+ }
+
+ ctx->dispatch.Disable (GL_DITHER);
+ if (ctx->states_cache.viewport_box.width != surface->width ||
+ ctx->states_cache.viewport_box.height != surface->height) {
+ ctx->dispatch.Viewport (0, 0, surface->width, surface->height);
+ ctx->states_cache.viewport_box.width = surface->width;
+ ctx->states_cache.viewport_box.height = surface->height;
+ }
+
+ if (_cairo_gl_surface_is_texture (surface))
+ _gl_identity_ortho (ctx->modelviewprojection_matrix,
+ 0, surface->width, 0, surface->height);
+ else
+ _gl_identity_ortho (ctx->modelviewprojection_matrix,
+ 0, surface->width, surface->height, 0);
+}
+
+void
+cairo_gl_device_set_thread_aware (cairo_device_t *device,
+ cairo_bool_t thread_aware)
+{
+ if ((! device)||(cairo_device_status(device)!= CAIRO_STATUS_SUCCESS)) {
+ fprintf (stderr, "cairo_gl_device_set_thread_aware(): cairo_device is NULL or not available\n");
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR);
+ return;
+ }
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return;
+ }
+ if(thread_aware == 0 || thread_aware == 1){
+ ((cairo_gl_context_t *) device)->thread_aware = thread_aware;
+ }
+ else{
+ _cairo_device_set_error (device, CAIRO_STATUS_INVALID_STATUS);
+ return;
+ }
+}
+
+void _cairo_gl_context_reset (cairo_gl_context_t *ctx)
+{
+ ctx->states_cache.viewport_box.width = 0;
+ ctx->states_cache.viewport_box.height = 0;
+
+ ctx->states_cache.clear_red = -1;
+ ctx->states_cache.clear_green = -1;
+ ctx->states_cache.clear_blue = -1;
+ ctx->states_cache.clear_alpha = -1;
+
+ ctx->states_cache.blend_enabled = FALSE;
+
+ ctx->states_cache.src_color_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+ ctx->states_cache.dst_color_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+ ctx->states_cache.src_alpha_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+ ctx->states_cache.dst_alpha_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+
+ ctx->states_cache.active_texture = CAIRO_GL_ENUM_UNINITIALIZED;
+
+ ctx->states_cache.depth_mask = FALSE;
+
+ /* FIXME: this is hack to fix mali driver*/
+ ctx->dispatch.Disable (GL_DITHER);
+
+ ctx->current_shader = NULL;
+}
diff --git a/src/cairo-gl-dispatch-private.h b/src/cairo-gl-dispatch-private.h
new file mode 100755
index 000000000..5996ebdc7
--- /dev/null
+++ b/src/cairo-gl-dispatch-private.h
@@ -0,0 +1,192 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#ifndef CAIRO_GL_DISPATCH_PRIVATE_H
+#define CAIRO_GL_DISPATCH_PRIVATE_H
+
+#include "cairo-gl-private.h"
+#include <stddef.h>
+
+typedef enum _cairo_gl_dispatch_name {
+ CAIRO_GL_DISPATCH_NAME_CORE,
+ CAIRO_GL_DISPATCH_NAME_EXT,
+ CAIRO_GL_DISPATCH_NAME_ES,
+ CAIRO_GL_DISPATCH_NAME_COUNT
+} cairo_gl_dispatch_name_t;
+
+typedef struct _cairo_gl_dispatch_entry {
+ const char *name[CAIRO_GL_DISPATCH_NAME_COUNT];
+ size_t offset;
+} cairo_gl_dispatch_entry_t;
+
+#define DISPATCH_ENTRY_CORE(name) { { "gl"#name, "gl"#name"", "gl"#name }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+
+#define DISPATCH_ENTRY_ARB(name) { { "gl"#name, "gl"#name"ARB", "gl"#name }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_EXT(name) { { "gl"#name, "gl"#name"EXT", "gl"#name }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_ARB_OES(name) { { "gl"#name, "gl"#name"ARB", "gl"#name"OES" }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_ES_ANGLE_EXT_IMG(name) { { "gl"#name"ANGLE", "gl"#name"EXT", "gl"#name"IMG" }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_ANGLE_IMG(name) { { "gl"#name, "gl"#name"ANGLE", "gl"#name"IMG" }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_EXT_IMG(name) { { "gl"#name, "gl"#name"EXT", "gl"#name"IMG" }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_CUSTOM(name, name2) { { "gl"#name, "gl"#name2, "gl"#name }, \
+ offsetof(cairo_gl_dispatch_t, name)}
+#define DISPATCH_ENTRY_EXT_ANGLE(name) { { "gl"#name, "gl"#name"EXT", "gl"#name"ANGLE" }, \
+ offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_LAST { { NULL, NULL, NULL }, 0 }
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_core_entries[] = {
+ DISPATCH_ENTRY_CORE (ActiveTexture),
+ DISPATCH_ENTRY_CORE (BindTexture),
+ DISPATCH_ENTRY_CORE (BlendFunc),
+ DISPATCH_ENTRY_CORE (BlendFuncSeparate),
+ DISPATCH_ENTRY_CORE (Clear),
+ DISPATCH_ENTRY_CORE (ClearColor),
+ DISPATCH_ENTRY_CORE (ClearStencil),
+ DISPATCH_ENTRY_CORE (ColorMask),
+ DISPATCH_ENTRY_CORE (DeleteTextures),
+ DISPATCH_ENTRY_CORE (Disable),
+ DISPATCH_ENTRY_CORE (DrawArrays),
+ DISPATCH_ENTRY_CORE (DrawElements),
+ DISPATCH_ENTRY_CORE (Enable),
+ DISPATCH_ENTRY_CORE (Flush),
+ DISPATCH_ENTRY_CORE (GenTextures),
+ DISPATCH_ENTRY_CORE (GetBooleanv),
+ DISPATCH_ENTRY_CORE (GetError),
+ DISPATCH_ENTRY_CORE (GetFloatv),
+ DISPATCH_ENTRY_CORE (GetIntegerv),
+ DISPATCH_ENTRY_CORE (GetString),
+ DISPATCH_ENTRY_CORE (PixelStorei),
+ DISPATCH_ENTRY_CORE (ReadPixels),
+ DISPATCH_ENTRY_CORE (Scissor),
+ DISPATCH_ENTRY_CORE (StencilFunc),
+ DISPATCH_ENTRY_CORE (StencilMask),
+ DISPATCH_ENTRY_CORE (StencilOp),
+ DISPATCH_ENTRY_CORE (TexSubImage2D),
+ DISPATCH_ENTRY_CORE (TexImage2D),
+ DISPATCH_ENTRY_CORE (TexParameteri),
+#if defined(CAIRO_HAS_GL_SURFACE) || defined(CAIRO_HAS_EVASGL_SURFACE)
+ DISPATCH_ENTRY_CORE (DrawBuffer),
+ DISPATCH_ENTRY_CORE (ReadBuffer),
+#endif
+ DISPATCH_ENTRY_CORE (DepthMask),
+ DISPATCH_ENTRY_CORE (Viewport),
+ DISPATCH_ENTRY_LAST
+};
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_buffers_entries[] = {
+ DISPATCH_ENTRY_ARB (GenBuffers),
+ DISPATCH_ENTRY_ARB (BindBuffer),
+ DISPATCH_ENTRY_ARB (BufferData),
+ DISPATCH_ENTRY_ARB_OES (MapBuffer),
+ DISPATCH_ENTRY_ARB_OES (UnmapBuffer),
+ DISPATCH_ENTRY_LAST
+};
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_shaders_entries[] = {
+ /* Shaders */
+ DISPATCH_ENTRY_CUSTOM (CreateShader, CreateShaderObjectARB),
+ DISPATCH_ENTRY_ARB (ShaderSource),
+ DISPATCH_ENTRY_ARB (CompileShader),
+ DISPATCH_ENTRY_CUSTOM (GetShaderiv, GetObjectParameterivARB),
+ DISPATCH_ENTRY_CUSTOM (GetShaderInfoLog, GetInfoLogARB),
+ DISPATCH_ENTRY_CUSTOM (DeleteShader, DeleteObjectARB),
+
+ /* Programs */
+ DISPATCH_ENTRY_CUSTOM (CreateProgram, CreateProgramObjectARB),
+ DISPATCH_ENTRY_CUSTOM (AttachShader, AttachObjectARB),
+ DISPATCH_ENTRY_CUSTOM (DeleteProgram, DeleteObjectARB),
+ DISPATCH_ENTRY_ARB (LinkProgram),
+ DISPATCH_ENTRY_CUSTOM (UseProgram, UseProgramObjectARB),
+ DISPATCH_ENTRY_CUSTOM (GetProgramiv, GetObjectParameterivARB),
+ DISPATCH_ENTRY_CUSTOM (GetProgramInfoLog, GetInfoLogARB),
+
+ /* Uniforms */
+ DISPATCH_ENTRY_ARB (GetUniformLocation),
+ DISPATCH_ENTRY_ARB (Uniform1f),
+ DISPATCH_ENTRY_ARB (Uniform2f),
+ DISPATCH_ENTRY_ARB (Uniform3f),
+ DISPATCH_ENTRY_ARB (Uniform4f),
+ DISPATCH_ENTRY_ARB (Uniform1fv),
+ DISPATCH_ENTRY_ARB (UniformMatrix3fv),
+ DISPATCH_ENTRY_ARB (UniformMatrix4fv),
+ DISPATCH_ENTRY_ARB (Uniform1i),
+
+ /* Attributes */
+ DISPATCH_ENTRY_ARB (BindAttribLocation),
+ DISPATCH_ENTRY_ARB (VertexAttribPointer),
+ DISPATCH_ENTRY_ARB (EnableVertexAttribArray),
+ DISPATCH_ENTRY_ARB (DisableVertexAttribArray),
+
+ DISPATCH_ENTRY_LAST
+};
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_fbo_entries[] = {
+ DISPATCH_ENTRY_EXT (GenFramebuffers),
+ DISPATCH_ENTRY_EXT (BindFramebuffer),
+ DISPATCH_ENTRY_EXT (FramebufferTexture2D),
+ DISPATCH_ENTRY_EXT (CheckFramebufferStatus),
+ DISPATCH_ENTRY_EXT (DeleteFramebuffers),
+ DISPATCH_ENTRY_EXT (GenRenderbuffers),
+ DISPATCH_ENTRY_EXT (BindRenderbuffer),
+ DISPATCH_ENTRY_EXT (RenderbufferStorage),
+ DISPATCH_ENTRY_EXT (FramebufferRenderbuffer),
+ DISPATCH_ENTRY_EXT (DeleteRenderbuffers),
+ DISPATCH_ENTRY_LAST
+};
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_multisampling_entries[] = {
+#if CAIRO_HAS_GLESV2_SURFACE
+ DISPATCH_ENTRY_ES_ANGLE_EXT_IMG (BlitFramebuffer),
+ DISPATCH_ENTRY_ES_ANGLE_EXT_IMG (RenderbufferStorageMultisample),
+ DISPATCH_ENTRY_ES_ANGLE_EXT_IMG (FramebufferTexture2DMultisample),
+#elif CAIRO_HAS_EVASGL_SURFACE
+ DISPATCH_ENTRY_ES_ANGLE_EXT_IMG (BlitFramebuffer),
+ DISPATCH_ENTRY_ES_ANGLE_EXT_IMG (RenderbufferStorageMultisample),
+ DISPATCH_ENTRY_ES_ANGLE_EXT_IMG (FramebufferTexture2DMultisample),
+
+ DISPATCH_ENTRY_EXT_IMG (BlitFramebuffer),
+ DISPATCH_ENTRY_EXT_IMG (RenderbufferStorageMultisample),
+ DISPATCH_ENTRY_EXT_IMG (FramebufferTexture2DMultisample),
+#else
+ DISPATCH_ENTRY_EXT_IMG (BlitFramebuffer),
+ DISPATCH_ENTRY_EXT_IMG (RenderbufferStorageMultisample),
+ DISPATCH_ENTRY_EXT_IMG (FramebufferTexture2DMultisample),
+#endif
+ DISPATCH_ENTRY_LAST
+};
+
+#endif /* CAIRO_GL_DISPATCH_PRIVATE_H */
diff --git a/src/cairo-gl-dispatch.c b/src/cairo-gl-dispatch.c
new file mode 100755
index 000000000..aaa234661
--- /dev/null
+++ b/src/cairo-gl-dispatch.c
@@ -0,0 +1,296 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+#include "cairo-gl-dispatch-private.h"
+#if CAIRO_HAS_DLSYM
+#include <dlfcn.h>
+#endif
+
+#if CAIRO_HAS_DLSYM
+static void *
+_cairo_gl_dispatch_open_lib (void)
+{
+ return dlopen (NULL, RTLD_LAZY);
+}
+
+static void
+_cairo_gl_dispatch_close_lib (void *handle)
+{
+ dlclose (handle);
+}
+
+static cairo_gl_generic_func_t
+_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name)
+{
+ return (cairo_gl_generic_func_t) dlsym (handle, name);
+}
+#else
+static void *
+_cairo_gl_dispatch_open_lib (void)
+{
+ return NULL;
+}
+
+static void
+_cairo_gl_dispatch_close_lib (void *handle)
+{
+ return;
+}
+
+static cairo_gl_generic_func_t
+_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name)
+{
+ return NULL;
+}
+#endif /* CAIRO_HAS_DLSYM */
+
+
+static void
+_cairo_gl_dispatch_init_entries (cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data,
+ cairo_gl_dispatch_entry_t *entries,
+ cairo_gl_dispatch_name_t dispatch_name)
+{
+ cairo_gl_dispatch_entry_t *entry = entries;
+ void *handle = _cairo_gl_dispatch_open_lib ();
+
+ while (entry->name[CAIRO_GL_DISPATCH_NAME_CORE] != NULL) {
+ void *dispatch_ptr = &((char *) dispatch)[entry->offset];
+ const char *name = entry->name[dispatch_name];
+
+ /*
+ * In strictly conforming EGL implementations, eglGetProcAddress() can
+ * be used only to get extension functions, but some of the functions
+ * we want belong to core GL(ES). If the *GetProcAddress function
+ * provided by the context fails, try to get the address of the wanted
+ * GL function using standard system facilities (eg dlsym() in *nix
+ * systems).
+ */
+ cairo_gl_generic_func_t func = get_proc_addr (data, name);
+ if (func == NULL)
+ func = _cairo_gl_dispatch_get_proc_addr (handle, name);
+ *((cairo_gl_generic_func_t *) dispatch_ptr) = func;
+
+ ++entry;
+ }
+
+ _cairo_gl_dispatch_close_lib (handle);
+}
+
+static cairo_status_t
+_cairo_gl_dispatch_init_buffers (cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data,
+ int gl_version, cairo_gl_flavor_t gl_flavor)
+{
+ cairo_gl_dispatch_name_t dispatch_name;
+
+ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ {
+ if (gl_version >= CAIRO_GL_VERSION_ENCODE (1, 5))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ else if (_cairo_gl_has_extension (dispatch, "GL_ARB_vertex_buffer_object"))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
+ else
+ return CAIRO_STATUS_DEVICE_ERROR;
+ }
+ else if (gl_flavor == CAIRO_GL_FLAVOR_ES3)
+ {
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ }
+ else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+ {
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
+ }
+ else
+ {
+ return CAIRO_STATUS_DEVICE_ERROR;
+ }
+
+ _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, data,
+ dispatch_buffers_entries, dispatch_name);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_gl_dispatch_init_core (cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data)
+{
+ cairo_gl_dispatch_name_t dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+
+ _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, data,
+ dispatch_core_entries, dispatch_name);
+
+}
+
+static cairo_status_t
+_cairo_gl_dispatch_init_shaders (cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data,
+ int gl_version, cairo_gl_flavor_t gl_flavor)
+{
+ cairo_gl_dispatch_name_t dispatch_name;
+
+ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ {
+ if (gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ else if (_cairo_gl_has_extension (dispatch, "GL_ARB_shader_objects"))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
+ else
+ return CAIRO_STATUS_DEVICE_ERROR;
+ }
+ else if (gl_flavor == CAIRO_GL_FLAVOR_ES3)
+ {
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ }
+ else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+ {
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
+ }
+ else
+ {
+ return CAIRO_STATUS_DEVICE_ERROR;
+ }
+
+ _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, data,
+ dispatch_shaders_entries, dispatch_name);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_dispatch_init_fbo (cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data,
+ int gl_version, cairo_gl_flavor_t gl_flavor)
+{
+ cairo_gl_dispatch_name_t dispatch_name;
+
+ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ {
+ if (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) ||
+ _cairo_gl_has_extension (dispatch, "GL_ARB_framebuffer_object"))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ else if (_cairo_gl_has_extension (dispatch, "GL_EXT_framebuffer_object"))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
+ else
+ return CAIRO_STATUS_DEVICE_ERROR;
+ }
+ else if (gl_flavor == CAIRO_GL_FLAVOR_ES3)
+ {
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ }
+ else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+ {
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
+ }
+ else
+ {
+ return CAIRO_STATUS_DEVICE_ERROR;
+ }
+
+ _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, data,
+ dispatch_fbo_entries, dispatch_name);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_dispatch_init_multisampling (cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data, int gl_version,
+ cairo_gl_flavor_t gl_flavor)
+{
+ /* For the multisampling table, there are two GLES versions of the
+ * extension, so we put one in the EXT slot and one in the real ES slot.*/
+ cairo_gl_dispatch_name_t dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ if (gl_flavor == CAIRO_GL_FLAVOR_ES2) {
+ if (_cairo_gl_has_extension (dispatch, "GL_EXT_multisampled_render_to_texture"))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
+ else if (_cairo_gl_has_extension (dispatch, "GL_IMG_multisampled_render_to_texture"))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
+ else if (_cairo_gl_has_extension (dispatch, "GL_ANGLE_framebuffer_multisample") &&
+ _cairo_gl_has_extension (dispatch, "GL_ANGLE_framebuffer_blit"))
+ dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+ }
+
+ _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, data,
+ dispatch_multisampling_entries,
+ dispatch_name);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_dispatch_init (cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data)
+{
+ cairo_status_t status;
+ int gl_version;
+ cairo_gl_flavor_t gl_flavor;
+
+ _cairo_gl_dispatch_init_core (dispatch, get_proc_addr, data);
+ gl_version = _cairo_gl_get_version (dispatch);
+ gl_flavor = _cairo_gl_get_flavor (dispatch);
+
+
+ status = _cairo_gl_dispatch_init_buffers (dispatch, get_proc_addr, data,
+ gl_version, gl_flavor);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ status = _cairo_gl_dispatch_init_shaders (dispatch, get_proc_addr, data,
+ gl_version, gl_flavor);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ status = _cairo_gl_dispatch_init_fbo (dispatch, get_proc_addr, data,
+ gl_version, gl_flavor);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ status = _cairo_gl_dispatch_init_multisampling (dispatch, get_proc_addr,
+ data, gl_version, gl_flavor);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-gl-ext-def-private.h b/src/cairo-gl-ext-def-private.h
new file mode 100755
index 000000000..a261947be
--- /dev/null
+++ b/src/cairo-gl-ext-def-private.h
@@ -0,0 +1,143 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#ifndef CAIRO_GL_EXT_DEF_PRIVATE_H
+#define CAIRO_GL_EXT_DEF_PRIVATE_H
+
+#ifndef GL_TEXTURE_RECTANGLE
+#define GL_TEXTURE_RECTANGLE 0x84F5
+#endif
+
+#ifndef GL_ARRAY_BUFFER
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+
+#ifndef GL_STREAM_DRAW
+#define GL_STREAM_DRAW 0x88E0
+#endif
+
+#ifndef GL_WRITE_ONLY
+#define GL_WRITE_ONLY 0x88B9
+#endif
+
+#ifndef GL_PIXEL_UNPACK_BUFFER
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#endif
+
+#ifndef GL_FRAMEBUFFER
+#define GL_FRAMEBUFFER 0x8D40
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT0
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#endif
+
+#ifndef GL_FRAMEBUFFER_COMPLETE
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
+#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS 0x8CDA
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
+#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
+#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
+#endif
+
+#ifndef GL_FRAMEBUFFER_UNSUPPORTED
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#endif
+
+#ifndef GL_PACK_INVERT_MESA
+#define GL_PACK_INVERT_MESA 0x8758
+#endif
+
+#ifndef GL_CLAMP_TO_BORDER
+#define GL_CLAMP_TO_BORDER 0x812D
+#endif
+
+#ifndef GL_BGR
+#define GL_BGR 0x80E0
+#endif
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_RGBA8
+#define GL_RGBA8 0x8058
+#endif
+
+#ifndef GL_UNSIGNED_INT_8_8_8_8
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#endif
+
+#ifndef GL_UNSIGNED_SHORT_5_6_5_REV
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#endif
+
+#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#endif
+
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+
+#ifndef GL_PACK_ROW_LENGTH
+#define GL_PACK_ROW_LENGTH 0x0D02
+#endif
+
+#ifndef GL_UNPACK_ROW_LENGTH
+#define GL_UNPACK_ROW_LENGTH 0x0CF2
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
+#endif
+
+#endif /* CAIRO_GL_EXT_DEF_PRIVATE_H */
diff --git a/src/cairo-gl-filters.c b/src/cairo-gl-filters.c
new file mode 100755
index 000000000..7ddc4a246
--- /dev/null
+++ b/src/cairo-gl-filters.c
@@ -0,0 +1,416 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+#include "cairo-filters-private.h"
+
+static cairo_int_status_t
+_draw_rect (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ cairo_rectangle_int_t *rect)
+{
+ int quad[8];
+
+ quad[0] = quad[2] = rect->x;
+ quad[1] = quad[7] = rect->y;
+ quad[3] = quad[5] = rect->y + rect->height;
+ quad[4] = quad[6] = rect->x + rect->width;
+
+ return _cairo_gl_composite_emit_int_quad_as_tristrip (ctx, setup, quad);
+}
+
+/* stage 0 - shrink image */
+static cairo_int_status_t
+gaussian_filter_stage_0 (cairo_surface_pattern_t *pattern,
+ cairo_gl_surface_t *src,
+ cairo_gl_surface_t *dst,
+ int src_width, int src_height,
+ int dst_width, int dst_height)
+{
+ cairo_rectangle_int_t rect;
+ cairo_clip_t *clip;
+ cairo_int_status_t status;
+
+ src->blur_stage = CAIRO_GL_BLUR_STAGE_0;
+ _cairo_pattern_init_for_surface (pattern, &src->base);
+
+ cairo_matrix_init_scale (&pattern->base.matrix,
+ (double) src_width / (double) dst_width,
+ (double) src_height / (double) dst_height);
+
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = dst_width + 1;
+ rect.height = dst_height + 1;
+ clip = _cairo_clip_intersect_rectangle (NULL, &rect);
+
+ status = _cairo_surface_paint (&dst->base,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern->base, clip);
+
+ _cairo_clip_destroy (clip);
+ status = _cairo_gl_surface_resolve_multisampling (dst);
+
+ return status;
+}
+
+/* x-axis pass to scratches[1] */
+static cairo_int_status_t
+gaussian_filter_stage_1 (cairo_bool_t x_axis,
+ const cairo_surface_pattern_t *original_pattern,
+ cairo_surface_pattern_t *pattern,
+ cairo_gl_surface_t *src,
+ cairo_gl_surface_t *dst,
+ int dst_width, int dst_height,
+ cairo_bool_t is_opaque,
+ cairo_gl_context_t **ctx)
+{
+ int row, col;
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx_out = NULL;
+ cairo_rectangle_int_t rect;
+ cairo_int_status_t status;
+
+ src->image_content_scale_x = (double) dst_width / (double) src->width;
+ src->image_content_scale_y = (double) dst_height / (double) src->height;
+ row = original_pattern->base.y_radius * 2 + 1;
+ col = original_pattern->base.x_radius * 2 + 1;
+
+ src->blur_stage = CAIRO_GL_BLUR_STAGE_1;
+ _cairo_pattern_init_for_surface (pattern, &src->base);
+ pattern->base.filter = CAIRO_FILTER_GOOD;
+
+ if (x_axis) {
+ src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
+ src->operand.pass = 1;
+
+ memset (&src->operand.texture.coef[0], 0, sizeof (float) * col);
+ compute_x_coef_to_float (original_pattern->base.convolution_matrix,
+ row, col, &src->operand.texture.coef[0]);
+
+ src->operand.texture.x_radius = original_pattern->base.x_radius;
+ src->operand.texture.y_radius = 1;
+ }
+ else {
+ src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
+ src->operand.pass = 2;
+
+ memset (&src->operand.texture.coef[0], 0, sizeof (float) * row);
+ compute_y_coef_to_float (original_pattern->base.convolution_matrix,
+ row, col, &src->operand.texture.coef[0]);
+
+ src->operand.texture.y_radius = original_pattern->base.y_radius;
+ src->operand.texture.x_radius = 1;
+ }
+
+ *ctx = ctx_out;
+ status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE,
+ dst, FALSE);
+ if (unlikely (status))
+ return status;
+
+ pattern->base.extend = CAIRO_EXTEND_NONE;
+ status = _cairo_gl_composite_set_source (&setup, &pattern->base,
+ NULL, NULL, FALSE, FALSE);
+ if (unlikely (status)) {
+ _cairo_gl_composite_fini (&setup);
+ return status;
+ }
+
+ status = _cairo_gl_composite_begin (&setup, &ctx_out);
+ if (unlikely (status)) {
+ _cairo_gl_composite_fini (&setup);
+ return status;
+ }
+
+ if (is_opaque)
+ _cairo_gl_shader_bind_float (ctx_out,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE),
+ 1.0);
+ else
+ _cairo_gl_shader_bind_float (ctx_out,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE),
+ 0.0);
+
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = dst_width + 1;
+ rect.height = dst_height + 1;
+ status = _draw_rect (ctx_out, &setup, &rect);
+ _cairo_gl_composite_fini (&setup);
+ if (unlikely (status)) {
+ status = _cairo_gl_context_release (ctx_out, status);
+ return status;
+ }
+
+ *ctx = ctx_out;
+ _cairo_pattern_fini (&pattern->base);
+ return status;
+}
+
+/* y-axis pass to scratches[1] */
+static cairo_int_status_t
+gaussian_filter_stage_2 (cairo_bool_t y_axis,
+ const cairo_surface_pattern_t *original_pattern,
+ cairo_gl_surface_t *stage_1_src,
+ cairo_gl_surface_t *stage_2_src,
+ int dst_width, int dst_height)
+{
+ cairo_int_status_t status;
+ int row, col;
+
+ stage_2_src->image_content_scale_x = (double) dst_width / (double) stage_1_src->width;
+ stage_2_src->image_content_scale_y = (double) dst_height / (double) stage_1_src->height;
+
+ if (y_axis) {
+ stage_2_src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
+ stage_2_src->operand.pass = 2;
+
+ row = original_pattern->base.y_radius * 2 + 1;
+ col = original_pattern->base.x_radius * 2 + 1;
+
+ memset (&stage_2_src->operand.texture.coef[0], 0, sizeof (float) * row);
+ compute_y_coef_to_float (original_pattern->base.convolution_matrix,
+ row, col, &stage_2_src->operand.texture.coef[2]);
+ stage_2_src->operand.texture.y_radius = original_pattern->base.y_radius;
+ stage_2_src->operand.texture.x_radius = 1;
+ }
+ else
+ stage_2_src->operand.type = CAIRO_GL_OPERAND_TEXTURE;
+
+ stage_2_src->blur_stage = CAIRO_GL_BLUR_STAGE_2;
+
+ status = _cairo_gl_surface_resolve_multisampling (stage_2_src);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+/* out_extents should be populated by caller, and modified by the
+ * function
+ */
+cairo_gl_surface_t *
+_cairo_gl_gaussian_filter (cairo_gl_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_gl_surface_t *src,
+ cairo_rectangle_int_t *extents_out)
+{
+ cairo_gl_surface_t *scratches[2];
+ int src_width, src_height;
+ int width, height;
+ int scratch_width, scratch_height;
+
+ cairo_gl_context_t *ctx, *ctx_out;
+ cairo_status_t status;
+ cairo_bool_t skip_stage_0 = FALSE;
+ cairo_gl_operand_type_t saved_type = src->operand.type;
+
+ cairo_surface_pattern_t temp_pattern;
+ cairo_bool_t is_source;
+
+ int n;
+ cairo_bool_t is_opaque = FALSE;
+
+ cairo_content_t content = cairo_surface_get_content (&src->base);
+
+ if (src->operand.type == CAIRO_GL_OPERAND_GAUSSIAN) {
+ extents_out->x = extents_out->y = 0;
+ extents_out->width = cairo_gl_surface_get_width (&src->base) * src->image_content_scale_x;
+ extents_out->height = cairo_gl_surface_get_height (&src->base) * src->image_content_scale_y;
+ return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
+ }
+
+ if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN ||
+ ! pattern->base.convolution_matrix ||
+ ! _cairo_gl_surface_is_texture (src))
+ return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
+
+ if (content == CAIRO_CONTENT_COLOR)
+ is_opaque = TRUE;
+
+ src_width = cairo_gl_surface_get_width (&src->base);
+ src_height = cairo_gl_surface_get_height (&src->base);
+
+ width = src_width / pattern->base.shrink_factor_x;
+ height = src_height / pattern->base.shrink_factor_y;
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
+
+ for (n = 0; n < 2; n++) {
+ if (ctx->source_scratch_in_use) {
+ scratches[n] = ctx->mask_scratch_surfaces[n];
+ is_source = FALSE;
+ }
+ else {
+ scratches[n] = ctx->source_scratch_surfaces[n];
+ is_source = TRUE;
+ }
+
+ if (scratches[n]) {
+ scratch_width = cairo_gl_surface_get_width (&scratches[n]->base);
+ scratch_height = cairo_gl_surface_get_height (&scratches[n]->base);
+
+ if ((scratch_width < width &&
+ scratch_width < MAX_SCRATCH_SIZE) ||
+ (scratch_height < height &&
+ scratch_height < MAX_SCRATCH_SIZE)) {
+ cairo_surface_destroy (&scratches[n]->base);
+ scratches[n] = NULL;
+ }
+ else if (scratch_width > 4 * width &&
+ scratch_height > 4 * height) {
+ cairo_surface_destroy (&scratches[n]->base);
+ scratches[n] = NULL;
+ }
+ }
+
+ if (! scratches[n]) {
+ scratch_width = scratch_height = MIN_SCRATCH_SIZE;
+ while (scratch_width < width) {
+ scratch_width *= 2;
+ if (scratch_width == MAX_SCRATCH_SIZE)
+ break;
+ else if (scratch_width > MAX_SCRATCH_SIZE) {
+ scratch_width *= 0.5;
+ break;
+ }
+ }
+
+ while (scratch_height < height) {
+ scratch_height *= 2;
+ if (scratch_height == MAX_SCRATCH_SIZE)
+ break;
+ else if (scratch_height > MAX_SCRATCH_SIZE) {
+ scratch_height *= 0.5;
+ break;
+ }
+ }
+
+ scratches[n] =
+ (cairo_gl_surface_t *)_cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ scratch_width,
+ scratch_height);
+ _cairo_surface_release_device_reference (&scratches[n]->base);
+ }
+
+ if (ctx->source_scratch_in_use)
+ ctx->mask_scratch_surfaces[n] = scratches[n];
+ else
+ ctx->source_scratch_surfaces[n] = scratches[n];
+
+ scratches[n]->needs_to_cache = FALSE;
+ scratches[n]->force_no_cache = TRUE;
+ }
+
+ if (is_source)
+ ctx->source_scratch_in_use = TRUE;
+
+ /* we have created two scratch surfaces */
+ /* shrink surface to scratches[0] */
+ width = src_width / pattern->base.shrink_factor_x;
+ height = src_height / pattern->base.shrink_factor_y;
+ if (pattern->base.shrink_factor_x == 1.0 &&
+ pattern->base.shrink_factor_y == 1.0) {
+ skip_stage_0 = TRUE;
+ width = src_width;
+ height = src_height;
+ }
+ else if (width > scratches[0]->width ||
+ height > scratches[0]->height) {
+ width = scratches[0]->width;
+ height = scratches[0]->height;
+ }
+
+ if (! skip_stage_0) {
+ status = gaussian_filter_stage_0 (&temp_pattern, src,
+ scratches[0],
+ src_width, src_height,
+ width, height);
+ _cairo_pattern_fini (&temp_pattern.base);
+ if (unlikely (status))
+ return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
+ }
+
+ /* x-axis pass to scratches[1] */
+ if (! skip_stage_0)
+ status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern,
+ scratches[0], scratches[1],
+ width, height, is_opaque, &ctx_out);
+ else {
+ status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern,
+ src, scratches[1],
+ width, height, is_opaque, &ctx_out);
+ src->operand.type = saved_type;
+ }
+
+ if (ctx_out)
+ status = _cairo_gl_context_release (ctx_out, status);
+ if (unlikely (status))
+ return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
+
+ /* y-axis pass */
+ status = gaussian_filter_stage_1 (FALSE, pattern, &temp_pattern,
+ scratches[1], scratches[0],
+ width, height, is_opaque, &ctx_out);
+ if (ctx_out)
+ status = _cairo_gl_context_release (ctx_out, status);
+ if (unlikely (status))
+ return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
+
+ status = gaussian_filter_stage_2 (FALSE, pattern,
+ scratches[1], scratches[0],
+ width, height);
+
+ extents_out->x = 0;
+ extents_out->y = 0;
+ extents_out->width = width;
+ extents_out->height = height;
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ return (cairo_gl_surface_t *) cairo_surface_reference (&scratches[0]->base);
+}
diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
new file mode 100755
index 000000000..8e5892e6e
--- /dev/null
+++ b/src/cairo-gl-glyphs.c
@@ -0,0 +1,628 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributors:
+ * Benjamin Otte <otte@gnome.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-rtree-private.h"
+
+#define GLYPH_CACHE_WIDTH 1024
+#define GLYPH_CACHE_HEIGHT 1024
+#define GLYPH_CACHE_MIN_SIZE 4
+#define GLYPH_CACHE_MAX_SIZE 128
+
+typedef struct _cairo_gl_glyph {
+ cairo_rtree_node_t node;
+ cairo_scaled_glyph_private_t base;
+ cairo_scaled_glyph_t *glyph;
+ cairo_gl_glyph_cache_t *cache;
+ struct { float x, y; } p1, p2;
+} cairo_gl_glyph_t;
+
+static void
+_cairo_gl_node_destroy (cairo_rtree_node_t *node)
+{
+ cairo_gl_glyph_t *priv = cairo_container_of (node, cairo_gl_glyph_t, node);
+ cairo_scaled_glyph_t *glyph;
+
+ glyph = priv->glyph;
+ if (glyph == NULL)
+ return;
+
+ if (glyph->dev_private_key == priv->cache) {
+ glyph->dev_private = NULL;
+ glyph->dev_private_key = NULL;
+ }
+ cairo_list_del (&priv->base.link);
+ priv->glyph = NULL;
+}
+
+static void
+_cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
+ cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_gl_glyph_t *priv = cairo_container_of (glyph_private,
+ cairo_gl_glyph_t,
+ base);
+
+ assert (priv->glyph);
+
+ _cairo_gl_node_destroy (&priv->node);
+
+ /* XXX thread-safety? Probably ok due to the frozen scaled-font. */
+ if (! priv->node.pinned)
+ _cairo_rtree_node_remove (&priv->cache->rtree, &priv->node);
+
+ assert (priv->glyph == NULL);
+}
+
+static cairo_int_status_t
+_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx,
+ cairo_gl_glyph_cache_t *cache,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+ cairo_gl_glyph_t *glyph_private;
+ cairo_rtree_node_t *node = NULL;
+ cairo_int_status_t status;
+ int width, height;
+
+ width = glyph_surface->width;
+ if (width < GLYPH_CACHE_MIN_SIZE)
+ width = GLYPH_CACHE_MIN_SIZE;
+ height = glyph_surface->height;
+ if (height < GLYPH_CACHE_MIN_SIZE)
+ height = GLYPH_CACHE_MIN_SIZE;
+
+ /* search for an available slot */
+ status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+ /* search for an unlocked slot */
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_rtree_evict_random (&cache->rtree,
+ width, height, &node);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ status = _cairo_rtree_node_insert (&cache->rtree,
+ node, width, height, &node);
+ }
+ }
+ if (status)
+ return status;
+
+ /* XXX: Make sure we use the mask texture. This should work automagically somehow */
+ if(ctx->states_cache.active_texture != GL_TEXTURE1)
+ {
+ ctx->dispatch.ActiveTexture (GL_TEXTURE1);
+ ctx->states_cache.active_texture = GL_TEXTURE1;
+ }
+ status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface,
+ 0, 0,
+ glyph_surface->width, glyph_surface->height,
+ node->x, node->y, FALSE);
+ if (unlikely (status))
+ return status;
+
+ glyph_private = (cairo_gl_glyph_t *) node;
+ glyph_private->cache = cache;
+ glyph_private->glyph = scaled_glyph;
+ _cairo_scaled_glyph_attach_private (scaled_glyph,
+ &glyph_private->base,
+ cache,
+ _cairo_gl_glyph_fini);
+
+ scaled_glyph->dev_private = glyph_private;
+ scaled_glyph->dev_private_key = cache;
+
+ /* compute tex coords */
+ glyph_private->p1.x = node->x;
+ glyph_private->p1.y = node->y;
+ glyph_private->p2.x = node->x + glyph_surface->width;
+ glyph_private->p2.y = node->y + glyph_surface->height;
+ if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) {
+ glyph_private->p1.x /= GLYPH_CACHE_WIDTH;
+ glyph_private->p2.x /= GLYPH_CACHE_WIDTH;
+ glyph_private->p1.y /= GLYPH_CACHE_HEIGHT;
+ glyph_private->p2.y /= GLYPH_CACHE_HEIGHT;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_gl_glyph_t *
+_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private);
+}
+
+static cairo_status_t
+cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
+ cairo_format_t format,
+ cairo_bool_t has_component_alpha,
+ cairo_gl_glyph_cache_t **cache_out)
+{
+ cairo_gl_glyph_cache_t *cache;
+ cairo_content_t content;
+
+ switch (format) {
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ if (has_component_alpha) {
+ cache = &ctx->glyph_cache[0];
+ } else {
+ cache = &ctx->glyph_cache[2];
+ }
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+ break;
+ break;
+ case CAIRO_FORMAT_A8:
+ case CAIRO_FORMAT_A1:
+ cache = &ctx->glyph_cache[1];
+ content = CAIRO_CONTENT_ALPHA;
+ break;
+ default:
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ }
+
+ if (unlikely (cache->surface == NULL)) {
+ cairo_surface_t *surface;
+
+ surface = _cairo_gl_surface_create_scratch_for_caching (ctx,
+ content,
+ GLYPH_CACHE_WIDTH,
+ GLYPH_CACHE_HEIGHT);
+ if (unlikely (surface->status))
+ return surface->status;
+
+ _cairo_surface_release_device_reference (surface);
+
+ cache->surface = (cairo_gl_surface_t *)surface;
+ cache->surface->operand.texture.attributes.has_component_alpha = has_component_alpha;
+ }
+
+ *cache_out = cache;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Clear a partial surface, assumes context has already been acquired */
+static void _cairo_gl_surface_clear_with_extent (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t * dst,
+ cairo_rectangle_int_t *extent,
+ cairo_bool_t use_multisample )
+{
+ _cairo_gl_context_set_destination(ctx, dst, use_multisample);
+
+ if (ctx->states_cache.clear_red != 0 ||
+ ctx->states_cache.clear_green != 0 ||
+ ctx->states_cache.clear_blue != 0 ||
+ ctx->states_cache.clear_alpha != 0) {
+
+ ctx->states_cache.clear_red = 0;
+ ctx->states_cache.clear_green = 0;
+ ctx->states_cache.clear_blue = 0;
+ ctx->states_cache.clear_alpha = 0;
+
+ ctx->dispatch.ClearColor (0, 0, 0, 0);
+ }
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ _enable_scissor_buffer (ctx);
+ ctx->dispatch.Scissor(0, 0, extent->width, extent->height);
+ _disable_stencil_buffer (ctx);
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT);
+ }
+ else {
+ _disable_stencil_buffer (ctx);
+ _disable_scissor_buffer (ctx);
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ }
+}
+
+
+static cairo_status_t
+render_glyphs (cairo_gl_surface_t *dst,
+ int dst_x, int dst_y,
+ cairo_operator_t op,
+ cairo_surface_t *source,
+ cairo_composite_glyphs_info_t *info,
+ cairo_bool_t *has_component_alpha,
+ cairo_clip_t *clip,
+ cairo_bool_t *is_color_glyph)
+{
+ cairo_format_t last_format = CAIRO_FORMAT_INVALID;
+ cairo_gl_glyph_cache_t *cache = NULL;
+ cairo_gl_context_t *ctx;
+ cairo_gl_emit_glyph_t emit;
+ cairo_gl_composite_t setup;
+ cairo_int_status_t status;
+ int i = 0;
+
+ TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__,
+ info->extents.x, info->extents.y,
+ info->extents.width, info->extents.height));
+
+ *has_component_alpha = FALSE;
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_composite_init (&setup, op, dst, TRUE);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (source == NULL) {
+ _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE);
+ } else {
+ _cairo_gl_composite_set_source_operand (&setup,
+ source_to_operand (source));
+
+ }
+
+ if (setup.src.type == CAIRO_GL_OPERAND_CONSTANT)
+ setup.src.constant.encode_as_attribute = TRUE;
+
+ _cairo_gl_composite_set_clip (&setup, clip);
+
+ for (i = 0; i < info->num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_gl_glyph_t *glyph;
+ double x_offset, y_offset;
+ double x1, x2, y1, y2;
+
+ status = _cairo_scaled_glyph_lookup (info->font,
+ info->glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (scaled_glyph->surface->width == 0 ||
+ scaled_glyph->surface->height == 0)
+ {
+ continue;
+ }
+ if (! *has_component_alpha)
+ *has_component_alpha = pixman_image_get_component_alpha (scaled_glyph->surface->pixman_image);
+
+ /* color glyph has ARGB32 format and dst mask surface is ALPHA */
+ if (scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32 &&
+ dst->base.content == CAIRO_CONTENT_ALPHA &&
+ *has_component_alpha == FALSE)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ if (scaled_glyph->surface->format != last_format) {
+ status = cairo_gl_context_get_glyph_cache (ctx,
+ scaled_glyph->surface->format,
+ *has_component_alpha,
+ &cache);
+ if (unlikely (status))
+ goto FINISH;
+
+ last_format = scaled_glyph->surface->format;
+
+ if (! *has_component_alpha &&
+ cache->surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
+ /* we have color glyph */
+ _cairo_gl_composite_set_source_operand (&setup, &cache->surface->operand);
+ *is_color_glyph = TRUE;
+ } else {
+ _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand);
+ *is_color_glyph = FALSE;
+ }
+
+ /* XXX Shoot me. */
+ if (dst->msaa_active)
+ _cairo_gl_composite_set_multisample (&setup);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status))
+ goto FINISH;
+
+ emit = _cairo_gl_context_choose_emit_glyph (ctx, *is_color_glyph);
+ }
+
+ if (scaled_glyph->dev_private_key != cache) {
+ cairo_scaled_glyph_private_t *priv;
+
+ priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache);
+ if (priv) {
+ scaled_glyph->dev_private_key = cache;
+ scaled_glyph->dev_private = cairo_container_of (priv,
+ cairo_gl_glyph_t,
+ base);
+ } else {
+ if (cache == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto FINISH;
+ }
+ status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ /* Cache is full, so flush existing prims and try again. */
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_glyph_cache_unlock (cache);
+ status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
+ }
+
+ if (unlikely (_cairo_int_status_is_error (status)))
+ goto FINISH;
+ }
+ }
+
+ x_offset = scaled_glyph->surface->base.device_transform.x0;
+ y_offset = scaled_glyph->surface->base.device_transform.y0;
+
+ x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x);
+ y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y);
+ x2 = x1 + scaled_glyph->surface->width;
+ y2 = y1 + scaled_glyph->surface->height;
+
+ if (cache == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto FINISH;
+ }
+ glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph);
+ emit (ctx,
+ x1, y1, x2, y2,
+ glyph->p1.x, glyph->p1.y,
+ glyph->p2.x, glyph->p2.y);
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ FINISH:
+ status = _cairo_gl_context_release (ctx, status);
+
+ _cairo_gl_composite_fini (&setup);
+
+ return status;
+}
+
+static cairo_int_status_t
+render_glyphs_via_mask (cairo_gl_surface_t *dst,
+ int dst_x, int dst_y,
+ cairo_operator_t op,
+ cairo_surface_t *source,
+ cairo_composite_glyphs_info_t *info,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_bool_t has_component_alpha;
+ cairo_gl_context_t *ctx;
+ cairo_bool_t is_color_glyph;
+
+ int width = info->extents.width;
+ int height = info->extents.height;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ if (ctx->glyph_mask &&
+ (ctx->glyph_mask->width < info->extents.width ||
+ ctx->glyph_mask->height < info->extents.height)) {
+ width = ctx->glyph_mask->width;
+ height = ctx->glyph_mask->height;
+
+ cairo_surface_destroy (&ctx->glyph_mask->base);
+ ctx->glyph_mask = NULL;
+ }
+
+ /* Create the mask if it has not yet been initialized or it was too small and deleted above. */
+ if (! ctx->glyph_mask) {
+ width = MAX (width, info->extents.width);
+ height = MAX (height, info->extents.height);
+ /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */
+ ctx->glyph_mask = (cairo_gl_surface_t *)
+ cairo_gl_surface_create (dst->base.device,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
+ if (unlikely (ctx->glyph_mask->base.status)) {
+ status = ctx->glyph_mask->base.status;
+ status = _cairo_gl_context_release (ctx, status);
+ return status;
+ }
+ _cairo_surface_release_device_reference (&ctx->glyph_mask->base);
+ }
+
+ /* clear it */
+ _cairo_gl_surface_clear_with_extent (ctx,
+ (cairo_gl_surface_t *)ctx->glyph_mask,
+ &info->extents, FALSE);
+
+ status = render_glyphs (ctx->glyph_mask,
+ info->extents.x, info->extents.y,
+ CAIRO_OPERATOR_ADD, NULL,
+ info, &has_component_alpha, NULL,
+ &is_color_glyph);
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ cairo_surface_pattern_t mask_pattern;
+ cairo_surface_pattern_t source_pattern;
+ cairo_rectangle_int_t clip_extents;
+
+ ctx->glyph_mask->base.is_clear = FALSE;
+ _cairo_pattern_init_for_surface (&mask_pattern, &ctx->glyph_mask->base);
+ mask_pattern.base.has_component_alpha = has_component_alpha;
+ mask_pattern.base.filter = CAIRO_FILTER_NEAREST;
+ mask_pattern.base.extend = CAIRO_EXTEND_NONE;
+
+ cairo_matrix_init_translate (&mask_pattern.base.matrix,
+ dst_x-info->extents.x, dst_y-info->extents.y);
+
+ _cairo_pattern_init_for_surface (&source_pattern, source);
+
+ clip = _cairo_clip_copy (clip);
+ clip_extents.x = info->extents.x - dst_x;
+ clip_extents.y = info->extents.y - dst_y;
+ clip_extents.width = info->extents.width;
+ clip_extents.height = info->extents.height;
+ clip = _cairo_clip_intersect_rectangle (clip, &clip_extents);
+
+ if(is_color_glyph) {
+ if(op == CAIRO_OPERATOR_SOURCE) {
+ /* do dest_out then add*/
+ status = _cairo_surface_paint (&dst->base,
+ CAIRO_OPERATOR_DEST_OUT,
+ &mask_pattern.base,
+ clip);
+ status = _cairo_surface_paint (&dst->base,
+ CAIRO_OPERATOR_ADD,
+ &mask_pattern.base, clip);
+ } else {
+ status = _cairo_surface_paint (&dst->base,op,
+ &mask_pattern.base,
+ clip);
+ }
+ }
+ else
+ status = _cairo_surface_mask (&dst->base, op,
+ &source_pattern.base,
+ &mask_pattern.base,
+ clip);
+
+ _cairo_clip_destroy (clip);
+
+ _cairo_pattern_fini (&mask_pattern.base);
+ _cairo_pattern_fini (&source_pattern.base);
+ }
+
+ status = _cairo_gl_context_release(ctx, status);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs)
+{
+ if (! _cairo_gl_operator_is_supported (extents->op))
+ return UNSUPPORTED ("unsupported operator");
+
+ /* XXX use individual masks for large glyphs? */
+ if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE)
+ return UNSUPPORTED ("glyphs too large");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_gl_composite_glyphs_with_clip (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info,
+ cairo_clip_t *clip)
+{
+ cairo_gl_surface_t *dst = _dst;
+ cairo_bool_t has_component_alpha;
+ cairo_bool_t is_color_glyph;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ /* If any of the glyphs require component alpha, we have to go through
+ * a mask, since only _cairo_gl_surface_composite() currently supports
+ * component alpha.
+ */
+ if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER &&
+ (info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ||
+ info->font->options.antialias == CAIRO_ANTIALIAS_BEST))
+ {
+ info->use_mask = TRUE;
+ }
+
+ if (info->use_mask) {
+ return render_glyphs_via_mask (dst, dst_x, dst_y,
+ op, _src, info, clip);
+ } else {
+ return render_glyphs (dst, dst_x, dst_y,
+ op, _src, info,
+ &has_component_alpha,
+ clip, &is_color_glyph);
+ }
+
+}
+
+cairo_int_status_t
+_cairo_gl_composite_glyphs (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info)
+{
+ return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y,
+ dst_x, dst_y, info, NULL);
+}
+
+void
+_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
+{
+ _cairo_rtree_init (&cache->rtree,
+ GLYPH_CACHE_WIDTH,
+ GLYPH_CACHE_HEIGHT,
+ GLYPH_CACHE_MIN_SIZE,
+ sizeof (cairo_gl_glyph_t),
+ _cairo_gl_node_destroy);
+}
+
+void
+_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
+ cairo_gl_glyph_cache_t *cache)
+{
+ _cairo_rtree_fini (&cache->rtree);
+ cairo_surface_destroy (&cache->surface->base);
+}
diff --git a/src/cairo-gl-gradient-private.h b/src/cairo-gl-gradient-private.h
new file mode 100755
index 000000000..024549e5d
--- /dev/null
+++ b/src/cairo-gl-gradient-private.h
@@ -0,0 +1,102 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#ifndef CAIRO_GL_GRADIENT_PRIVATE_H
+#define CAIRO_GL_GRADIENT_PRIVATE_H
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "cairo-cache-private.h"
+#include "cairo-device-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-gl.h"
+
+#if 0
+#if CAIRO_HAS_EVASGL_SURFACE
+#include <Evas_GL.h>
+#else
+ #if CAIRO_HAS_GL_SURFACE
+ #include <GL/gl.h>
+ #include <GL/glext.h>
+ #elif CAIRO_HAS_GLESV2_SURFACE
+ #include <GLES2/gl2.h>
+ #include <GLES2/gl2ext.h>
+ #elif CAIRO_HAS_GLESV3_SURFACE
+ #include <GLES3/gl3.h>
+ #include <GLES3/gl3ext.h>
+ #endif
+#endif
+#endif
+
+#define CAIRO_GL_GRADIENT_CACHE_SIZE 4096
+
+/* XXX: Declare in a better place */
+typedef struct _cairo_gl_context cairo_gl_context_t;
+
+typedef struct _cairo_gl_gradient {
+ cairo_cache_entry_t cache_entry;
+ cairo_reference_count_t ref_count;
+ cairo_device_t *device; /* NB: we don't hold a reference */
+ unsigned int tex;
+ unsigned int n_stops;
+ const cairo_gradient_stop_t *stops;
+ cairo_gradient_stop_t stops_embedded[1];
+} cairo_gl_gradient_t;
+
+cairo_private cairo_int_status_t
+_cairo_gl_gradient_create (cairo_gl_context_t *ctx,
+ unsigned int n_stops,
+ const cairo_gradient_stop_t *stops,
+ cairo_gl_gradient_t **gradient_out);
+
+cairo_private_no_warn cairo_gl_gradient_t *
+_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient);
+
+cairo_private void
+_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient);
+
+cairo_private cairo_bool_t
+_cairo_gl_gradient_equal (const void *key_a, const void *key_b);
+
+
+#endif /* CAIRO_GL_GRADIENT_PRIVATE_H */
diff --git a/src/cairo-gl-gradient.c b/src/cairo-gl-gradient.c
new file mode 100755
index 000000000..ddf869ead
--- /dev/null
+++ b/src/cairo-gl-gradient.c
@@ -0,0 +1,340 @@
+/* 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-error-private.h"
+#include "cairo-gl-gradient-private.h"
+#include "cairo-gl-private.h"
+
+
+static int
+_cairo_gl_gradient_sample_width (unsigned int n_stops,
+ const cairo_gradient_stop_t *stops)
+{
+ unsigned int n;
+ int width;
+
+ width = 8;
+ for (n = 1; n < n_stops; n++) {
+ double dx = stops[n].offset - stops[n-1].offset;
+ double delta, max;
+ int ramp;
+
+ if (dx == 0)
+ return 1024; /* we need to emulate an infinitely sharp step */
+
+ max = fabs (stops[n].color.red - stops[n-1].color.red);
+
+ delta = fabs (stops[n].color.green - stops[n-1].color.green);
+ if (delta > max)
+ max = delta;
+
+ delta = fabs (stops[n].color.blue - stops[n-1].color.blue);
+ if (delta > max)
+ max = delta;
+
+ delta = fabs (stops[n].color.alpha - stops[n-1].color.alpha);
+ if (delta > max)
+ max = delta;
+
+ ramp = 128 * max / dx;
+ if (ramp > width)
+ width = ramp;
+ }
+
+ return (width + 7) & -8;
+}
+
+static uint8_t premultiply(double c, double a)
+{
+ int v = c * a * 256;
+ return v - (v >> 8);
+}
+
+static uint32_t color_stop_to_pixel(const cairo_gradient_stop_t *stop)
+{
+ uint8_t a, r, g, b;
+
+ a = stop->color.alpha_short >> 8;
+ r = premultiply(stop->color.red, stop->color.alpha);
+ g = premultiply(stop->color.green, stop->color.alpha);
+ b = premultiply(stop->color.blue, stop->color.alpha);
+
+ if (_cairo_is_little_endian ())
+ return a << 24 | r << 16 | g << 8 | b << 0;
+ else
+ return a << 0 | r << 8 | g << 16 | b << 24;
+}
+
+static cairo_status_t
+_cairo_gl_gradient_render (const cairo_gl_context_t *ctx,
+ unsigned int n_stops,
+ const cairo_gradient_stop_t *stops,
+ void *bytes,
+ int width)
+{
+ pixman_image_t *gradient, *image;
+ pixman_gradient_stop_t pixman_stops_stack[32];
+ pixman_gradient_stop_t *pixman_stops;
+ pixman_point_fixed_t p1, p2;
+ unsigned int i;
+ pixman_format_code_t gradient_pixman_format;
+
+ /*
+ * Ensure that the order of the gradient's components in memory is BGRA.
+ * This is done so that the gradient's pixel data is always suitable for
+ * texture upload using format=GL_BGRA and type=GL_UNSIGNED_BYTE.
+ */
+ if (_cairo_is_little_endian ())
+ gradient_pixman_format = PIXMAN_a8r8g8b8;
+ else
+ gradient_pixman_format = PIXMAN_b8g8r8a8;
+
+ pixman_stops = pixman_stops_stack;
+ if (unlikely (n_stops > ARRAY_LENGTH (pixman_stops_stack))) {
+ pixman_stops = _cairo_malloc_ab (n_stops,
+ sizeof (pixman_gradient_stop_t));
+ if (unlikely (pixman_stops == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < n_stops; i++) {
+ pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset);
+ pixman_stops[i].color.red = stops[i].color.red_short;
+ pixman_stops[i].color.green = stops[i].color.green_short;
+ pixman_stops[i].color.blue = stops[i].color.blue_short;
+ pixman_stops[i].color.alpha = stops[i].color.alpha_short;
+ }
+
+ p1.x = _cairo_fixed_16_16_from_double (0.5);
+ p1.y = 0;
+ p2.x = _cairo_fixed_16_16_from_double (width - 0.5);
+ p2.y = 0;
+
+ gradient = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ n_stops);
+ if (pixman_stops != pixman_stops_stack)
+ free (pixman_stops);
+
+ if (unlikely (gradient == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
+ pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD);
+
+ image = pixman_image_create_bits (gradient_pixman_format, width, 1,
+ bytes, sizeof(uint32_t)*width);
+ if (unlikely (image == NULL)) {
+ pixman_image_unref (gradient);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ gradient, NULL, image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, 1);
+
+ pixman_image_unref (gradient);
+ pixman_image_unref (image);
+
+ /* We need to fudge pixel 0 to hold the left-most color stop and not
+ * the neareset stop to the zeroth pixel centre in order to correctly
+ * populate the border color. For completeness, do both edges.
+ */
+ ((uint32_t*)bytes)[0] = color_stop_to_pixel(&stops[0]);
+ ((uint32_t*)bytes)[width-1] = color_stop_to_pixel(&stops[n_stops-1]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned long
+_cairo_gl_gradient_hash (unsigned int n_stops,
+ const cairo_gradient_stop_t *stops)
+{
+ return _cairo_hash_bytes (n_stops,
+ stops,
+ sizeof (cairo_gradient_stop_t) * n_stops);
+}
+
+static cairo_gl_gradient_t *
+_cairo_gl_gradient_lookup (cairo_gl_context_t *ctx,
+ unsigned long hash,
+ unsigned int n_stops,
+ const cairo_gradient_stop_t *stops)
+{
+ cairo_gl_gradient_t lookup;
+
+ lookup.cache_entry.hash = hash,
+ lookup.n_stops = n_stops;
+ lookup.stops = stops;
+
+ return _cairo_cache_lookup (&ctx->gradients, &lookup.cache_entry);
+}
+
+cairo_bool_t
+_cairo_gl_gradient_equal (const void *key_a, const void *key_b)
+{
+ const cairo_gl_gradient_t *a = key_a;
+ const cairo_gl_gradient_t *b = key_b;
+
+ if (a->n_stops != b->n_stops)
+ return FALSE;
+
+ return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
+}
+
+cairo_int_status_t
+_cairo_gl_gradient_create (cairo_gl_context_t *ctx,
+ unsigned int n_stops,
+ const cairo_gradient_stop_t *stops,
+ cairo_gl_gradient_t **gradient_out)
+{
+ unsigned long hash;
+ cairo_gl_gradient_t *gradient;
+ cairo_status_t status;
+ int tex_width;
+ GLint internal_format;
+ void *data;
+
+ if ((unsigned int) ctx->max_texture_size / 2 <= n_stops)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ hash = _cairo_gl_gradient_hash (n_stops, stops);
+
+ gradient = _cairo_gl_gradient_lookup (ctx, hash, n_stops, stops);
+ if (gradient) {
+ *gradient_out = _cairo_gl_gradient_reference (gradient);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ gradient = malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1));
+ if (gradient == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ tex_width = _cairo_gl_gradient_sample_width (n_stops, stops);
+ if (tex_width > ctx->max_texture_size)
+ tex_width = ctx->max_texture_size;
+
+ CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 2);
+ gradient->cache_entry.hash = hash;
+ gradient->cache_entry.size = tex_width;
+ gradient->device = &ctx->base;
+ gradient->n_stops = n_stops;
+ gradient->stops = gradient->stops_embedded;
+ memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t));
+
+ ctx->dispatch.GenTextures (1, &gradient->tex);
+ _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+ ctx->dispatch.BindTexture (ctx->tex_target, gradient->tex);
+
+ data = _cairo_malloc_ab (tex_width, sizeof (uint32_t));
+ if (unlikely (data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup_gradient;
+ }
+
+ status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width);
+ if (unlikely (status))
+ goto cleanup_data;
+
+ /*
+ * In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat'
+ * must match 'format' in glTexImage2D.
+ */
+ if (_cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES2 ||
+ _cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES3)
+ internal_format = GL_BGRA;
+ else
+ internal_format = GL_RGBA;
+
+ ctx->dispatch.TexImage2D (ctx->tex_target, 0, internal_format,
+ tex_width, 1, 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, data);
+
+ free (data);
+
+ /* we ignore errors here and just return an uncached gradient */
+ if (unlikely (_cairo_cache_insert (&ctx->gradients, &gradient->cache_entry)))
+ CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
+
+ *gradient_out = gradient;
+ return CAIRO_STATUS_SUCCESS;
+
+cleanup_data:
+ free (data);
+cleanup_gradient:
+ free (gradient);
+ return status;
+}
+
+cairo_gl_gradient_t *
+_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+ _cairo_reference_count_inc (&gradient->ref_count);
+
+ return gradient;
+}
+
+void
+_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient)
+{
+ cairo_gl_context_t *ctx;
+ cairo_status_t ignore;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
+ return;
+
+ if (_cairo_gl_context_acquire (gradient->device, &ctx) == CAIRO_STATUS_SUCCESS) {
+ /* The gradient my still be active in the last operation, so flush */
+ _cairo_gl_composite_flush (ctx);
+ ctx->dispatch.DeleteTextures (1, &gradient->tex);
+ ignore = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+ }
+
+ free (gradient);
+}
diff --git a/src/cairo-gl-hairline-stroke.c b/src/cairo-gl-hairline-stroke.c
new file mode 100755
index 000000000..9ff59e502
--- /dev/null
+++ b/src/cairo-gl-hairline-stroke.c
@@ -0,0 +1,412 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Samsung Electronics
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Henry Song <hsong@sisa.samsung.com>
+ */
+
+#include "cairo-gl-private.h"
+#include "cairo-slope-private.h"
+
+#define SCALE_TOLERANCE 0.0000001
+#define POINT_ADJUST 0.5
+
+static inline cairo_bool_t
+_add_cap (cairo_gl_hairline_closure_t *hairline,
+ double slope_dx,
+ double slope_dy,
+ cairo_bool_t lead_cap,
+ cairo_point_t *outp)
+{
+ double dx, dy;
+
+ if (hairline->cap_style == CAIRO_LINE_CAP_BUTT)
+ return FALSE;
+
+ dx = slope_dx * POINT_ADJUST;
+ dy = slope_dy * POINT_ADJUST;
+ hairline->line_last_capped = lead_cap;
+ cairo_matrix_transform_distance (hairline->ctm, &dx, &dy);
+ outp->x += _cairo_fixed_from_double (dx);
+ outp->y += _cairo_fixed_from_double (dy);
+
+ return TRUE;
+}
+
+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;
+}
+
+/* We implement hairline optimization for stroke. */
+cairo_bool_t
+_cairo_gl_hairline_style_is_hairline (const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm)
+{
+ double x, y;
+ cairo_status_t status = _cairo_matrix_compute_basis_scale_factors (ctm, &x, &y, TRUE);
+
+ if (unlikely (status))
+ return FALSE;
+
+ x = fabs (x - 1.0);
+ y = fabs (y - 1.0);
+
+ return style->line_width == 1.0 &&
+ (style->line_join != CAIRO_LINE_JOIN_MITER ||
+ style->miter_limit <= 10.0) &&
+ (x <= SCALE_TOLERANCE && y <= SCALE_TOLERANCE);
+}
+
+static cairo_status_t
+_path_add_first_and_last_cap (cairo_gl_hairline_closure_t *hairline)
+{
+ cairo_point_t p[2];
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_bool_t needs_to_cap;
+
+ /* check last point */
+ if (hairline->initialized) {
+ if (! hairline->line_last_capped) {
+ p[0] = hairline->line_last_point;
+ p[1] = hairline->line_last_point;
+ needs_to_cap = _add_cap (hairline,
+ hairline->line_last_dx,
+ hairline->line_last_dy,
+ FALSE,
+ &p[1]);
+ if (needs_to_cap) {
+ status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+ if (unlikely(status))
+ return status;
+ }
+ }
+ if (! hairline->stroke_first_capped) {
+ p[0] = hairline->stroke_first_point;
+ p[1] = hairline->stroke_first_point;
+ needs_to_cap = _add_cap (hairline,
+ hairline->stroke_first_dx,
+ hairline->stroke_first_dy,
+ TRUE,
+ &p[0]);
+
+ if (needs_to_cap) {
+ status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+ if (unlikely(status))
+ return status;
+ }
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_hairline_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_status_t status;
+
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+
+ hairline->current_point = *point;
+ hairline->moved_to_stroke_first_point = FALSE;
+
+ /* check last point */
+ if (hairline->initialized) {
+ status = _path_add_first_and_last_cap (hairline);
+ if (unlikely(status))
+ return status;
+ }
+
+ hairline->line_last_capped = TRUE;
+ hairline->stroke_first_capped = FALSE;
+ hairline->stroke_first_point = *point;
+ hairline->initialized = TRUE;
+
+ if (hairline->dash.dashed) {
+ _cairo_stroker_dash_start (&hairline->dash);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_hairline_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ double slope_dx, slope_dy;
+ double mag;
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+ cairo_point_t *p1 = &hairline->current_point;
+ cairo_slope_t dev_slope;
+ cairo_point_t p[2];
+
+ _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);
+
+ if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+ hairline->ctm_inverse, &mag))
+ return CAIRO_STATUS_SUCCESS;
+
+ hairline->line_last_point = *point;
+ hairline->line_last_capped = FALSE;
+ hairline->line_last_dx = slope_dx;
+ hairline->line_last_dy = slope_dy;
+
+ if (! hairline->moved_to_stroke_first_point) {
+ hairline->stroke_first_dx = slope_dx;
+ hairline->stroke_first_dy = slope_dy;
+ hairline->moved_to_stroke_first_point = TRUE;
+ }
+
+ p[0] = hairline->current_point;
+ p[1] = *point;
+ hairline->current_point = *point;
+
+ return _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+}
+
+cairo_status_t
+_cairo_gl_hairline_line_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_point_t p[2];
+ double remain, mag, step_length = 0;
+ double slope_dx, slope_dy;
+ double dx, dy;
+ cairo_line_t segment;
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+ cairo_point_t *p1 = &hairline->current_point;
+ cairo_slope_t dev_slope;
+ cairo_status_t status;
+ cairo_bool_t needs_to_cap;
+
+ _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);
+
+ if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+ hairline->ctm_inverse, &mag))
+ return CAIRO_STATUS_SUCCESS;
+
+ remain = mag;
+ segment.p1 = *p1;
+ while (remain) {
+ step_length = MIN (hairline->dash.dash_remain, remain);
+ remain -= step_length;
+ dx = slope_dx * (mag - remain);
+ dy = slope_dy * (mag - remain);
+ cairo_matrix_transform_distance (hairline->ctm, &dx, &dy);
+ segment.p2.x = _cairo_fixed_from_double (dx) + p1->x;
+ segment.p2.y = _cairo_fixed_from_double (dy) + p1->y;
+
+ if (hairline->dash.dash_on) {
+ p[0] = segment.p1;
+ p[1] = segment.p2;
+ /* Check leading cap. */
+ if (segment.p1.x == hairline->stroke_first_point.x &&
+ segment.p1.y == hairline->stroke_first_point.y) {
+ /* We are at the first stroke point, and we have
+ been here, add a leading cap. */
+ if (hairline->moved_to_stroke_first_point) {
+ if (hairline->dash.dashes[hairline->dash.dash_index] == hairline->dash.dash_remain)
+ needs_to_cap = _add_cap (hairline,
+ slope_dx,
+ slope_dy,
+ TRUE, &p[0]);
+ }
+ else {
+ /* We have not been in the first stroke point,
+ this is the first line_to call sinece move_to()
+ save the slope, we need use it later. */
+ hairline->stroke_first_dx = slope_dx;
+ hairline->stroke_first_dy = slope_dy;
+ hairline->moved_to_stroke_first_point = TRUE;
+ }
+ }
+ else if (segment.p1.x == hairline->current_point.x &&
+ segment.p1.y == hairline->current_point.y) {
+ /* Start of the line segment, if we have the entire
+ dash length, we are at the begining of a dash,
+ add a leading cap. */
+ if (hairline->dash.dashes[hairline->dash.dash_index] == hairline->dash.dash_remain)
+ needs_to_cap = _add_cap (hairline,
+ slope_dx, slope_dy,
+ TRUE, &p[0]);
+ }
+ else
+ /* We are in the middle of the line segment, add
+ a leading cap. */
+ needs_to_cap = _add_cap (hairline,
+ slope_dx, slope_dy,
+ TRUE, &p[0]);
+
+ /* Add trailing cap. We have not exhausted line segment,
+ or we have exhausted current dash length, add a
+ trailing cap. */
+ if (remain ||
+ hairline->dash.dash_remain - step_length < CAIRO_FIXED_ERROR_DOUBLE)
+ needs_to_cap = _add_cap (hairline,
+ slope_dx, slope_dy,
+ FALSE, &p[1]);
+ else {
+ /* We indicate here that we have not added a trailing
+ cap yet. If next move is move_to, we will add a
+ trailing cap, otherwise, it will be ignored. */
+ hairline->line_last_capped = FALSE;
+ hairline->line_last_point = segment.p2;
+ hairline->line_last_dx = slope_dx;
+ hairline->line_last_dy = slope_dy;
+ }
+
+ status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+ if (unlikely (status))
+ return status;
+ }
+
+ _cairo_stroker_dash_step (&hairline->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ hairline->current_point = *point;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_hairline_curve_to (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+ cairo_spline_t spline;
+ cairo_path_fixed_line_to_func_t *line_to;
+
+ line_to = hairline->dash.dashed ?
+ _cairo_gl_hairline_line_to_dashed :
+ _cairo_gl_hairline_line_to;
+
+ if (! _cairo_spline_init (&spline,
+ (cairo_spline_add_point_func_t)line_to,
+ closure,
+ &hairline->current_point, p0, p1, p2))
+ return _cairo_gl_hairline_line_to (closure, p2);
+
+ return _cairo_spline_decompose (&spline, hairline->tolerance);
+}
+
+cairo_status_t
+_cairo_gl_hairline_close_path (void *closure)
+{
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+
+ hairline->line_last_capped = TRUE;
+ hairline->stroke_first_capped = TRUE;
+
+ if (hairline->dash.dashed)
+ return _cairo_gl_hairline_line_to_dashed (closure,
+ &hairline->stroke_first_point);
+ return _cairo_gl_hairline_line_to (closure, &hairline->stroke_first_point);
+}
+
+cairo_status_t
+_cairo_gl_path_fixed_stroke_to_hairline (const cairo_path_fixed_t *path,
+ cairo_gl_hairline_closure_t *closure,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ 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)
+{
+ cairo_status_t status;
+
+ _cairo_stroker_dash_init (&closure->dash, style);
+ closure->ctm = (cairo_matrix_t *)ctm;
+ closure->ctm_inverse = (cairo_matrix_t *)ctm_inverse;
+ closure->cap_style = style->line_cap;
+ closure->initialized = FALSE;
+
+ status = _cairo_path_fixed_interpret (path,
+ move_to,
+ line_to,
+ curve_to,
+ close_path,
+ (void *) closure);
+ if (unlikely (status))
+ return status;
+
+ return _path_add_first_and_last_cap (closure);
+}
diff --git a/src/cairo-gl-info.c b/src/cairo-gl-info.c
new file mode 100755
index 000000000..33dc0cc56
--- /dev/null
+++ b/src/cairo-gl-info.c
@@ -0,0 +1,98 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+
+int _cairo_gl_get_version (cairo_gl_dispatch_t *dispatch)
+{
+ int major, minor;
+ const char *version = (const char *) dispatch->GetString (GL_VERSION);
+ const char *dot = version == NULL ? NULL : strchr (version, '.');
+ const char *major_start = dot;
+
+ /* Sanity check */
+ if (dot == NULL || dot == version || *(dot + 1) == '\0') {
+ major = 0;
+ minor = 0;
+ } else {
+ /* Find the start of the major version in the string */
+ while (major_start > version && *major_start != ' ')
+ --major_start;
+ major = strtol (major_start, NULL, 10);
+ minor = strtol (dot + 1, NULL, 10);
+ }
+
+ return CAIRO_GL_VERSION_ENCODE (major, minor);
+}
+
+cairo_gl_flavor_t
+_cairo_gl_get_flavor (cairo_gl_dispatch_t *dispatch)
+{
+ const char *version = (const char *) dispatch->GetString (GL_VERSION);
+ cairo_gl_flavor_t flavor;
+
+ if (version == NULL)
+ flavor = CAIRO_GL_FLAVOR_NONE;
+ else if (strstr (version, "OpenGL ES 2") != NULL)
+ flavor = CAIRO_GL_FLAVOR_ES2;
+ else if (strstr (version, "OpenGL ES 3") != NULL)
+#if CAIRO_HAS_GLESV2_SURFACE
+ flavor = CAIRO_GL_FLAVOR_ES2;
+#elif CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ flavor = CAIRO_GL_FLAVOR_ES3;
+#else
+ flavor = CAIRO_GL_FLAVOR_NONE;
+#endif
+ else
+ flavor = CAIRO_GL_FLAVOR_DESKTOP;
+
+ return flavor;
+}
+
+cairo_bool_t
+_cairo_gl_has_extension (cairo_gl_dispatch_t *dispatch, const char *ext)
+{
+ const char *extensions = (const char *) dispatch->GetString (GL_EXTENSIONS);
+ size_t len = strlen (ext);
+ const char *ext_ptr = extensions;
+
+ if (unlikely (ext_ptr == NULL))
+ return 0;
+
+ while ((ext_ptr = strstr (ext_ptr, ext)) != NULL) {
+ if (ext_ptr[len] == ' ' || ext_ptr[len] == '\0')
+ break;
+ ext_ptr += len;
+ }
+
+ return (ext_ptr != NULL);
+}
diff --git a/src/cairo-gl-msaa-compositor.c b/src/cairo-gl-msaa-compositor.c
new file mode 100755
index 000000000..7ce5c7a4b
--- /dev/null
+++ b/src/cairo-gl-msaa-compositor.c
@@ -0,0 +1,1284 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2011 Samsung Electronics
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Henry Song <hsong@sisa.samsung.com>
+ * Martin Robinson <mrobinson@igalia.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-inline.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-gl-private.h"
+#include "cairo-path-private.h"
+#include "cairo-traps-private.h"
+
+static cairo_bool_t
+can_use_msaa_compositor (cairo_gl_surface_t *surface,
+ cairo_antialias_t antialias);
+
+static void
+query_surface_capabilities (cairo_gl_surface_t *surface);
+
+struct _tristrip_composite_info {
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+};
+
+static cairo_bool_t
+_is_continuous_single_line (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style)
+{
+ return (_cairo_path_fixed_is_single_line (path) &&
+ style->dash == NULL);
+}
+
+static cairo_int_status_t
+_draw_trap (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ cairo_trapezoid_t *trap)
+{
+ cairo_point_t quad[4];
+
+ if (trap->left.p1.x == trap->left.p2.x) {
+ quad[0].x = trap->left.p1.x;
+ quad[1].x = trap->left.p1.x;
+ } else {
+ cairo_fixed_t x, dy;
+ x = trap->left.p1.x;
+ dy = trap->left.p2.y - trap->left.p1.y;
+
+ if (trap->top == trap->left.p1.y)
+ quad[0].x = x;
+ else if (trap->top == trap->left.p2.y)
+ quad[0].x = trap->left.p2.x;
+ else if (dy != 0)
+ quad[0].x = x + _cairo_fixed_mul_div_floor (trap->top - trap->left.p1.y,
+ trap->left.p2.x - trap->left.p1.x, dy);
+
+ if (trap->bottom == trap->left.p2.y)
+ quad[1].x = trap->left.p2.x;
+ else if (trap->bottom == trap->left.p1.y)
+ quad[1].x = x;
+ else if (dy != 0)
+ quad[1].x = x + _cairo_fixed_mul_div_floor (trap->bottom - trap->left.p1.y,
+ trap->left.p2.x - trap->left.p1.x, dy);
+ }
+ quad[0].y = trap->top;
+ quad[1].y = trap->bottom;
+
+ if (trap->right.p1.x == trap->right.p2.x) {
+ quad[2].x = trap->right.p1.x;
+ quad[3].x = trap->right.p1.x;
+ } else {
+ cairo_fixed_t x, dy;
+ x = trap->right.p1.x;
+ dy = trap->right.p2.y - trap->right.p1.y;
+
+ if (trap->bottom == trap->right.p2.y)
+ quad[2].x = trap->right.p2.x;
+ else if (trap->bottom == trap->right.p1.y)
+ quad[2].x = x;
+ else if (dy != 0)
+ quad[2].x = x + _cairo_fixed_mul_div_floor (trap->bottom - trap->right.p1.y,
+ trap->right.p2.x - trap->right.p1.x, dy);
+
+ if (trap->top == trap->right.p1.y)
+ quad[3].x = x;
+ else if (trap->top == trap->right.p2.y)
+ quad[3].x = trap->right.p2.x;
+ else if (dy != 0)
+ quad[3].x = x + _cairo_fixed_mul_div_floor (trap->top - trap->right.p1.y,
+ trap->right.p2.x - trap->right.p1.x, dy);
+ }
+ quad[2].y = trap->bottom;
+ quad[3].y = trap->top;
+
+ return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad);
+}
+
+static cairo_int_status_t
+_draw_traps (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ cairo_traps_t *traps)
+{
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+ int i;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ cairo_trapezoid_t *trap = traps->traps + i;
+ if (unlikely ((status = _draw_trap (ctx, setup, trap))))
+ return status;
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_draw_int_rect (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ cairo_rectangle_int_t *rect)
+{
+ int quad[8];
+
+ quad[0] = quad[2] = rect->x;
+ quad[1] = quad[7] = rect->y;
+ quad[3] = quad[5] = rect->y + rect->height;
+ quad[4] = quad[6] = rect->x + rect->width;
+
+ return _cairo_gl_composite_emit_int_quad_as_tristrip (ctx, setup, quad);
+}
+
+static cairo_int_status_t
+_draw_triangle_fan (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const cairo_point_t *midpt,
+ const cairo_point_t *points,
+ int npoints)
+{
+ int i;
+
+ /* Our strategy here is to not even try to build a triangle fan, but to
+ draw each triangle as if it was an unconnected member of a triangle strip. */
+ for (i = 1; i < npoints; i++) {
+ cairo_int_status_t status;
+ cairo_point_t triangle[3];
+
+ triangle[0] = *midpt;
+ triangle[1] = points[i - 1];
+ triangle[2] = points[i];
+
+ status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_clip_to_traps (cairo_clip_t *clip,
+ cairo_traps_t *traps)
+{
+ cairo_int_status_t status;
+ cairo_polygon_t polygon;
+ cairo_antialias_t antialias;
+ cairo_fill_rule_t fill_rule;
+
+ _cairo_traps_init (traps);
+
+ if (clip->num_boxes == 1 && clip->path == NULL) {
+ cairo_boxes_t boxes;
+ _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes);
+ return _cairo_traps_init_boxes (traps, &boxes);
+ }
+
+ status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias);
+ if (unlikely (status))
+ return status;
+
+ /* We ignore the antialias mode of the clip here, since the user requested
+ * unantialiased rendering of their path and we expect that this stencil
+ * based rendering of the clip to be a reasonable approximation to
+ * the intersection between that clip and the path.
+ *
+ * In other words, what the user expects when they try to perform
+ * a geometric intersection between an unantialiased polygon and an
+ * antialiased polygon is open to interpretation. And we choose the fast
+ * option.
+ */
+
+ _cairo_traps_init (traps);
+ status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+ &polygon,
+ fill_rule);
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_traps_t traps;
+
+ status = _clip_to_traps (clip, &traps);
+ if (unlikely (status))
+ return status;
+ status = _draw_traps (ctx, setup, &traps);
+
+ _cairo_traps_fini (&traps);
+ return status;
+}
+
+static cairo_int_status_t
+_blit_texture_to_renderbuffer (cairo_gl_surface_t *surface)
+{
+ cairo_gl_context_t *ctx = NULL;
+ cairo_gl_composite_t setup;
+ cairo_surface_pattern_t pattern;
+ cairo_rectangle_int_t extents;
+ cairo_int_status_t status;
+ cairo_gl_flavor_t gl_flavor = ((cairo_gl_context_t *) surface->base.device)->gl_flavor;
+
+ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (! _cairo_gl_surface_is_texture (surface))
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (surface->msaa_active)
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (surface->content_synced) {
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+ _cairo_gl_context_set_destination (ctx, surface, TRUE);
+ return _cairo_gl_context_release (ctx, status);
+ }
+
+ memset (&setup, 0, sizeof (cairo_gl_composite_t));
+
+ status = _cairo_gl_composite_set_operator (&setup,
+ CAIRO_OPERATOR_SOURCE,
+ FALSE);
+ if (status)
+ return status;
+
+ setup.dst = surface;
+ setup.clip_region = surface->clip_region;
+
+ _cairo_pattern_init_for_surface (&pattern, &surface->base);
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ status = _cairo_gl_composite_set_source (&setup, &pattern.base,
+ NULL, &extents, FALSE, FALSE);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_multisample (&setup);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _draw_int_rect (ctx, &setup, &extents);
+
+ if (unlikely (status))
+ goto FAIL;
+
+ surface->content_synced = TRUE;
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+
+ if (ctx) {
+ _cairo_gl_composite_flush (ctx);
+ status = _cairo_gl_context_release (ctx, status);
+ }
+
+ return status;
+}
+
+
+static cairo_bool_t
+_should_use_unbounded_surface (cairo_composite_rectangles_t *composite)
+{
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
+ cairo_rectangle_int_t *source = &composite->source;
+
+ if (composite->is_bounded)
+ return FALSE;
+
+ /* This isn't just an optimization. It also detects when painting is used
+ to paint back the unbounded surface, preventing infinite recursion. */
+ return ! (source->x <= 0 && source->y <= 0 &&
+ source->height + source->y >= dst->height &&
+ source->width + source->x >= dst->width);
+}
+
+static cairo_surface_t*
+_prepare_unbounded_surface (cairo_gl_surface_t *dst)
+{
+
+ cairo_surface_t* surface = cairo_gl_surface_create (dst->base.device,
+ dst->base.content,
+ dst->width,
+ dst->height);
+ if (surface == NULL)
+ return NULL;
+ if (unlikely (surface->status)) {
+ cairo_surface_destroy (surface);
+ return NULL;
+ }
+ return surface;
+}
+
+static cairo_int_status_t
+_paint_back_unbounded_surface (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ cairo_surface_t *surface)
+{
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
+ cairo_int_status_t status;
+
+ cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
+ if (unlikely (pattern->status)) {
+ status = pattern->status;
+ goto finish;
+ }
+
+ status = _cairo_compositor_paint (compositor, &dst->base,
+ composite->op, pattern,
+ composite->clip);
+
+finish:
+ cairo_pattern_destroy (pattern);
+ cairo_surface_destroy (surface);
+ return status;
+}
+
+static cairo_bool_t
+can_use_msaa_compositor (cairo_gl_surface_t *surface,
+ cairo_antialias_t antialias)
+{
+ cairo_gl_flavor_t flavor = ((cairo_gl_context_t *) surface->base.device)->gl_flavor;
+ cairo_bool_t has_angle_multisampling = ((cairo_gl_context_t *) surface->base.device)->has_angle_multisampling;
+
+ query_surface_capabilities (surface);
+ if (! surface->supports_stencil)
+ return FALSE;
+
+ /* Multisampling OpenGL ES surfaces only maintain one multisampling
+ framebuffer and thus must use the spans compositor to do non-antialiased
+ rendering. */
+ if (! (flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ flavor == CAIRO_GL_FLAVOR_ES3 ||
+ (flavor == CAIRO_GL_FLAVOR_ES2 &&
+ has_angle_multisampling)) &&
+ surface->supports_msaa &&
+ antialias == CAIRO_ANTIALIAS_NONE)
+ return FALSE;
+
+ /* The MSAA compositor has a single-sample mode, so we can
+ support non-antialiased rendering. */
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ return TRUE;
+
+ if ((antialias == CAIRO_ANTIALIAS_GRAY ||
+ antialias== CAIRO_ANTIALIAS_SUBPIXEL ||
+ antialias == CAIRO_ANTIALIAS_FAST ||
+ antialias == CAIRO_ANTIALIAS_DEFAULT) &&
+ surface->num_samples > 1)
+ return surface->supports_msaa;
+ return FALSE;
+}
+
+static void
+_cairo_gl_msaa_compositor_set_clip (cairo_composite_rectangles_t *composite,
+ cairo_gl_composite_t *setup)
+{
+ uint32_t is_bounded;
+
+ /* We don't need to check CAIRO_OPERATOR_BOUND_BY_MASK in these
+ situations. */
+ is_bounded = composite->is_bounded;
+ composite->is_bounded = CAIRO_OPERATOR_BOUND_BY_SOURCE;
+ if (_cairo_composite_rectangles_can_reduce_clip (composite, composite->clip))
+ return;
+
+ _cairo_gl_composite_set_clip (setup, composite->clip);
+
+ composite->is_bounded = is_bounded;
+}
+
+static cairo_bool_t
+_pattern_is_pixel_aligned (const cairo_pattern_t *pattern)
+{
+ long xoffset, yoffset;
+
+ if (!pattern)
+ return TRUE;
+
+ xoffset = pattern->matrix.x0;
+ yoffset = pattern->matrix.y0;
+
+ if (pattern->matrix.xx != 1.0 ||
+ pattern->matrix.xy != 0.0 ||
+ pattern->matrix.yy != 1.0 ||
+ pattern->matrix.yx != 1.0 ||
+ pattern->matrix.x0 != xoffset ||
+ pattern->matrix.y0 != yoffset)
+ return FALSE;
+ return TRUE;
+}
+
+static cairo_bool_t
+_clip_is_pixel_aligned (const cairo_clip_t *clip)
+{
+ if (!clip)
+ return TRUE;
+
+ if (clip->path || clip->num_boxes > 1)
+ return FALSE;
+
+ if (_cairo_fixed_is_integer (clip->boxes[0].p1.x) &&
+ _cairo_fixed_is_integer (clip->boxes[0].p1.y) &&
+ _cairo_fixed_is_integer (clip->boxes[0].p2.x) &&
+ _cairo_fixed_is_integer (clip->boxes[0].p2.y))
+ return TRUE;
+ return FALSE;
+}
+
+/* Masking with the SOURCE operator requires two passes. In the first
+ * pass we use the mask as the source to get:
+ * result = (1 - ma) * dst
+ * In the second pass we use the add operator to achieve:
+ * result = (src * ma) + dst
+ * Combined this produces:
+ * result = (src * ma) + (1 - ma) * dst
+ */
+static cairo_int_status_t
+_cairo_gl_msaa_compositor_mask_source_operator (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
+ cairo_gl_context_t *ctx = NULL;
+ cairo_int_status_t status;
+
+ cairo_clip_t *clip = composite->clip;
+ cairo_traps_t traps;
+ cairo_bool_t is_pixel_aligned =
+ _pattern_is_pixel_aligned (composite->original_source_pattern) &&
+ _pattern_is_pixel_aligned (composite->original_mask_pattern) &&
+ _clip_is_pixel_aligned (clip);
+
+ /* If we have a non-rectangular clip, we can avoid using the stencil buffer
+ * for clipping and just draw the clip polygon. */
+ if (clip) {
+ status = _clip_to_traps (clip, &traps);
+ if (unlikely (status)) {
+ _cairo_traps_fini (&traps);
+ return status;
+ }
+ }
+
+ if (! is_pixel_aligned) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_gl_composite_init (&setup,
+ CAIRO_OPERATOR_DEST_OUT,
+ dst,
+ FALSE /* assume_component_alpha */);
+ if (unlikely (status))
+ return status;
+ status = _cairo_gl_composite_set_source (&setup,
+ composite->original_mask_pattern,
+ &composite->mask_sample_area,
+ &composite->bounded,
+ FALSE, FALSE);
+ if (unlikely (status))
+ goto finish;
+
+ if (! is_pixel_aligned || dst->msaa_active)
+ _cairo_gl_composite_set_multisample (&setup);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto finish;
+
+ if (! clip)
+ status = _draw_int_rect (ctx, &setup, &composite->bounded);
+ else
+ status = _draw_traps (ctx, &setup, &traps);
+
+ /* Now draw the second pass. */
+ status = _cairo_gl_composite_set_operator (&setup, CAIRO_OPERATOR_ADD,
+ FALSE /* assume_component_alpha */);
+ if (unlikely (status))
+ goto finish;
+ status = _cairo_gl_composite_set_source (&setup,
+ composite->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, FALSE);
+ if (unlikely (status))
+ goto finish;
+ status = _cairo_gl_composite_set_mask (&setup,
+ composite->original_mask_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE);
+ if (unlikely (status))
+ goto finish;
+
+ _cairo_gl_context_set_destination (ctx, dst, setup.multisample);
+
+ status = _cairo_gl_set_operands_and_operator (&setup, ctx);
+ if (unlikely (status))
+ goto finish;
+
+ if (! clip)
+ status = _draw_int_rect (ctx, &setup, &composite->bounded);
+ else
+ status = _draw_traps (ctx, &setup, &traps);
+
+ if (unlikely (status))
+ goto finish;
+
+ dst->content_synced = FALSE;
+
+finish:
+ _cairo_gl_composite_fini (&setup);
+ if (ctx)
+ status = _cairo_gl_context_release (ctx, status);
+ if (clip)
+ _cairo_traps_fini (&traps);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
+ cairo_gl_context_t *ctx = NULL;
+ cairo_int_status_t status;
+ cairo_operator_t op = composite->op;
+ cairo_clip_t *clip = composite->clip;
+ cairo_bool_t is_pixel_aligned = FALSE;
+
+ if (! can_use_msaa_compositor (dst, CAIRO_ANTIALIAS_DEFAULT))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->op == CAIRO_OPERATOR_CLEAR &&
+ composite->original_mask_pattern != NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* GL compositing operators cannot properly represent a mask operation
+ using the SOURCE compositing operator in one pass. This only matters if
+ there actually is a mask (there isn't in a paint operation) and if the
+ mask isn't totally opaque. */
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ composite->original_mask_pattern != NULL &&
+ ! _cairo_pattern_is_opaque (&composite->mask_pattern.base,
+ &composite->mask_sample_area)) {
+
+ if (! _cairo_pattern_is_opaque (&composite->source_pattern.base,
+ &composite->source_sample_area)) {
+ return _cairo_gl_msaa_compositor_mask_source_operator (compositor, composite);
+ }
+
+ /* If the source is opaque the operation reduces to OVER. */
+ op = CAIRO_OPERATOR_OVER;
+ }
+
+ if (_should_use_unbounded_surface (composite)) {
+ cairo_surface_t* surface = _prepare_unbounded_surface (dst);
+
+ if (unlikely (surface == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* This may be a paint operation. */
+ if (composite->original_mask_pattern == NULL) {
+ status = _cairo_compositor_paint (compositor, surface,
+ CAIRO_OPERATOR_SOURCE,
+ &composite->source_pattern.base,
+ NULL);
+ } else {
+ status = _cairo_compositor_mask (compositor, surface,
+ CAIRO_OPERATOR_SOURCE,
+ &composite->source_pattern.base,
+ &composite->mask_pattern.base,
+ NULL);
+ }
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return status;
+ }
+
+ return _paint_back_unbounded_surface (compositor, composite, surface);
+ }
+
+ if (_pattern_is_pixel_aligned (composite->original_source_pattern) &&
+ _pattern_is_pixel_aligned (composite->original_mask_pattern) &&
+ _clip_is_pixel_aligned (composite->clip))
+ is_pixel_aligned = TRUE;
+
+ if (! is_pixel_aligned) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_gl_composite_init (&setup,
+ op,
+ dst,
+ FALSE /* assume_component_alpha */);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_composite_set_source (&setup,
+ composite->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, FALSE);
+ if (unlikely (status))
+ goto finish;
+
+ if (composite->original_mask_pattern != NULL) {
+ status = _cairo_gl_composite_set_mask (&setup,
+ composite->original_mask_pattern,
+ &composite->mask_sample_area,
+ &composite->bounded,
+ FALSE);
+ }
+ if (unlikely (status))
+ goto finish;
+
+ /* if the source, mask and clip are pixel-aligned and
+ msaa is not active, we paint to texture directly */
+ if (! is_pixel_aligned || dst->msaa_active)
+ _cairo_gl_composite_set_multisample (&setup);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+
+ if (unlikely (status))
+ goto finish;
+
+ if (op != CAIRO_OPERATOR_OVER) {
+ if (! clip)
+ status = _draw_int_rect (ctx, &setup, &composite->bounded);
+ else
+ status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup, clip);
+ }
+ else {
+ /* fast path for CAIRO_OVER_OPERATOR */
+ cairo_rectangle_int_t rect, temp;
+
+ _cairo_surface_get_extents (&dst->base, &rect);
+ _cairo_pattern_get_extents (composite->original_source_pattern,
+ &temp);
+ _cairo_rectangle_intersect (&rect, &temp);
+ if (composite->original_mask_pattern) {
+ _cairo_pattern_get_extents (composite->original_mask_pattern,
+ &temp);
+ _cairo_rectangle_intersect (&rect, &temp);
+ }
+
+ if (clip) {
+ cairo_clip_t *clip_copy = _cairo_clip_copy (clip);
+
+ clip_copy = _cairo_clip_intersect_rectangle (clip_copy, &rect);
+ status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup,
+ clip_copy);
+ _cairo_clip_destroy (clip_copy);
+ }
+ else
+ status = _draw_int_rect (ctx, &setup, &rect);
+ }
+
+ if (unlikely (status))
+ goto finish;
+
+ dst->content_synced = FALSE;
+
+finish:
+ _cairo_gl_composite_fini (&setup);
+
+ if (ctx)
+ status = _cairo_gl_context_release (ctx, status);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_msaa_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite)
+{
+ return _cairo_gl_msaa_compositor_mask (compositor, composite);
+}
+
+static cairo_status_t
+_stroke_shaper_add_triangle (void *closure,
+ const cairo_point_t triangle[3])
+{
+ struct _tristrip_composite_info *info = closure;
+ return _cairo_gl_composite_emit_triangle_as_tristrip (info->ctx,
+ &info->setup,
+ triangle);
+}
+
+static cairo_status_t
+_stroke_shaper_add_triangle_fan (void *closure,
+ const cairo_point_t *midpoint,
+ const cairo_point_t *points,
+ int npoints)
+{
+ struct _tristrip_composite_info *info = closure;
+ return _draw_triangle_fan (info->ctx, &info->setup,
+ midpoint, points, npoints);
+}
+
+static cairo_status_t
+_stroke_shaper_add_quad (void *closure,
+ const cairo_point_t quad[4])
+{
+ struct _tristrip_composite_info *info = closure;
+ return _cairo_gl_composite_emit_quad_as_tristrip (info->ctx, &info->setup,
+ quad);
+}
+
+static cairo_bool_t
+_is_continuous_arc (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style)
+{
+ return (_cairo_path_fixed_is_single_arc (path) &&
+ style->dash == NULL);
+}
+
+static cairo_int_status_t
+_prevent_overlapping_strokes (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ cairo_composite_rectangles_t *composite,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm)
+{
+ cairo_rectangle_int_t stroke_extents;
+ const cairo_pattern_t *pattern = composite->original_source_pattern;
+ cairo_pattern_type_t type = cairo_pattern_get_type ((cairo_pattern_t *) pattern);
+
+ if (! _cairo_gl_ensure_stencil (ctx, setup->dst))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+
+ /* XXX: improve me - since we have lazy init, we cannot use sample
+ area */
+ if (type == CAIRO_PATTERN_TYPE_SOLID &&
+ _cairo_pattern_is_opaque_solid (pattern))
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ if (ctx->states_cache.stencil_test_enabled == FALSE) {
+ /* In case we have pending operations we have to flush before
+ adding the stencil buffer. */
+ _cairo_gl_composite_flush (ctx);
+
+ /* Enable the stencil buffer, even if we are not using it for clipping,
+ so we can use it below to prevent overlapping shapes. We initialize
+ it all to one here which represents infinite clip. */
+ if (! ctx->states_cache.depth_mask) {
+ ctx->dispatch.DepthMask (GL_TRUE);
+ ctx->states_cache.depth_mask = TRUE;
+ }
+ ctx->dispatch.Enable (GL_STENCIL_TEST);
+ ctx->states_cache.stencil_test_enabled = TRUE;
+
+ /* We scissor here so that we don't have to clear the entire stencil
+ * buffer. If the scissor test is already enabled, it was enabled
+ * for clipping. In that case, instead of calculating an intersection,
+ * we just reuse it, and risk clearing too much. */
+ if (ctx->states_cache.scissor_test_enabled == FALSE) {
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm,
+ &stroke_extents);
+ _cairo_gl_scissor_to_rectangle (setup->dst, &stroke_extents);
+ ctx->dispatch.Enable (GL_SCISSOR_TEST);
+ ctx->states_cache.scissor_test_enabled = TRUE;
+ }
+ ctx->dispatch.ClearStencil (1);
+ ctx->dispatch.Clear (GL_STENCIL_BUFFER_BIT);
+ _disable_scissor_buffer (ctx);
+
+ ctx->dispatch.StencilFunc (GL_EQUAL, 1, 1);
+ }
+
+ /* This means that once we draw to a particular pixel nothing else can
+ be drawn there until the stencil buffer is reset or the stencil test
+ is disabled. */
+ ctx->dispatch.StencilOp (GL_ZERO, GL_ZERO, GL_ZERO);
+
+ _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer);
+ setup->dst->clip_on_stencil_buffer = NULL;
+
+ /* we must let the next drawing know we have changed stencil buffer
+ * so that next drawing calls flush
+ */
+ setup->dst->needs_update = TRUE;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static void
+query_surface_capabilities (cairo_gl_surface_t *surface)
+{
+ GLint samples, stencil_bits;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+
+ /* Texture surfaces are create in such a way that they always
+ have stencil and multisample bits if possible, so we don't
+ need to query their capabilities lazily. */
+ if (_cairo_gl_surface_is_texture (surface))
+ return;
+ if (surface->stencil_and_msaa_caps_initialized)
+ return;
+
+ surface->stencil_and_msaa_caps_initialized = TRUE;
+ surface->supports_stencil = FALSE;
+ surface->supports_msaa = FALSE;
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return;
+
+ _cairo_gl_context_set_destination (ctx, surface, FALSE);
+
+ //ctx->dispatch.GetIntegerv(GL_SAMPLES, &samples);
+ //ctx->dispatch.GetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+ samples = 4;
+ stencil_bits = 4;
+ surface->supports_stencil = stencil_bits > 0;
+
+ surface->supports_msaa = samples > 1;
+
+ surface->num_samples = samples;
+
+ status = _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_int_status_t
+_cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_int_status_t status;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
+ struct _tristrip_composite_info info;
+ cairo_bool_t use_color_attribute;
+ cairo_rectangle_int_t stroke_extents;
+
+ if (! can_use_msaa_compositor (dst, antialias))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_path_fixed_stroke_is_rectilinear (path)) {
+ _cairo_path_fixed_approximate_fill_extents (path, &stroke_extents);
+
+ if (stroke_extents.width != 0 &&
+ stroke_extents.height != 0) {
+ if ((stroke_extents.width / stroke_extents.height > 10 &&
+ stroke_extents.height < 10) ||
+ (stroke_extents.height / stroke_extents.width > 10 &&
+ stroke_extents.width < 10)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+
+ if (composite->is_bounded == FALSE) {
+ cairo_surface_t* surface = _prepare_unbounded_surface (dst);
+
+ if (unlikely (surface == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_compositor_stroke (compositor, surface,
+ CAIRO_OPERATOR_SOURCE,
+ &composite->source_pattern.base,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return status;
+ }
+
+ return _paint_back_unbounded_surface (compositor, composite, surface);
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_gl_composite_init (&info.setup,
+ composite->op,
+ dst,
+ FALSE /* assume_component_alpha */);
+ if (unlikely (status))
+ return status;
+
+ info.ctx = NULL;
+ use_color_attribute = _cairo_gl_hairline_style_is_hairline (style, ctm);
+
+ status = _cairo_gl_composite_set_source (&info.setup,
+ composite->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, use_color_attribute);
+ if (unlikely (status))
+ goto finish;
+
+ _cairo_gl_msaa_compositor_set_clip (composite, &info.setup);
+ if (antialias != CAIRO_ANTIALIAS_NONE)
+ _cairo_gl_composite_set_multisample (&info.setup);
+
+ status = _cairo_gl_composite_begin (&info.setup, &info.ctx);
+ if (unlikely (status))
+ goto finish;
+
+ if (_cairo_gl_hairline_style_is_hairline (style, ctm)) {
+ cairo_gl_hairline_closure_t closure;
+
+ if (! (_is_continuous_arc (path, style) ||
+ _is_continuous_single_line (path, style))) {
+ status = _prevent_overlapping_strokes (info.ctx, &info.setup,
+ composite, path,
+ style, ctm);
+ if (unlikely (status))
+ goto finish;
+ }
+
+ closure.ctx = info.ctx;
+
+ closure.tolerance = tolerance;
+
+ status = _cairo_gl_path_fixed_stroke_to_hairline (path, &closure,
+ style, ctm,
+ ctm_inverse,
+ _cairo_gl_hairline_move_to,
+ style->dash ?
+ _cairo_gl_hairline_line_to_dashed :
+ _cairo_gl_hairline_line_to,
+ _cairo_gl_hairline_curve_to,
+ _cairo_gl_hairline_close_path);
+ goto finish;
+ }
+
+ if (!_is_continuous_single_line (path, style)) {
+ status = _prevent_overlapping_strokes (info.ctx, &info.setup,
+ composite, path, style, ctm);
+ if (unlikely (status))
+ goto finish;
+ }
+
+ status =
+ _cairo_path_fixed_stroke_to_shaper ((cairo_path_fixed_t *) path,
+ style,
+ ctm,
+ ctm_inverse,
+ tolerance,
+ _stroke_shaper_add_triangle,
+ _stroke_shaper_add_triangle_fan,
+ _stroke_shaper_add_quad,
+ &info);
+ if (unlikely (status))
+ goto finish;
+
+ dst->content_synced = FALSE;
+
+finish:
+ _cairo_gl_composite_fini (&info.setup);
+
+ if (info.ctx)
+ status = _cairo_gl_context_release (info.ctx, status);
+
+ return status;
+}
+
+static cairo_int_status_t
+_draw_simple_quad_path (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const cairo_path_fixed_t *path)
+{
+ cairo_point_t triangle[3];
+ cairo_int_status_t status;
+ const cairo_point_t *points;
+
+ points = cairo_path_head (path)->points;
+
+ triangle[0] = points[0];
+ triangle[1] = points[1];
+ triangle[2] = points[2];
+ status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle);
+ if (status)
+ return status;
+
+ triangle[0] = points[2];
+ triangle[1] = points[3];
+ triangle[2] = points[0];
+ return _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle);
+}
+
+static cairo_int_status_t
+_cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
+ cairo_gl_context_t *ctx = NULL;
+ cairo_int_status_t status;
+ cairo_traps_t traps;
+ cairo_bool_t draw_path_with_traps;
+ cairo_rectangle_int_t fill_extents;
+
+ if (! can_use_msaa_compositor (dst, antialias))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_path_fixed_fill_is_rectilinear (path)) {
+ _cairo_path_fixed_approximate_fill_extents (path, &fill_extents);
+
+ if (fill_extents.width != 0 && fill_extents.height != 0) {
+ if ((fill_extents.width / fill_extents.height > 10 &&
+ fill_extents.height < 10) ||
+ (fill_extents.height / fill_extents.width > 10 &&
+ fill_extents.width < 10)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+
+ if (composite->is_bounded == FALSE) {
+ cairo_surface_t* surface = _prepare_unbounded_surface (dst);
+
+ if (unlikely (surface == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+
+ status = _cairo_compositor_fill (compositor, surface,
+ CAIRO_OPERATOR_SOURCE,
+ &composite->source_pattern.base,
+ path, fill_rule, tolerance,
+ antialias, NULL);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return status;
+ }
+
+ return _paint_back_unbounded_surface (compositor, composite, surface);
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ status = _blit_texture_to_renderbuffer (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ draw_path_with_traps = ! _cairo_path_fixed_is_simple_quad (path);
+
+ if (draw_path_with_traps) {
+ _cairo_traps_init (&traps);
+ status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps);
+ if (unlikely (status))
+ goto cleanup_traps;
+ }
+
+ status = _cairo_gl_composite_init (&setup,
+ composite->op,
+ dst,
+ FALSE /* assume_component_alpha */);
+ if (unlikely (status))
+ goto cleanup_traps;
+
+ status = _cairo_gl_composite_set_source (&setup,
+ composite->original_source_pattern,
+ &composite->source_sample_area,
+ &composite->bounded,
+ FALSE, ! draw_path_with_traps);
+ if (unlikely (status))
+ goto cleanup_setup;
+
+ _cairo_gl_msaa_compositor_set_clip (composite, &setup);
+ if (antialias != CAIRO_ANTIALIAS_NONE)
+ _cairo_gl_composite_set_multisample (&setup);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto cleanup_setup;
+
+ if (! draw_path_with_traps)
+ status = _draw_simple_quad_path (ctx, &setup, path);
+ else
+ status = _draw_traps (ctx, &setup, &traps);
+ if (unlikely (status))
+ goto cleanup_setup;
+
+ dst->content_synced = FALSE;
+
+cleanup_setup:
+ _cairo_gl_composite_fini (&setup);
+
+ if (ctx)
+ status = _cairo_gl_context_release (ctx, status);
+
+cleanup_traps:
+ if (draw_path_with_traps)
+ _cairo_traps_fini (&traps);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_msaa_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_int_status_t status;
+ cairo_surface_t *src = NULL;
+ int src_x, src_y;
+ cairo_composite_glyphs_info_t info;
+
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
+
+ query_surface_capabilities (dst);
+ if (! dst->supports_stencil)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->op == CAIRO_OPERATOR_CLEAR)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->is_bounded == FALSE) {
+ cairo_surface_t* surface = _prepare_unbounded_surface (dst);
+
+ if (unlikely (surface == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_compositor_glyphs (compositor, surface,
+ CAIRO_OPERATOR_SOURCE,
+ &composite->source_pattern.base,
+ glyphs, num_glyphs,
+ scaled_font, composite->clip);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return status;
+ }
+
+ return _paint_back_unbounded_surface (compositor, composite, surface);
+ }
+
+ src = _cairo_gl_pattern_to_source (&dst->base,
+ composite->original_source_pattern,
+ FALSE,
+ &composite->bounded,
+ &composite->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (src->status)) {
+ status = src->status;
+ goto finish;
+ }
+
+ status = _cairo_gl_check_composite_glyphs (composite,
+ scaled_font, glyphs,
+ &num_glyphs);
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+ goto finish;
+
+ info.font = scaled_font;
+ info.glyphs = glyphs;
+ info.num_glyphs = num_glyphs;
+ info.use_mask = overlap || ! composite->is_bounded ||
+ composite->op == CAIRO_OPERATOR_SOURCE;
+ info.extents = composite->source;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ status = _cairo_gl_composite_glyphs_with_clip (dst, composite->op,
+ src, src_x, src_y,
+ 0, 0, &info,
+ composite->clip);
+
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ if (unlikely (status))
+ goto finish;
+
+ dst->content_synced = FALSE;
+
+finish:
+ cairo_surface_destroy (src);
+
+ return status;
+}
+
+static void
+_cairo_gl_msaa_compositor_init (cairo_compositor_t *compositor,
+ const cairo_compositor_t *delegate)
+{
+ compositor->delegate = delegate;
+ compositor->lazy_init = TRUE;
+
+ compositor->paint = _cairo_gl_msaa_compositor_paint;
+ compositor->mask = _cairo_gl_msaa_compositor_mask;
+ compositor->fill = _cairo_gl_msaa_compositor_fill;
+ compositor->stroke = _cairo_gl_msaa_compositor_stroke;
+ compositor->glyphs = _cairo_gl_msaa_compositor_glyphs;
+}
+
+const cairo_compositor_t *
+_cairo_gl_msaa_compositor_get (void)
+{
+ static cairo_compositor_t compositor;
+ if (compositor.delegate == NULL)
+ _cairo_gl_msaa_compositor_init (&compositor,
+ _cairo_gl_span_compositor_get ());
+
+ return &compositor;
+}
diff --git a/src/cairo-gl-operand.c b/src/cairo-gl-operand.c
new file mode 100755
index 000000000..8c21e6985
--- /dev/null
+++ b/src/cairo-gl-operand.c
@@ -0,0 +1,1500 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-subsurface-inline.h"
+#include "cairo-rtree-private.h"
+
+static cairo_int_status_t
+_cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst,
+ const cairo_gradient_pattern_t *pattern,
+ cairo_gl_gradient_t **gradient)
+{
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient);
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+static void
+_cairo_gl_image_cache_lock (cairo_gl_context_t *ctx,
+ cairo_gl_image_t *image_node)
+{
+ if (ctx->image_cache && ctx->image_cache->surface)
+ _cairo_rtree_pin (&ctx->image_cache->rtree, &image_node->node);
+}
+
+void
+_cairo_gl_image_cache_unlock (cairo_gl_context_t *ctx)
+{
+ if (ctx->image_cache && ctx->image_cache->surface)
+ _cairo_rtree_unpin (&(ctx->image_cache->rtree));
+}
+
+static cairo_int_status_t
+_cairo_gl_copy_texture (cairo_gl_surface_t *surface,
+ cairo_gl_surface_t *dst,
+ cairo_gl_surface_t *image,
+ int dst_x, int dst_y,
+ int src_x, int src_y,
+ int width, int height,
+ cairo_bool_t replace,
+ cairo_gl_context_t **ctx)
+{
+ cairo_int_status_t status;
+ cairo_gl_context_t *ctx_out;
+ cairo_gl_dispatch_t *dispatch;
+ cairo_gl_surface_t *target;
+ cairo_surface_pattern_t pattern;
+ cairo_rectangle_int_t rect;
+ cairo_clip_t *clip;
+
+ if (! _cairo_gl_surface_is_texture (image))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx_out);
+ if(unlikely (status))
+ return status;
+
+ if (replace)
+ _cairo_gl_composite_flush (ctx_out);
+
+ image->needs_to_cache = FALSE;
+ dispatch = &ctx_out->dispatch;
+ target = ctx_out->current_target;
+
+ /* paint image to dst */
+ _cairo_pattern_init_for_surface (&pattern, &image->base);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -dst_x + src_x, -dst_y + src_y);
+
+ rect.x = dst_x;
+ rect.y = dst_y;
+ rect.width = width;
+ rect.height = height;
+ clip = _cairo_clip_intersect_rectangle (NULL, &rect);
+
+ status = _cairo_surface_paint (&dst->base,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, clip);
+
+ _cairo_clip_destroy (clip);
+
+ _cairo_gl_composite_flush (ctx_out);
+ _cairo_pattern_fini (&pattern.base);
+ image->needs_to_cache = TRUE;
+
+ if (unlikely (status))
+ goto finish;
+
+ status = _cairo_gl_surface_resolve_multisampling (dst);
+
+finish:
+ /* restore ctx status */
+ if (target)
+ _cairo_gl_context_set_destination (ctx_out, target,
+ target->msaa_active);
+
+ *ctx = ctx_out;
+
+ if (unlikely (status))
+ return _cairo_gl_context_release (ctx_out, status);
+ return status;
+
+}
+
+static void
+_cairo_gl_copy_image_cache (cairo_rtree_node_t *node, void *data)
+{
+ cairo_gl_image_cache_t *new_cache = (cairo_gl_image_cache_t *)data;
+ cairo_gl_image_t *image_node = (cairo_gl_image_t *)node;
+ cairo_gl_image_t *new_image_node;
+ cairo_int_status_t status;
+ cairo_rtree_node_t *new_node = NULL;
+ int width, height;
+ cairo_gl_surface_t *image = (cairo_gl_surface_t *)image_node->original_surface;
+ cairo_gl_context_t *ctx = image_node->ctx;
+
+ if (node->state != CAIRO_RTREE_NODE_OCCUPIED || !image)
+ return;
+
+ width = image->width;
+ height = image->height;
+
+ status = _cairo_rtree_insert (&new_cache->rtree, width,
+ height, &new_node);
+
+ /* because new_cache has larger size, eviction will not happen */
+ if (unlikely (status))
+ {
+ new_cache->copy_success = FALSE;
+ return;
+ }
+
+ /* Paint image to cache. */
+ status = _cairo_gl_copy_texture (new_cache->surface,
+ new_cache->surface,
+ ctx->image_cache->surface,
+ new_node->x, new_node->y,
+ node->x, node->y,
+ width, height,
+ FALSE, &ctx);
+ if (unlikely (status)) {
+ new_cache->copy_success = FALSE;
+ return;
+ }
+
+ new_image_node = (cairo_gl_image_t *)new_node;
+ new_image_node->ctx = ctx;
+ new_image_node->original_surface = &image->base;
+ /* Coordinate. */
+ new_image_node->p1.x = new_node->x;
+ new_image_node->p1.y = new_node->y;
+ new_image_node->p2.x = new_node->x + image->width;
+ new_image_node->p2.y = new_node->y + image->height;
+ if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) {
+ new_image_node->p1.x /= new_cache->surface->width;
+ new_image_node->p2.x /= new_cache->surface->width;
+ new_image_node->p1.y /= new_cache->surface->height;
+ new_image_node->p2.y /= new_cache->surface->height;
+ }
+ image->content_changed = FALSE;
+
+ image_node->original_surface = NULL;
+
+ image->image_node = new_image_node;
+
+ _cairo_gl_image_cache_lock (ctx, new_image_node);
+ status = _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_int_status_t
+_cairo_gl_image_cache_replace_image (cairo_gl_image_t *image_node,
+ cairo_gl_surface_t *dst,
+ cairo_gl_surface_t *cache_surface,
+ cairo_gl_surface_t *image,
+ cairo_gl_context_t **ctx)
+{
+ cairo_int_status_t status;
+ /* Paint image to cache. */
+ status = _cairo_gl_copy_texture (dst, cache_surface,
+ image, image_node->node.x,
+ image_node->node.y,
+ 0, 0,
+ image->width, image->height,
+ TRUE,
+ ctx);
+ image->content_changed = FALSE;
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_image_cache_add_image (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *dst,
+ cairo_gl_surface_t *image,
+ cairo_gl_image_t **image_node)
+{
+ cairo_int_status_t status;
+ cairo_rtree_node_t *node = NULL;
+ int width, height;
+ cairo_bool_t replaced = FALSE;
+ int image_cache_size;
+
+ if (! image->base.device ||
+ (image->width >= IMAGE_CACHE_MAX_SIZE ||
+ image->height >= IMAGE_CACHE_MAX_SIZE))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ else if (! _cairo_gl_surface_is_texture (image))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ width = image->width;
+ height = image->height;
+
+ *image_node = image->image_node;
+
+ if (*image_node) {
+ if (image->content_changed) {
+ status = _cairo_gl_image_cache_replace_image (*image_node,
+ dst,
+ ctx->image_cache->surface,
+ image, &ctx);
+
+ if (unlikely (status))
+ return status;
+
+ replaced = TRUE;
+ }
+
+ _cairo_gl_image_cache_lock (ctx, *image_node);
+
+ image->content_changed = FALSE;
+ if (replaced)
+ return _cairo_gl_context_release (ctx, status);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (! ctx->image_cache) {
+ status = _cairo_gl_image_cache_init (ctx,
+ MIN_IMAGE_CACHE_WIDTH,
+ MIN_IMAGE_CACHE_HEIGHT,
+ &ctx->image_cache);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_rtree_insert (&ctx->image_cache->rtree, width,
+ height, &node);
+ /* Search for an unlocked slot. */
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_gl_image_cache_t *new_cache = NULL;
+
+ _cairo_gl_composite_flush (ctx);
+
+ image_cache_size = ((cairo_gl_surface_t *)(ctx->image_cache->surface))->width;
+ if (image_cache_size < MAX_IMAGE_CACHE_WIDTH) {
+ image_cache_size *= 2;
+ status = _cairo_gl_image_cache_init (ctx,
+ image_cache_size,
+ image_cache_size,
+ &new_cache);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ /* copy existing image cache to new image cache */
+ _cairo_rtree_foreach (&ctx->image_cache->rtree,
+ _cairo_gl_copy_image_cache,
+ (void *)new_cache);
+ if (new_cache->copy_success) {
+ _cairo_gl_image_cache_fini (ctx);
+ ctx->image_cache = new_cache;
+ }
+ else {
+ _cairo_rtree_fini (&new_cache->rtree);
+ cairo_surface_destroy (&new_cache->surface->base);
+ free (new_cache);
+ new_cache = NULL;
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+ if (!new_cache)
+ status = _cairo_rtree_evict_random (&ctx->image_cache->rtree,
+ width, height, &node);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ if (! node)
+ status = _cairo_rtree_insert (&ctx->image_cache->rtree,
+ width, height, &node);
+ else
+ status = _cairo_rtree_node_insert (&ctx->image_cache->rtree,
+ node, width, height, &node);
+ }
+ }
+
+ if (unlikely (status))
+ return status;
+
+ /* Paint image to cache. */
+ status = _cairo_gl_copy_texture (dst, ctx->image_cache->surface,
+ image, node->x, node->y,
+ 0, 0,
+ image->width, image->height,
+ FALSE, &ctx);
+ if (unlikely (status))
+ return status;
+
+ *image_node = (cairo_gl_image_t *)node;
+ (*image_node)->ctx = ctx;
+ (*image_node)->original_surface = &image->base;
+ /* Coordinate. */
+ (*image_node)->p1.x = node->x;
+ (*image_node)->p1.y = node->y;
+ (*image_node)->p2.x = node->x + image->width;
+ (*image_node)->p2.y = node->y + image->height;
+ if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) {
+ (*image_node)->p1.x /= ctx->image_cache->surface->width;
+ (*image_node)->p2.x /= ctx->image_cache->surface->width;
+ (*image_node)->p1.y /= ctx->image_cache->surface->height;
+ (*image_node)->p2.y /= ctx->image_cache->surface->height;
+ }
+ image->content_changed = FALSE;
+
+ image->image_node = *image_node;
+
+ _cairo_gl_image_cache_lock (ctx, *image_node);
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_status_t
+_cairo_gl_subsurface_clone_operand_init (cairo_gl_operand_t *operand,
+ const cairo_pattern_t *_src,
+ cairo_gl_surface_t *dst,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen)
+{
+ const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
+ cairo_surface_pattern_t local_pattern;
+ cairo_surface_subsurface_t *sub;
+ cairo_gl_surface_t *surface;
+ cairo_gl_context_t *ctx;
+ cairo_surface_attributes_t *attributes;
+ cairo_status_t status;
+
+ sub = (cairo_surface_subsurface_t *) src->surface;
+
+ if (sub->snapshot &&
+ sub->snapshot->type == CAIRO_SURFACE_TYPE_GL &&
+ sub->snapshot->device == dst->base.device)
+ {
+ surface = (cairo_gl_surface_t *)
+ cairo_surface_reference (sub->snapshot);
+ }
+ else
+ {
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ /* XXX Trim surface to the sample area within the subsurface? */
+ surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx,
+ sub->target->content,
+ sub->extents.width,
+ sub->extents.height);
+ if (surface->base.status)
+ return _cairo_gl_context_release (ctx, surface->base.status);
+
+ _cairo_pattern_init_for_surface (&local_pattern, sub->target);
+ cairo_matrix_init_translate (&local_pattern.base.matrix,
+ sub->extents.x, sub->extents.y);
+ local_pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (&surface->base,
+ CAIRO_OPERATOR_SOURCE,
+ &local_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&local_pattern.base);
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ return status;
+ }
+
+ _cairo_surface_subsurface_set_snapshot (&sub->base, &surface->base);
+ }
+
+ status = _cairo_gl_surface_resolve_multisampling (surface);
+ if (unlikely (status))
+ return status;
+
+ attributes = &operand->texture.attributes;
+
+ operand->type = CAIRO_GL_OPERAND_TEXTURE;
+ operand->texture.surface = surface;
+ operand->texture.owns_surface = surface;
+ operand->texture.tex = surface->tex;
+ operand->texture.use_atlas = FALSE;
+
+ if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) {
+ attributes->matrix = src->base.matrix;
+ } else {
+ cairo_matrix_t m;
+
+ cairo_matrix_init_scale (&m,
+ 1.0 / surface->width,
+ 1.0 / surface->height);
+ cairo_matrix_multiply (&attributes->matrix, &src->base.matrix, &m);
+ }
+
+ attributes->extend = src->base.extend;
+ attributes->filter = src->base.filter;
+ attributes->has_component_alpha = src->base.has_component_alpha;
+
+ operand->texture.texgen = use_texgen;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_subsurface_operand_init (cairo_gl_operand_t *operand,
+ const cairo_pattern_t *_src,
+ cairo_gl_surface_t *dst,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen)
+{
+ const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
+ cairo_surface_subsurface_t *sub;
+ cairo_gl_surface_t *surface, *blur_surface;
+ cairo_surface_attributes_t *attributes;
+ cairo_int_status_t status;
+ cairo_gl_image_t *image_node = NULL;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ cairo_bool_t ctx_acquired = FALSE;
+ cairo_rectangle_int_t blur_extents;
+
+ sub = (cairo_surface_subsurface_t *) src->surface;
+
+ if (sample->x < 0 || sample->y < 0 ||
+ sample->x + sample->width > sub->extents.width ||
+ sample->y + sample->height > sub->extents.height)
+ {
+ return _cairo_gl_subsurface_clone_operand_init (operand, _src,
+ dst, sample, extents,
+ use_texgen);
+ }
+
+ surface = (cairo_gl_surface_t *) sub->target;
+ if (surface->base.device && (surface->base.device != dst->base.device ||
+ (! _cairo_gl_surface_is_texture (surface) && ! surface->bounded_tex)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_gl_surface_resolve_multisampling (surface);
+ if (unlikely (status))
+ return status;
+
+ blur_extents.x = blur_extents.y = 0;
+ blur_extents.width = cairo_gl_surface_get_width (&surface->base);
+ blur_extents.height = cairo_gl_surface_get_height (&surface->base);
+
+ blur_surface = _cairo_gl_gaussian_filter (dst, src, surface, &blur_extents);
+
+ _cairo_gl_operand_copy(operand, &surface->operand);
+ *operand = surface->operand;
+ operand->texture.use_atlas = FALSE;
+
+ attributes = &operand->texture.attributes;
+ attributes->extend = src->base.extend;
+ attributes->filter = src->base.filter;
+ attributes->has_component_alpha = src->base.has_component_alpha;
+
+ attributes->matrix = src->base.matrix;
+ attributes->matrix.x0 += sub->extents.x;
+ attributes->matrix.y0 += sub->extents.y;
+
+ operand->texture.texgen = use_texgen;
+
+
+ if (blur_surface == surface &&
+ surface->needs_to_cache &&
+ surface->base.device) {
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ ctx_acquired = TRUE;
+ status = _cairo_gl_image_cache_add_image (ctx, dst, surface,
+ &image_node);
+ }
+ }
+
+ /* Translate the matrix from
+ * (unnormalized src -> unnormalized src) to
+ * (unnormalized dst -> unnormalized src)
+ */
+
+ if (unlikely (status) || ! image_node) {
+ if (blur_surface == surface &&
+ blur_surface->operand.type != CAIRO_GL_OPERAND_GAUSSIAN) {
+ cairo_matrix_multiply (&attributes->matrix,
+ &attributes->matrix,
+ &surface->operand.texture.attributes.matrix);
+ }
+ else {
+ cairo_matrix_t matrix = src->base.matrix;
+ operand->texture.use_atlas = TRUE;
+ attributes->extend = CAIRO_EXTEND_NONE;
+ operand->texture.extend = src->base.extend;
+
+ operand->texture.p1.x = 0;
+ operand->texture.p1.y = 0;
+ operand->texture.p2.x = (double) blur_extents.width / (double) blur_surface->width;
+ operand->texture.p2.y = (double) blur_extents.height / (double) blur_surface->height;
+
+ operand->texture.p1.x += 0.5 / blur_surface->width;
+ operand->texture.p1.y += 0.5 / blur_surface->height;
+ operand->texture.p2.x -= 0.5 / blur_surface->width;
+ operand->texture.p2.y -= 0.5 / blur_surface->height;
+
+
+ operand->texture.surface = blur_surface;
+ operand->texture.owns_surface = NULL;
+ operand->texture.tex = blur_surface->tex;
+ if (blur_surface->blur_stage == CAIRO_GL_BLUR_STAGE_2)
+ cairo_matrix_scale (&attributes->matrix,
+ (double)blur_extents.width/(double)surface->width,
+ (double)blur_extents.height/(double)surface->height);
+ cairo_matrix_multiply (&attributes->matrix,
+ &matrix,
+ &attributes->matrix);
+
+ }
+ }
+ else {
+ cairo_matrix_t matrix = src->base.matrix;
+ operand->texture.surface = ctx->image_cache->surface;
+ operand->texture.owns_surface = NULL;
+ operand->texture.tex = ctx->image_cache->surface->tex;
+ attributes->extend = CAIRO_EXTEND_NONE;
+ operand->texture.extend = src->base.extend;
+ attributes->matrix.x0 = image_node->node.x + sub->extents.x;
+ attributes->matrix.y0 = image_node->node.y + sub->extents.y;
+ operand->texture.use_atlas = TRUE;
+
+ operand->texture.p1.x = image_node->p1.x;
+ operand->texture.p1.y = image_node->p1.y;
+ operand->texture.p2.x = image_node->p2.x;
+ operand->texture.p2.y = image_node->p2.y;
+
+ operand->texture.p1.x += 0.5 / ctx->image_cache->surface->width;
+ operand->texture.p1.y += 0.5 / ctx->image_cache->surface->height;
+ operand->texture.p2.x -= 0.5 / ctx->image_cache->surface->width;
+ operand->texture.p2.y -= 0.5 / ctx->image_cache->surface->height;
+
+
+ cairo_matrix_multiply (&attributes->matrix,
+ &matrix,
+ &ctx->image_cache->surface->operand.texture.attributes.matrix);
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ cairo_surface_destroy (&blur_surface->base);
+
+ if (ctx_acquired)
+ return _cairo_gl_context_release (ctx, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_surface_operand_init (cairo_gl_operand_t *operand,
+ const cairo_pattern_t *_src,
+ cairo_gl_surface_t *dst,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen)
+{
+ const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
+ cairo_gl_surface_t *surface, *blur_surface;
+ cairo_surface_attributes_t *attributes;
+ cairo_int_status_t status;
+ cairo_gl_image_t *image_node = NULL;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ cairo_bool_t ctx_acquired = FALSE;
+ cairo_rectangle_int_t blur_extents;
+
+ surface = (cairo_gl_surface_t *) src->surface;
+ if (surface->base.type != CAIRO_SURFACE_TYPE_GL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->base.backend->type != CAIRO_SURFACE_TYPE_GL) {
+ if (_cairo_surface_is_subsurface (&surface->base))
+ return _cairo_gl_subsurface_operand_init (operand, _src, dst,
+ sample, extents,
+ use_texgen);
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (surface->base.device && surface->base.device != dst->base.device)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->base.device && ! _cairo_gl_surface_is_texture (surface) &&
+ ! surface->bounded_tex)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_gl_surface_resolve_multisampling (surface);
+ if (unlikely (status))
+ return status;
+
+ blur_extents.x = blur_extents.y = 0;
+ blur_extents.width = cairo_gl_surface_get_width (&surface->base);
+ blur_extents.height = cairo_gl_surface_get_height (&surface->base);
+
+ blur_surface = _cairo_gl_gaussian_filter (dst, src, surface, &blur_extents);
+
+ _cairo_gl_operand_copy(operand, &blur_surface->operand);
+ operand->texture.use_atlas = FALSE;
+
+ attributes = &operand->texture.attributes;
+ attributes->extend = src->base.extend;
+ attributes->filter = src->base.filter;
+ attributes->has_component_alpha = src->base.has_component_alpha;
+
+ operand->texture.texgen = use_texgen;
+
+ if (surface->base.device &&
+ blur_surface == surface &&
+ surface->needs_to_cache) {
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ ctx_acquired = TRUE;
+ status = _cairo_gl_image_cache_add_image (ctx, dst, surface,
+ &image_node);
+ }
+ }
+
+ if (unlikely (status) || ! image_node) {
+ if (blur_surface == surface &&
+ blur_surface->operand.type != CAIRO_GL_OPERAND_GAUSSIAN) {
+ cairo_matrix_multiply (&attributes->matrix,
+ &src->base.matrix,
+ &attributes->matrix);
+ }
+ else {
+ cairo_matrix_t matrix = src->base.matrix;
+ operand->texture.use_atlas = TRUE;
+ attributes->extend = CAIRO_EXTEND_NONE;
+ operand->texture.extend = src->base.extend;
+
+ operand->texture.p1.x = 0;
+ operand->texture.p1.y = 0;
+ operand->texture.p2.x = (double) blur_extents.width / (double) blur_surface->width;
+ operand->texture.p2.y = (double) blur_extents.height / (double) blur_surface->height;
+
+ operand->texture.p1.x += 0.5 / blur_surface->width;
+ operand->texture.p1.y += 0.5 / blur_surface->height;
+ operand->texture.p2.x -= 0.5 / blur_surface->width;
+ operand->texture.p2.y -= 0.5 / blur_surface->height;
+
+
+ operand->texture.surface = blur_surface;
+ operand->texture.owns_surface = NULL;
+ operand->texture.tex = blur_surface->tex;
+ if (blur_surface->blur_stage == CAIRO_GL_BLUR_STAGE_2)
+ cairo_matrix_scale (&attributes->matrix,
+ (double)blur_extents.width/(double)surface->width,
+ (double)blur_extents.height/(double)surface->height);
+ cairo_matrix_multiply (&attributes->matrix,
+ &matrix,
+ &attributes->matrix);
+
+ }
+ }
+ else {
+ cairo_matrix_t matrix = src->base.matrix;
+ operand->texture.use_atlas = TRUE;
+ attributes->extend = CAIRO_EXTEND_NONE;
+ operand->texture.extend = src->base.extend;
+
+ operand->texture.p1.x = image_node->p1.x;
+ operand->texture.p1.y = image_node->p1.y;
+ operand->texture.p2.x = image_node->p2.x;
+ operand->texture.p2.y = image_node->p2.y;
+
+ operand->texture.p1.x += 0.5 / ctx->image_cache->surface->width;
+ operand->texture.p1.y += 0.5 / ctx->image_cache->surface->height;
+ operand->texture.p2.x -= 0.5 / ctx->image_cache->surface->width;
+ operand->texture.p2.y -= 0.5 / ctx->image_cache->surface->height;
+
+
+ operand->texture.surface = ctx->image_cache->surface;
+ operand->texture.owns_surface = NULL;
+ operand->texture.tex = ctx->image_cache->surface->tex;
+ matrix.x0 += image_node->node.x;
+ matrix.y0 += image_node->node.y;
+ cairo_matrix_multiply (&attributes->matrix,
+ &matrix,
+ &ctx->image_cache->surface->operand.texture.attributes.matrix);
+ }
+
+ cairo_surface_destroy (&blur_surface->base);
+
+ status = CAIRO_STATUS_SUCCESS;
+
+ if (ctx_acquired)
+ return _cairo_gl_context_release (ctx, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand,
+ const cairo_pattern_t *_src,
+ cairo_gl_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+ cairo_gl_surface_t *surface;
+ cairo_gl_context_t *ctx;
+ cairo_image_surface_t *image;
+ cairo_bool_t src_is_gl_surface = FALSE;
+ cairo_rectangle_int_t map_extents;
+
+ if (_src->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_t* src_surface = ((cairo_surface_pattern_t *) _src)->surface;
+ src_is_gl_surface = src_surface->type == CAIRO_SURFACE_TYPE_GL;
+ }
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ extents->width, extents->height);
+ map_extents = *extents;
+ map_extents.x = map_extents.y = 0;
+ image = _cairo_surface_map_to_image (&surface->base, &map_extents);
+
+ /* If the pattern is a GL surface, it belongs to some other GL context,
+ so we need to release this device while we paint it to the image. */
+ if (src_is_gl_surface) {
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status))
+ goto fail;
+
+ /* we need to release one more time */
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status))
+ goto fail;
+ }
+
+ status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y,
+ CAIRO_OPERATOR_SOURCE, _src, NULL);
+
+ if (src_is_gl_surface) {
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ goto fail;
+ /* one more time acquire */
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ goto fail;
+ }
+
+ status = _cairo_surface_unmap_image (&surface->base, image);
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status))
+ goto fail;
+
+ *operand = surface->operand;
+ operand->texture.owns_surface = surface;
+ operand->texture.attributes.matrix.x0 -= extents->x * operand->texture.attributes.matrix.xx;
+ operand->texture.attributes.matrix.y0 -= extents->y * operand->texture.attributes.matrix.yy;
+
+ if (_cairo_gl_surface_is_texture (dst) &&
+ dst->width <= IMAGE_CACHE_MAX_SIZE &&
+ dst->height <= IMAGE_CACHE_MAX_SIZE &&
+ ! dst->force_no_cache)
+ dst->needs_to_cache = TRUE;
+
+ operand->texture.use_atlas = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail:
+ cairo_surface_destroy (&surface->base);
+ cairo_surface_destroy (image);
+ return status;
+}
+
+void
+_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand,
+ const cairo_color_t *color)
+{
+ operand->type = CAIRO_GL_OPERAND_CONSTANT;
+ operand->constant.color[0] = color->red * color->alpha;
+ operand->constant.color[1] = color->green * color->alpha;
+ operand->constant.color[2] = color->blue * color->alpha;
+ operand->constant.color[3] = color->alpha;
+}
+
+void
+_cairo_gl_operand_translate (cairo_gl_operand_t *operand,
+ double tx, double ty)
+{
+ switch (operand->type) {
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ operand->texture.attributes.matrix.x0 -= tx * operand->texture.attributes.matrix.xx;
+ operand->texture.attributes.matrix.y0 -= ty * operand->texture.attributes.matrix.yy;
+ break;
+
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ operand->gradient.m.x0 -= tx * operand->gradient.m.xx;
+ operand->gradient.m.y0 -= ty * operand->gradient.m.yy;
+ break;
+
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ case CAIRO_GL_OPERAND_COUNT:
+ default:
+ break;
+ }
+}
+
+static cairo_status_t
+_cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand,
+ const cairo_pattern_t *pattern,
+ cairo_gl_surface_t *dst,
+ cairo_bool_t use_texgen)
+{
+ const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *)pattern;
+ cairo_status_t status;
+
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ if (! _cairo_gl_device_has_glsl (dst->base.device))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_gl_create_gradient_texture (dst,
+ gradient,
+ &operand->gradient.gradient);
+ if (unlikely (status))
+ return status;
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ double x0, y0, dx, dy, sf, offset;
+
+ dx = linear->pd2.x - linear->pd1.x;
+ dy = linear->pd2.y - linear->pd1.y;
+ sf = 1.0 / (dx * dx + dy * dy);
+ dx *= sf;
+ dy *= sf;
+
+ x0 = linear->pd1.x;
+ y0 = linear->pd1.y;
+ offset = dx * x0 + dy * y0;
+
+ operand->type = CAIRO_GL_OPERAND_LINEAR_GRADIENT;
+
+ cairo_matrix_init (&operand->gradient.m, dx, 0, dy, 1, -offset, 0);
+ if (! _cairo_matrix_is_identity (&pattern->matrix)) {
+ cairo_matrix_multiply (&operand->gradient.m,
+ &pattern->matrix,
+ &operand->gradient.m);
+ }
+ } else {
+ cairo_matrix_t m;
+ cairo_circle_double_t circles[2];
+ double x0, y0, r0, dx, dy, dr;
+ double scale = 1.0;
+ cairo_radial_pattern_t *radial_pattern = (cairo_radial_pattern_t *)gradient;
+
+ /*
+ * Some fragment shader implementations use half-floats to
+ * represent numbers, so the maximum number they can represent
+ * is about 2^14. Some intermediate computations used in the
+ * radial gradient shaders can produce results of up to 2*k^4.
+ * Setting k=8 makes the maximum result about 8192 (assuming
+ * that the extreme circles are not much smaller than the
+ * destination image).
+ */
+ _cairo_gradient_pattern_fit_to_range (gradient, 8.,
+ &operand->gradient.m, circles);
+
+ /*
+ * Instead of using scaled data that might introducing rounding
+ * errors, we use original data directly
+ */
+ if (circles[0].center.x)
+ scale = radial_pattern->cd1.center.x / circles[0].center.x;
+ else if (circles[0].center.y)
+ scale = radial_pattern->cd1.center.y / circles[0].center.y;
+ else if (circles[0].radius)
+ scale = radial_pattern->cd1.radius / circles[0].radius;
+ else if (circles[1].center.x)
+ scale = radial_pattern->cd2.center.x / circles[1].center.x;
+ else if (circles[1].center.y)
+ scale = radial_pattern->cd2.center.y / circles[1].center.y;
+ else if (circles[1].radius)
+ scale = radial_pattern->cd2.radius / circles[1].radius;
+
+ x0 = circles[0].center.x;
+ y0 = circles[0].center.y;
+ r0 = circles[0].radius;
+ dx = radial_pattern->cd2.center.x - radial_pattern->cd1.center.x;
+ dy = radial_pattern->cd2.center.y - radial_pattern->cd1.center.y;
+ dr = radial_pattern->cd2.radius - radial_pattern->cd1.radius;
+
+ operand->gradient.a = (dx * dx + dy * dy - dr * dr)/(scale * scale);
+ operand->gradient.radius_0 = r0;
+ operand->gradient.circle_d.center.x = dx / scale;
+ operand->gradient.circle_d.center.y = dy / scale;
+ operand->gradient.circle_d.radius = dr / scale;
+
+ if (operand->gradient.a == 0)
+ operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0;
+ else if (pattern->extend == CAIRO_EXTEND_NONE)
+ operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE;
+ else
+ operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT;
+
+ cairo_matrix_init_translate (&m, -x0, -y0);
+ cairo_matrix_multiply (&operand->gradient.m,
+ &operand->gradient.m,
+ &m);
+ }
+
+ operand->gradient.extend = pattern->extend;
+ operand->gradient.texgen = use_texgen;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_operand_copy (cairo_gl_operand_t *dst,
+ const cairo_gl_operand_t *src)
+{
+ *dst = *src;
+ switch (dst->type) {
+ case CAIRO_GL_OPERAND_CONSTANT:
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ _cairo_gl_gradient_reference (dst->gradient.gradient);
+ break;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ cairo_surface_reference (&dst->texture.owns_surface->base);
+ break;
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ break;
+ }
+}
+
+void
+_cairo_gl_operand_destroy (cairo_gl_operand_t *operand)
+{
+ switch (operand->type) {
+ case CAIRO_GL_OPERAND_CONSTANT:
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ _cairo_gl_gradient_destroy (operand->gradient.gradient);
+ break;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ cairo_surface_destroy (&operand->texture.owns_surface->base);
+ break;
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ cairo_surface_destroy (&operand->texture.owns_surface->base);
+ break;
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ break;
+ }
+
+ operand->type = CAIRO_GL_OPERAND_NONE;
+}
+
+cairo_int_status_t
+_cairo_gl_operand_init (cairo_gl_operand_t *operand,
+ const cairo_pattern_t *pattern,
+ cairo_gl_surface_t *dst,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen,
+ cairo_bool_t encode_color_as_attribute)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s: type=%d\n", __FUNCTION__, pattern->type));
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ _cairo_gl_solid_operand_init (operand,
+ &((cairo_solid_pattern_t *) pattern)->color);
+ operand->constant.encode_as_attribute = encode_color_as_attribute;
+ return CAIRO_STATUS_SUCCESS;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ status = _cairo_gl_surface_operand_init (operand, pattern, dst,
+ sample, extents, use_texgen);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ break;
+
+ return status;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _cairo_gl_gradient_operand_init (operand, pattern, dst,
+ use_texgen);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ break;
+
+ return status;
+
+ default:
+ case CAIRO_PATTERN_TYPE_MESH:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ break;
+ }
+
+ return _cairo_gl_pattern_texture_setup (operand, pattern, dst, extents);
+}
+
+cairo_filter_t
+_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand)
+{
+ cairo_filter_t filter;
+
+ switch ((int) operand->type) {
+ case CAIRO_GL_OPERAND_TEXTURE:
+ filter = operand->texture.attributes.filter;
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ filter = CAIRO_FILTER_BILINEAR;
+ break;
+ default:
+ filter = CAIRO_FILTER_DEFAULT;
+ break;
+ }
+
+ return filter;
+}
+
+GLint
+_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand)
+{
+ cairo_filter_t filter = _cairo_gl_operand_get_filter (operand);
+
+ if (filter == CAIRO_FILTER_GAUSSIAN)
+ return GL_LINEAR;
+
+ return filter != CAIRO_FILTER_FAST && filter != CAIRO_FILTER_NEAREST ?
+ GL_LINEAR :
+ GL_NEAREST;
+}
+
+cairo_bool_t
+_cairo_gl_operand_get_use_atlas (cairo_gl_operand_t *operand)
+{
+ if (operand->type != CAIRO_GL_OPERAND_TEXTURE &&
+ operand->type != CAIRO_GL_OPERAND_GAUSSIAN)
+ return FALSE;
+
+ return operand->texture.use_atlas;
+}
+
+cairo_extend_t
+_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand)
+{
+ cairo_extend_t extend;
+
+ switch ((int) operand->type) {
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (! operand->texture.use_atlas)
+ extend = operand->texture.attributes.extend;
+ else
+ extend = operand->texture.extend;
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ extend = operand->gradient.extend;
+ break;
+ default:
+ extend = CAIRO_EXTEND_NONE;
+ break;
+ }
+
+ return extend;
+}
+
+cairo_extend_t
+_cairo_gl_operand_get_atlas_extend (cairo_gl_operand_t *operand)
+{
+ cairo_extend_t extend;
+
+ switch ((int) operand->type) {
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (operand->texture.use_atlas)
+ extend = operand->texture.extend;
+ else
+ extend = CAIRO_EXTEND_NONE;
+ break;
+ default:
+ extend = CAIRO_EXTEND_NONE;
+ break;
+ }
+
+ return extend;
+}
+
+void
+_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
+ cairo_gl_operand_t *operand,
+ cairo_gl_tex_t tex_unit)
+{
+ const cairo_matrix_t *texgen = NULL;
+
+ switch (operand->type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ return;
+ case CAIRO_GL_OPERAND_CONSTANT:
+ if (operand->constant.encode_as_attribute)
+ return;
+
+ _cairo_gl_shader_bind_vec4 (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_CONSTANT, tex_unit),
+ operand->constant.color[0],
+ operand->constant.color[1],
+ operand->constant.color[2],
+ operand->constant.color[3]);
+ return;
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_A, tex_unit),
+ operand->gradient.a);
+ /* fall through */
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ _cairo_gl_shader_bind_vec3 (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_CIRCLE_D, tex_unit),
+ operand->gradient.circle_d.center.x,
+ operand->gradient.circle_d.center.y,
+ operand->gradient.circle_d.radius);
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_RADIUS_0, tex_unit),
+ operand->gradient.radius_0);
+ /* fall through */
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ /*
+ * For GLES2 we use shaders to implement GL_CLAMP_TO_BORDER (used
+ * with CAIRO_EXTEND_NONE). When bilinear filtering is enabled,
+ * these shaders need the texture dimensions for their calculations.
+ */
+ if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) &&
+ _cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE &&
+ _cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR)
+ {
+ float width, height;
+ if (operand->type == CAIRO_GL_OPERAND_TEXTURE ||
+ operand->type == CAIRO_GL_OPERAND_GAUSSIAN) {
+ width = operand->texture.surface->width;
+ height = operand->texture.surface->height;
+ }
+ else {
+ width = operand->gradient.gradient->cache_entry.size,
+ height = 1;
+ }
+ if (operand->type != CAIRO_GL_OPERAND_GAUSSIAN)
+ _cairo_gl_shader_bind_vec2 (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_TEXDIMS, tex_unit),
+ width, height);
+ }
+
+ break;
+ }
+
+ if (operand->type == CAIRO_GL_OPERAND_GAUSSIAN &&
+ operand->pass == 1) {
+ float x_axis = 1.0;
+ float y_axis = 0.0;
+ float temp_width;
+ float near_zero = 0.00001;
+
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_X_AXIS, tex_unit),
+ x_axis);
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_Y_AXIS, tex_unit),
+ y_axis);
+ _cairo_gl_shader_bind_int (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_RADIUS, tex_unit),
+ operand->texture.x_radius);
+
+ temp_width = cairo_gl_surface_get_width (&operand->texture.surface->base);
+ temp_width = (temp_width == 0) ? near_zero : temp_width;
+
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_STEP, tex_unit),
+ 1.0 / temp_width);
+
+ _cairo_gl_shader_bind_float_array (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLURS, tex_unit),
+ operand->texture.x_radius * 2 + 1,
+ &operand->texture.coef[0]);
+ }
+ else if (operand->type == CAIRO_GL_OPERAND_GAUSSIAN &&
+ operand->pass == 2) {
+ float x_axis = 0.0;
+ float y_axis = 1.0;
+
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_X_AXIS, tex_unit),
+ x_axis);
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_Y_AXIS, tex_unit),
+ y_axis);
+ _cairo_gl_shader_bind_int (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_RADIUS, tex_unit),
+ operand->texture.y_radius);
+
+ _cairo_gl_shader_bind_float (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLUR_STEP, tex_unit),
+ 1.0 / cairo_gl_surface_get_height (&operand->texture.surface->base));
+
+ _cairo_gl_shader_bind_float_array (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_BLURS, tex_unit),
+ operand->texture.y_radius * 2 + 1,
+ &operand->texture.coef[0]);
+ }
+
+ if (operand->type == CAIRO_GL_OPERAND_TEXTURE ||
+ operand->type == CAIRO_GL_OPERAND_GAUSSIAN) {
+ if (operand->texture.texgen)
+ texgen = &operand->texture.attributes.matrix;
+ } else {
+ if (operand->gradient.texgen)
+ texgen = &operand->gradient.m;
+ }
+
+ if (texgen) {
+ _cairo_gl_shader_bind_matrix (ctx,
+ _cairo_gl_shader_uniform_for_texunit (
+ CAIRO_GL_UNIFORM_TEXGEN, tex_unit),
+ texgen);
+ }
+}
+
+cairo_bool_t
+_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest,
+ cairo_gl_operand_t *source,
+ unsigned int vertex_offset)
+{
+ if (dest->type != source->type)
+ return TRUE;
+ if (dest->vertex_offset != vertex_offset)
+ return TRUE;
+
+ switch (source->type) {
+ case CAIRO_GL_OPERAND_NONE:
+ return FALSE;
+ case CAIRO_GL_OPERAND_CONSTANT:
+ if (dest->constant.encode_as_attribute &&
+ source->constant.encode_as_attribute)
+ return FALSE;
+ if (dest->constant.encode_as_attribute !=
+ source->constant.encode_as_attribute)
+ return TRUE;
+ return dest->constant.color[0] != source->constant.color[0] ||
+ dest->constant.color[1] != source->constant.color[1] ||
+ dest->constant.color[2] != source->constant.color[2] ||
+ dest->constant.color[3] != source->constant.color[3];
+ case CAIRO_GL_OPERAND_TEXTURE:
+ return dest->texture.surface != source->texture.surface ||
+ dest->texture.attributes.extend != source->texture.attributes.extend ||
+ dest->texture.attributes.filter != source->texture.attributes.filter ||
+ dest->texture.attributes.has_component_alpha != source->texture.attributes.has_component_alpha;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ /* XXX: improve this */
+ return TRUE;
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ return TRUE;
+}
+
+unsigned int
+_cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand)
+{
+ switch (operand->type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ return operand->constant.encode_as_attribute ? 4 * sizeof (GLfloat) : 0;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (operand->texture.texgen) {
+ if (operand->texture.use_atlas)
+ return 4 * sizeof (GLfloat);
+ return 0;
+ }
+ else {
+ if (operand->texture.use_atlas)
+ return 6 * sizeof (GLfloat);
+ return 2 * sizeof (GLfloat);
+ }
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ return operand->gradient.texgen ? 0 : 2 * sizeof (GLfloat);
+ }
+}
+
+void
+_cairo_gl_operand_emit (cairo_gl_operand_t *operand,
+ GLfloat ** vb,
+ GLfloat x,
+ GLfloat y)
+{
+ switch (operand->type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ break;
+ case CAIRO_GL_OPERAND_CONSTANT: {
+ if (operand->constant.encode_as_attribute) {
+ *(*vb)++ = operand->constant.color[0];
+ *(*vb)++ = operand->constant.color[1];
+ *(*vb)++ = operand->constant.color[2];
+ *(*vb)++ = operand->constant.color[3];
+ }
+ break;
+ }
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ if (! operand->gradient.texgen) {
+ double s = x;
+ double t = y;
+
+ cairo_matrix_transform_point (&operand->gradient.m, &s, &t);
+
+ *(*vb)++ = s;
+ *(*vb)++ = t;
+ }
+ break;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (! operand->texture.texgen) {
+ cairo_surface_attributes_t *src_attributes = &operand->texture.attributes;
+ double s = x;
+ double t = y;
+
+ cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
+ *(*vb)++ = s;
+ *(*vb)++ = t;
+ }
+
+ if (operand->texture.use_atlas) {
+ *(*vb)++ = operand->texture.p1.x;
+ *(*vb)++ = operand->texture.p1.y;
+ *(*vb)++ = operand->texture.p2.x;
+ *(*vb)++ = operand->texture.p2.y;
+ }
+ break;
+ }
+}
+
+static inline cairo_int_status_t
+_cairo_gl_context_get_image_cache (cairo_gl_context_t *ctx,
+ cairo_gl_image_cache_t **cache_out)
+{
+ if (! ctx->image_cache)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ *cache_out = ctx->image_cache;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+/* Called from _cairo_rtree_node_remove. */
+void
+_cairo_gl_image_node_destroy (cairo_rtree_node_t *node)
+{
+ cairo_surface_t *surface;
+
+ cairo_gl_image_t *image_node = cairo_container_of (node,
+ cairo_gl_image_t,
+ node);
+
+ surface = image_node->original_surface;
+ /* Remove from original surface. */
+ if (image_node->original_surface)
+ ((cairo_gl_surface_t *)surface)->image_node = NULL;
+}
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
new file mode 100755
index 000000000..c46a3e8cc
--- /dev/null
+++ b/src/cairo-gl-private.h
@@ -0,0 +1,1237 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ * T. Zachary Laine <whatwasthataddress@gmail.com>
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#ifndef CAIRO_GL_PRIVATE_H
+#define CAIRO_GL_PRIVATE_H
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "cairoint.h"
+
+#include "cairo-gl.h"
+#include "cairo-gl-gradient-private.h"
+
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-array-private.h"
+#include "cairo-stroke-dash-private.h"
+
+#include <assert.h>
+
+#if CAIRO_HAS_EVASGL_SURFACE
+#include <Evas_GL.h>
+#else
+ #if CAIRO_HAS_GL_SURFACE
+ #include <GL/gl.h>
+ #include <GL/glext.h>
+ #elif CAIRO_HAS_GLESV2_SURFACE
+ #include <GLES2/gl2.h>
+ #include <GLES2/gl2ext.h>
+ #elif CAIRO_HAS_GLESV3_SURFACE
+ #include <GLES3/gl3.h>
+ #include <GLES3/gl3ext.h>
+ #endif
+#endif
+
+
+#include "cairo-gl-ext-def-private.h"
+
+#define DEBUG_GL 0
+
+#define CAIRO_GL_ENUM_UNINITIALIZED 0xFFFF
+
+#if DEBUG_GL && __GNUC__
+#define UNSUPPORTED(reason) ({ \
+ fprintf (stderr, \
+ "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \
+ __FUNCTION__, __LINE__, reason); \
+ CAIRO_INT_STATUS_UNSUPPORTED; \
+})
+#else
+#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
+#endif
+
+#define CAIRO_GL_VERSION_ENCODE(major, minor) ( \
+ ((major) * 256) \
+ + ((minor) * 1))
+
+/* maximal number of shaders we keep in the cache.
+ * Random number that is hopefully big enough to not cause many cache evictions. */
+#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64
+
+/* VBO size that we allocate, smaller size means we gotta flush more often,
+ * but larger means hogging more memory and can cause trouble for drivers
+ * (especially on embedded devices). */
+#define CAIRO_GL_VBO_SIZE (16*1024)
+
+#define MIN_IMAGE_CACHE_WIDTH 512
+#define MIN_IMAGE_CACHE_HEIGHT 512
+#define MAX_IMAGE_CACHE_WIDTH 2048
+#define MAX_IMAGE_CACHE_HEIGHT 2048
+#define IMAGE_CACHE_MIN_SIZE 1
+#define IMAGE_CACHE_MAX_SIZE 256
+#define MIN_SCRATCH_SIZE 32
+#define MAX_SCRATCH_SIZE 1024
+
+typedef struct _cairo_gl_surface cairo_gl_surface_t;
+typedef struct _cairo_gl_image cairo_gl_image_t;
+
+/* Mali or SGX driver */
+typedef enum _cairo_gl_multisample_ext_type {
+ CAIRO_GL_EXT_MULTISAMPLE_TO_TEXTURE,
+ CAIRO_GL_IMG_MULTISAMPLE_TO_TEXTURE,
+ CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE
+} cairo_gl_multisample_ext_type;
+
+/* GL blur stages */
+typedef enum _cairo_blur_stage {
+ CAIRO_GL_BLUR_STAGE_NONE,
+ CAIRO_GL_BLUR_STAGE_0, /* shrink stage */
+ CAIRO_GL_BLUR_STAGE_1, /* x/y pass */
+ CAIRO_GL_BLUR_STAGE_2 /* enlarge stage */
+} cairo_blur_stage_t;
+
+/* GL flavor */
+typedef enum cairo_gl_flavor {
+ CAIRO_GL_FLAVOR_NONE = 0,
+ CAIRO_GL_FLAVOR_DESKTOP = 1,
+ CAIRO_GL_FLAVOR_ES2 = 2,
+ CAIRO_GL_FLAVOR_ES3 = 3
+} cairo_gl_flavor_t;
+
+/* The order here is sensitive because of the logic of
+ *_cairo_gl_shader_uniform_for_texunit. */
+typedef enum cairo_gl_uniform_t {
+ CAIRO_GL_UNIFORM_TEXDIMS, /* "source_texdims" */
+ CAIRO_GL_UNIFORM_TEXGEN, /* "source_texgen" */
+ CAIRO_GL_UNIFORM_CONSTANT, /* "source_constant" */
+ CAIRO_GL_UNIFORM_SAMPLER, /* "source_sampler" */
+ CAIRO_GL_UNIFORM_A, /* "source_a" */
+ CAIRO_GL_UNIFORM_CIRCLE_D, /* "source_circle_d" */
+ CAIRO_GL_UNIFORM_RADIUS_0, /* "source_radius_0" */
+ CAIRO_GL_UNIFORM_BLUR_RADIUS,/* "source_blur_radius" */
+ CAIRO_GL_UNIFORM_BLURS, /* "source_blurs" */
+ CAIRO_GL_UNIFORM_BLUR_STEP, /* "source_blurstep" */
+ CAIRO_GL_UNIFORM_BLUR_X_AXIS, /* "source_blur_x_axis" */
+ CAIRO_GL_UNIFORM_BLUR_Y_AXIS, /* "source_blur_y_axis" */
+ CAIRO_GL_UNIFORM_ALPHA, /* "source_alpha */
+
+ CAIRO_GL_UNIFORM_MASK_TEXDIMS, /* "mask_texdims" */
+ CAIRO_GL_UNIFORM_MASK_TEXGEN, /* "mask_texgen" */
+ CAIRO_GL_UNIFORM_MASK_CONSTANT, /* "mask_constant" */
+ CAIRO_GL_UNIFORM_MASK_SAMPLER, /* "mask_sampler" */
+ CAIRO_GL_UNIFORM_MASK_A, /* "mask_a" */
+ CAIRO_GL_UNIFORM_MASK_CIRCLE_D, /* "mask_circle_d" */
+ CAIRO_GL_UNIFORM_MASK_RADIUS_0, /* "mask_radius_0" */
+ CAIRO_GL_UNIFORM_MASK_BLUR_RADIUS, /* "mask_blur_radius */
+ CAIRO_GL_UNIFORM_MASK_BLURS, /* "mask_blurs */
+ CAIRO_GL_UNIFORM_MASK_BLUR_STEP, /* "mask_blur_step" */
+ CAIRO_GL_UNIFORM_MASK_BLUR_X_AXIS, /* "mask_blur_x_axis" */
+ CAIRO_GL_UNIFORM_MASK_BLUR_Y_AXIS, /* "mask_blur_y_axis" */
+ CAIRO_GL_UNIFORM_MASK_ALPHA, /* "mask_alpha" */
+
+ CAIRO_GL_UNIFORM_PROJECTION_MATRIX, /* "ModelViewProjectionMatrix" */
+
+
+ CAIRO_GL_UNIFORM_MAX
+} cairo_gl_uniform_t;
+
+/* Indices for vertex attributes used by BindAttribLocation etc */
+enum {
+ CAIRO_GL_VERTEX_ATTRIB_INDEX = 0,
+ CAIRO_GL_COLOR_ATTRIB_INDEX = 1,
+ CAIRO_GL_COVERAGE_ATTRIB_INDEX = 2,
+ CAIRO_GL_TEXCOORD0_ATTRIB_INDEX = 3,
+ CAIRO_GL_TEXCOORD1_ATTRIB_INDEX = 4,
+ CAIRO_GL_START_COORD0_ATTRIB_INDEX = 5,
+ CAIRO_GL_START_COORD1_ATTRIB_INDEX = 6,
+ CAIRO_GL_STOP_COORD0_ATTRIB_INDEX = 7,
+ CAIRO_GL_STOP_COORD1_ATTRIB_INDEX = 8
+};
+
+typedef enum cairo_gl_operand_type {
+ CAIRO_GL_OPERAND_NONE,
+ CAIRO_GL_OPERAND_CONSTANT,
+ CAIRO_GL_OPERAND_TEXTURE,
+ CAIRO_GL_OPERAND_LINEAR_GRADIENT,
+ CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0,
+ CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE,
+ CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT,
+ CAIRO_GL_OPERAND_GAUSSIAN,
+
+ CAIRO_GL_OPERAND_COUNT
+} cairo_gl_operand_type_t;
+
+typedef enum cairo_gl_draw_mode {
+ CAIRO_GL_VERTEX,
+ CAIRO_GL_LINE_STRIP,
+ CAIRO_GL_LINES
+} cairo_gl_draw_mode_t;
+
+/* This union structure describes a potential source or mask operand to the
+ * compositing equation.
+ */
+typedef struct cairo_gl_operand {
+ cairo_gl_operand_type_t type;
+ int pass; /* 0 none, 1, x-axis, 2, y-axis */
+ union {
+ struct {
+ GLuint tex;
+ cairo_gl_surface_t *surface;
+ cairo_gl_surface_t *owns_surface;
+ cairo_surface_attributes_t attributes;
+ int texgen;
+ cairo_bool_t use_atlas;
+ cairo_extend_t extend;
+ struct { float x, y; } p1, p2;
+ float coef[17]; /* max 1x/x1 kernel size */
+ int x_radius;
+ int y_radius;
+ } texture;
+ struct {
+ GLfloat color[4];
+ cairo_bool_t encode_as_attribute;
+ } constant;
+ struct {
+ cairo_gl_gradient_t *gradient;
+ cairo_matrix_t m;
+ cairo_circle_double_t circle_d;
+ double radius_0, a;
+ cairo_extend_t extend;
+ int texgen;
+ } gradient;
+ };
+ unsigned int vertex_offset;
+} cairo_gl_operand_t;
+
+typedef struct cairo_gl_source {
+ cairo_surface_t base;
+ cairo_gl_operand_t operand;
+} cairo_gl_source_t;
+
+struct _cairo_gl_surface {
+ cairo_surface_t base;
+ cairo_gl_operand_t operand;
+
+ int width, height;
+
+ GLuint tex; /* GL texture object containing our data. */
+ GLuint fb; /* GL framebuffer object wrapping our data. */
+ GLuint depth_stencil; /* GL renderbuffer object for holding stencil buffer clip. */
+
+ GLuint msaa_rb; /* The ARB MSAA path uses a renderbuffer. */
+ GLuint msaa_fb;
+ GLuint msaa_depth_stencil;
+
+ cairo_bool_t stencil_and_msaa_caps_initialized;
+ cairo_bool_t supports_stencil; /* Stencil support for for non-texture surfaces. */
+ cairo_bool_t supports_msaa;
+ cairo_bool_t num_samples;
+ cairo_bool_t force_no_msaa;
+ cairo_bool_t msaa_active; /* Whether the multisampling
+ framebuffer is active or not. */
+ cairo_bool_t content_synced; /* texture and renderbuffer content
+ synced */
+ cairo_bool_t content_cleared; /* last draw is glClear */
+ cairo_clip_t *clip_on_stencil_buffer;
+
+ int owns_tex;
+ cairo_bool_t needs_update;
+ cairo_bool_t size_changed;
+
+ cairo_region_t *clip_region;
+ GLuint bounded_tex; /* bounded tex for non-texture surface */
+
+ /* Indicate whether we need to cache it in image_cache. */
+ cairo_bool_t needs_to_cache;
+ cairo_bool_t force_no_cache;
+ double image_content_scale_x;
+ double image_content_scale_y;
+ cairo_blur_stage_t blur_stage;
+ /* Damage is too expensive to check, we use this flag. */
+ cairo_bool_t content_changed;
+ cairo_gl_image_t *image_node;
+};
+
+typedef struct cairo_gl_glyph_cache {
+ cairo_rtree_t rtree;
+ cairo_gl_surface_t *surface;
+} cairo_gl_glyph_cache_t;
+
+typedef enum cairo_gl_tex {
+ CAIRO_GL_TEX_SOURCE = 0,
+ CAIRO_GL_TEX_MASK = 1,
+ CAIRO_GL_TEX_TEMP = 2
+} cairo_gl_tex_t;
+
+typedef struct cairo_gl_shader {
+ GLuint fragment_shader;
+ GLuint program;
+ GLint uniforms[CAIRO_GL_UNIFORM_MAX];
+} cairo_gl_shader_t;
+
+typedef struct _cairo_gl_image_cache {
+ cairo_rtree_t rtree;
+ cairo_gl_surface_t *surface;
+ cairo_bool_t copy_success;
+} cairo_gl_image_cache_t;
+
+struct _cairo_gl_image {
+ cairo_rtree_node_t node;
+ cairo_surface_t *original_surface;
+ struct { float x, y; } p1, p2;
+ cairo_gl_context_t *ctx;
+};
+
+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 struct _cairo_gl_hairline_closure
+{
+ cairo_gl_context_t *ctx;
+ double tolerance;
+ cairo_stroker_dash_t dash;
+ cairo_matrix_t *ctm;
+ cairo_matrix_t *ctm_inverse;
+ cairo_point_t current_point;
+
+ cairo_point_t stroke_first_point; /* First stroke point at move_to. */
+ double stroke_first_dx;
+ double stroke_first_dy;
+ cairo_bool_t stroke_first_capped;
+ cairo_bool_t moved_to_stroke_first_point;
+
+ cairo_line_cap_t cap_style;
+
+ cairo_bool_t line_last_capped;
+
+ cairo_point_t line_last_point;
+ double line_last_dx;
+ double line_last_dy;
+
+ cairo_bool_t initialized;
+} cairo_gl_hairline_closure_t;
+
+typedef enum cairo_gl_var_type {
+ CAIRO_GL_VAR_NONE,
+ CAIRO_GL_VAR_TEXCOORDS,
+ CAIRO_GL_VAR_TEXGEN,
+ CAIRO_GL_VAR_COLOR,
+} cairo_gl_var_type_t;
+
+typedef enum cairo_gl_primitive_type {
+ CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES,
+ CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS
+} cairo_gl_primitive_type_t;
+
+typedef void (*cairo_gl_emit_rect_t) (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2);
+
+typedef void (*cairo_gl_emit_span_t) (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2,
+ uint8_t alpha);
+
+typedef void (*cairo_gl_emit_glyph_t) (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2,
+ GLfloat glyph_x1, GLfloat glyph_y1,
+ GLfloat glyph_x2, GLfloat glyph_y2);
+
+#define cairo_gl_var_type_hash(src,mask,src_atlas_extend,mask_atlas_extend,src_use_atlas,mask_use_atlas,spans,dest) ((spans) << 13) | ((mask) << 10 | (src << 7) | (mask_atlas_extend << 5) | (src_atlas_extend << 3) | (mask_use_atlas << 2) | (src_use_atlas << 1) | (dest))
+#define CAIRO_GL_VAR_TYPE_MAX (1 << 14)
+
+typedef void (*cairo_gl_generic_func_t)(void);
+typedef cairo_gl_generic_func_t (*cairo_gl_get_proc_addr_func_t)(void *data, const char *procname);
+
+typedef struct _cairo_gl_dispatch {
+ /* common */
+ void (*ActiveTexture) (GLenum texture);
+ void (*BindTexture) (GLenum target, GLuint texture);
+ void (*BlendFunc) (GLenum sfactor, GLenum dfactor);
+ void (*BlendFuncSeparate) (GLenum srcRGB, GLenum dstRGB,
+ GLenum srcAlpha, GLenum dstAlpha);
+ void (*Clear) (GLbitfield mask);
+ void (*ClearColor) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+ void (*ClearStencil) (GLint s);
+ void (*ColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+ void (*DeleteTextures) (GLsizei n, const GLuint *textures);
+ void (*DepthMask)(GLboolean flag);
+ void (*Disable) (GLenum cap);
+ void (*DrawArrays) (GLenum mode, GLint first, GLsizei count);
+ void (*DrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+ void (*Enable) (GLenum cap);
+ void (*Flush) (void);
+ void (*GenTextures) (GLsizei n, GLuint *textures);
+ void (*GetBooleanv) (GLenum pname, GLboolean *data);
+ GLenum (*GetError) (void);
+ void (*GetFloatv) (GLenum pname, GLfloat *data);
+ void (*GetIntegerv) (GLenum pname, GLint *data);
+ const GLubyte *(*GetString) (GLenum pname);
+ void (*PixelStorei) (GLenum pname, GLint param);
+ void (*ReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid *data);
+ void (*Scissor) (GLint x, GLint y, GLsizei width, GLsizei height);
+ void (*StencilFunc) (GLenum func, GLint ref, GLuint mask);
+ void (*StencilMask) (GLuint mask);
+ void (*StencilOp) (GLenum sfail, GLenum dpfail, GLenum dppass);
+ void (*TexSubImage2D) (GLenum target, GLint level,
+ GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type, const GLvoid *data);
+ void (*TexImage2D) (GLenum target, GLint level, GLenum internalformat,
+ GLsizei width, GLsizei height,
+ GLint border, GLenum format,
+ GLenum type, const GLvoid *data);
+ void (*TexParameteri) (GLenum target, GLenum pname, GLint param);
+#if defined(CAIRO_HAS_GL_SURFACE) || defined(CAIRO_HAS_EVASGL_SURFACE)
+ void (*DrawBuffer) (GLenum buf);
+ void (*ReadBuffer) (GLenum buf);
+#endif
+ void (*Viewport) (GLint x, GLint y, GLsizei width, GLsizei height);
+
+ /* Buffers */
+ void (*GenBuffers) (GLsizei n, GLuint *buffers);
+ void (*BindBuffer) (GLenum target, GLuint buffer);
+ void (*BufferData) (GLenum target, GLsizeiptr size,
+ const GLvoid* data, GLenum usage);
+ GLvoid *(*MapBuffer) (GLenum target, GLenum access);
+ GLboolean (*UnmapBuffer) (GLenum target);
+
+ /* Shaders */
+ GLuint (*CreateShader) (GLenum type);
+ void (*ShaderSource) (GLuint shader, GLsizei count,
+ const GLchar** string, const GLint* length);
+ void (*CompileShader) (GLuint shader);
+ void (*GetShaderiv) (GLuint shader, GLenum pname, GLint *params);
+ void (*GetShaderInfoLog) (GLuint shader, GLsizei bufSize,
+ GLsizei *length, GLchar *infoLog);
+ void (*DeleteShader) (GLuint shader);
+
+ /* Programs */
+ GLuint (*CreateProgram) (void);
+ void (*AttachShader) (GLuint program, GLuint shader);
+ void (*DeleteProgram) (GLuint program);
+ void (*LinkProgram) (GLuint program);
+ void (*UseProgram) (GLuint program);
+ void (*GetProgramiv) (GLuint program, GLenum pname, GLint *params);
+ void (*GetProgramInfoLog) (GLuint program, GLsizei bufSize,
+ GLsizei *length, GLchar *infoLog);
+
+ /* Uniforms */
+ GLint (*GetUniformLocation) (GLuint program, const GLchar* name);
+ void (*Uniform1f) (GLint location, GLfloat x);
+ void (*Uniform2f) (GLint location, GLfloat x, GLfloat y);
+ void (*Uniform3f) (GLint location, GLfloat x, GLfloat y, GLfloat z);
+ void (*Uniform4f) (GLint location, GLfloat x, GLfloat y, GLfloat z,
+ GLfloat w);
+ void (*Uniform1fv) (GLint location, GLsizei count, const GLfloat *v);
+
+ void (*UniformMatrix3fv) (GLint location, GLsizei count,
+ GLboolean transpose, const GLfloat *value);
+ void (*UniformMatrix4fv) (GLint location, GLsizei count,
+ GLboolean transpose, const GLfloat *value);
+ void (*Uniform1i) (GLint location, GLint x);
+
+ /* Attributes */
+ void (*BindAttribLocation) (GLuint program, GLuint index,
+ const GLchar *name);
+ void (*VertexAttribPointer) (GLuint index, GLint size, GLenum type,
+ GLboolean normalized, GLsizei stride,
+ const GLvoid *pointer);
+ void (*EnableVertexAttribArray) (GLuint index);
+ void (*DisableVertexAttribArray) (GLuint index);
+
+ /* Framebuffer objects */
+ void (*GenFramebuffers) (GLsizei n, GLuint* framebuffers);
+ void (*BindFramebuffer) (GLenum target, GLuint framebuffer);
+ void (*FramebufferTexture2D) (GLenum target, GLenum attachment,
+ GLenum textarget, GLuint texture,
+ GLint level);
+ GLenum (*CheckFramebufferStatus) (GLenum target);
+ void (*DeleteFramebuffers) (GLsizei n, const GLuint* framebuffers);
+ void (*GenRenderbuffers) (GLsizei n, GLuint *renderbuffers);
+ void (*BindRenderbuffer) (GLenum target, GLuint renderbuffer);
+ void (*RenderbufferStorage) (GLenum target, GLenum internal_format,
+ GLsizei width, GLsizei height);
+ void (*FramebufferRenderbuffer) (GLenum target, GLenum attachment,
+ GLenum renderbuffer_ttarget, GLuint renderbuffer);
+ void (*DeleteRenderbuffers) (GLsizei n, GLuint *renderbuffers);
+ void (*BlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+ GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter);
+ void (*RenderbufferStorageMultisample) (GLenum target, GLsizei samples,
+ GLenum internalformat,
+ GLsizei width, GLsizei height);
+ void (*FramebufferTexture2DMultisample) (GLenum target, GLenum attachment,
+ GLenum textarget, GLuint texture,
+ GLint level, GLsizei samples);
+} cairo_gl_dispatch_t;
+
+typedef struct _cairo_gl_states {
+ cairo_rectangle_int_t viewport_box;
+
+ GLclampf clear_red;
+ GLclampf clear_green;
+ GLclampf clear_blue;
+ GLclampf clear_alpha;
+
+ cairo_bool_t blend_enabled;
+
+ GLenum src_color_factor;
+ GLenum dst_color_factor;
+ GLenum src_alpha_factor;
+ GLenum dst_alpha_factor;
+
+ GLenum active_texture;
+
+ cairo_bool_t depth_mask;
+
+ cairo_bool_t scissor_test_enabled;
+ cairo_bool_t stencil_test_enabled;
+
+} cairo_gl_states_t;
+
+struct _cairo_gl_context {
+ cairo_device_t base;
+
+ const cairo_compositor_t *compositor;
+
+ GLuint texture_load_pbo;
+ GLint max_framebuffer_size;
+ GLint max_texture_size;
+ GLint max_textures;
+ GLenum tex_target;
+
+ GLint num_samples;
+ cairo_bool_t supports_msaa;
+ char *vb;
+
+ cairo_bool_t has_shader_support;
+
+ GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX];
+ cairo_gl_shader_t fill_rectangles_shader;
+ cairo_cache_t shaders;
+
+ cairo_cache_t gradients;
+
+ /* cache[0] for gray font, cache[1] for rgba component alpha
+ * cache[2] for color glyph */
+ cairo_gl_glyph_cache_t glyph_cache[3];
+ cairo_list_t fonts;
+
+ cairo_gl_surface_t *current_target;
+ cairo_operator_t current_operator;
+ cairo_gl_shader_t *pre_shader; /* for component alpha */
+ cairo_gl_shader_t *current_shader;
+
+ cairo_gl_operand_t operands[2];
+ cairo_bool_t spans;
+
+ unsigned int vb_offset;
+ unsigned int vertex_size;
+ cairo_region_t *clip_region;
+
+ cairo_gl_primitive_type_t primitive_type;
+ cairo_array_t tristrip_indices;
+
+ cairo_bool_t has_mesa_pack_invert;
+ cairo_gl_dispatch_t dispatch;
+ GLfloat modelviewprojection_matrix[16];
+ cairo_gl_flavor_t gl_flavor;
+ cairo_bool_t has_map_buffer;
+ cairo_bool_t has_packed_depth_stencil;
+ cairo_bool_t has_npot_repeat;
+ cairo_bool_t can_read_bgra;
+
+ cairo_bool_t thread_aware;
+
+ cairo_gl_image_cache_t *image_cache;
+ cairo_gl_draw_mode_t draw_mode;
+ cairo_gl_states_t states_cache;
+
+ /* Intermediate mask surface for glyph rendering. Created on first access, enlarged on demand. */
+ cairo_gl_surface_t *glyph_mask;
+ /* Intermediate blur surface for gaussian blur. Created on first access, enlarged on demand. */
+ cairo_gl_surface_t *source_scratch_surfaces[2];
+ cairo_gl_surface_t *mask_scratch_surfaces[2];
+ cairo_gl_surface_t *shadow_scratch_surfaces[3];
+ cairo_gl_surface_t *shadow_masks[4];
+ cairo_bool_t source_scratch_in_use;
+ cairo_bool_t has_angle_multisampling;
+ cairo_gl_multisample_ext_type msaa_type;
+
+ void (*acquire) (void *ctx);
+ void (*release) (void *ctx);
+
+ void (*make_current) (void *ctx, cairo_gl_surface_t *surface);
+ void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface);
+ void (*destroy) (void *ctx);
+};
+
+typedef struct _cairo_gl_composite {
+ cairo_gl_surface_t *dst;
+ cairo_operator_t op;
+ cairo_region_t *clip_region;
+
+ cairo_gl_operand_t src;
+ cairo_gl_operand_t mask;
+ cairo_bool_t spans;
+
+ cairo_clip_t *clip;
+ cairo_bool_t multisample;
+} cairo_gl_composite_t;
+
+typedef struct _cairo_gl_font {
+ cairo_scaled_font_private_t base;
+ cairo_device_t *device;
+ cairo_list_t link;
+} cairo_gl_font_t;
+
+static cairo_always_inline GLenum
+_cairo_gl_get_error (cairo_gl_context_t *ctx)
+{
+ GLenum err = ctx->dispatch.GetError();
+
+ if (unlikely (err))
+ while (ctx->dispatch.GetError ());
+
+ 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_context_reset (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_surface_init (cairo_device_t *device,
+ cairo_gl_surface_t *surface,
+ cairo_content_t content,
+ int width, int height);
+
+static cairo_always_inline cairo_bool_t cairo_warn
+_cairo_gl_surface_is_texture (cairo_gl_surface_t *surface)
+{
+ return surface->tex != 0;
+}
+
+cairo_private cairo_status_t
+_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
+ cairo_image_surface_t *src,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y,
+ cairo_bool_t force_flush);
+
+cairo_private cairo_int_status_t
+_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface);
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_device_has_glsl (cairo_device_t *device)
+{
+ return ((cairo_gl_context_t *) device)->has_shader_support;
+}
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device)
+{
+ return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE;
+}
+
+static cairo_always_inline cairo_status_t cairo_warn
+_cairo_gl_context_acquire (cairo_device_t *device,
+ cairo_gl_context_t **ctx)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (device);
+ if (unlikely (status))
+ return status;
+
+ /* clear potential previous GL errors */
+ _cairo_gl_get_error ((cairo_gl_context_t *) device);
+
+ *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 (ctx);
+
+ if (unlikely (err)) {
+ cairo_status_t new_status;
+ new_status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = new_status;
+ }
+
+ cairo_device_release (&(ctx)->base);
+
+ return status;
+}
+
+cairo_private void
+_cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface,
+ cairo_bool_t multisampling);
+
+cairo_private void
+_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface,
+ cairo_bool_t multisampling);
+
+cairo_private cairo_gl_emit_rect_t
+_cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_context_emit_rect (cairo_gl_context_t *ctx,
+ GLfloat x1, GLfloat y1,
+ GLfloat x2, GLfloat y2);
+
+cairo_private cairo_gl_emit_span_t
+_cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx);
+
+cairo_private cairo_gl_emit_glyph_t
+_cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx,
+ const cairo_bool_t is_color_glyph);
+
+cairo_private void
+_cairo_gl_context_activate (cairo_gl_context_t *ctx,
+ cairo_gl_tex_t tex_unit);
+
+cairo_private cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op);
+
+cairo_private cairo_bool_t
+_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface);
+
+static cairo_always_inline void
+_disable_stencil_buffer (cairo_gl_context_t *ctx)
+{
+ if (ctx->states_cache.stencil_test_enabled == TRUE) {
+ ctx->dispatch.Disable (GL_STENCIL_TEST);
+ ctx->states_cache.stencil_test_enabled = FALSE;
+ }
+}
+
+static cairo_always_inline void
+_disable_scissor_buffer (cairo_gl_context_t *ctx)
+{
+ if (ctx->states_cache.scissor_test_enabled == TRUE) {
+ ctx->dispatch.Disable (GL_SCISSOR_TEST);
+ ctx->states_cache.scissor_test_enabled = FALSE;
+ }
+}
+
+static cairo_always_inline void
+_enable_stencil_buffer (cairo_gl_context_t *ctx)
+{
+ if (ctx->states_cache.stencil_test_enabled == FALSE) {
+ ctx->dispatch.Enable (GL_STENCIL_TEST);
+ ctx->states_cache.stencil_test_enabled = TRUE;
+ }
+}
+
+static cairo_always_inline void
+_enable_scissor_buffer (cairo_gl_context_t *ctx)
+{
+ if (ctx->states_cache.scissor_test_enabled == FALSE) {
+ ctx->dispatch.Enable (GL_SCISSOR_TEST);
+ ctx->states_cache.scissor_test_enabled = TRUE;
+ }
+}
+
+cairo_private cairo_status_t
+_cairo_gl_composite_init (cairo_gl_composite_t *setup,
+ cairo_operator_t op,
+ cairo_gl_surface_t *dst,
+ cairo_bool_t has_component_alpha);
+
+cairo_private void
+_cairo_gl_composite_fini (cairo_gl_composite_t *setup);
+
+cairo_private cairo_status_t
+_cairo_gl_composite_set_operator (cairo_gl_composite_t *setup,
+ cairo_operator_t op,
+ cairo_bool_t assume_component_alpha);
+
+cairo_private void
+_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
+ cairo_region_t *clip_region);
+
+cairo_private void
+_cairo_gl_composite_set_clip(cairo_gl_composite_t *setup,
+ cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen,
+ cairo_bool_t encode_color_as_attribute);
+
+cairo_private void
+_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup,
+ const cairo_color_t *color);
+
+cairo_private void
+_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup,
+ const cairo_gl_operand_t *source);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen);
+
+cairo_private void
+_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup,
+ const cairo_gl_operand_t *mask);
+
+cairo_private void
+_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup);
+
+cairo_private void
+_cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup);
+
+cairo_private cairo_status_t
+_cairo_gl_composite_begin (cairo_gl_composite_t *setup,
+ cairo_gl_context_t **ctx);
+
+cairo_private cairo_status_t
+_cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup,
+ cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_composite_flush (cairo_gl_context_t *ctx);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const cairo_point_t quad[4]);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_emit_int_quad_as_tristrip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const int quad[8]);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ const cairo_point_t triangle[3]);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_emit_point_as_tristrip_line (cairo_gl_context_t *ctx,
+ const cairo_point_t point[2],
+ cairo_bool_t start_point);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_emit_point_as_single_line (cairo_gl_context_t *ctx,
+ const cairo_point_t point[2]);
+
+cairo_private void
+_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx,
+ cairo_gl_tex_t tex_unit);
+
+cairo_private cairo_bool_t
+_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor,
+ pixman_format_code_t pixman_format,
+ GLenum *internal_format, GLenum *format,
+ GLenum *type, cairo_bool_t *has_alpha,
+ cairo_bool_t *needs_swap);
+
+cairo_private void
+_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache);
+
+cairo_private void
+_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
+ cairo_gl_glyph_cache_t *cache);
+
+cairo_private cairo_int_status_t
+_cairo_gl_surface_show_glyphs (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip,
+ int *remaining_glyphs);
+
+cairo_private cairo_status_t
+_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx);
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_context_is_flushed (cairo_gl_context_t *ctx)
+{
+ return ctx->vb_offset == 0;
+}
+
+cairo_private cairo_status_t
+_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
+ cairo_gl_operand_t *source,
+ cairo_gl_operand_t *mask,
+ cairo_bool_t use_coverage,
+ cairo_gl_shader_in_t in,
+ cairo_gl_shader_t **shader);
+
+cairo_private cairo_gl_uniform_t
+_cairo_gl_shader_uniform_for_texunit (cairo_gl_uniform_t uniform,
+ cairo_gl_tex_t tex_unit);
+
+cairo_private void
+_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value);
+
+cairo_private void
+_cairo_gl_shader_bind_float_array (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ int num, float *values);
+
+cairo_private void
+_cairo_gl_shader_bind_int (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ int value);
+
+cairo_private void
+_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value0, float value1);
+
+cairo_private void
+_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value0,
+ float value1,
+ float value2);
+
+cairo_private void
+_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value0, float value1,
+ float value2, float value3);
+
+cairo_private void
+_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ const cairo_matrix_t* m);
+
+cairo_private void
+_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ GLfloat* gl_m);
+
+cairo_private void
+_cairo_gl_set_shader (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader);
+
+cairo_private void
+_cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader);
+
+cairo_private int
+_cairo_gl_get_version (cairo_gl_dispatch_t *dispatch);
+
+cairo_private cairo_gl_flavor_t
+_cairo_gl_get_flavor (cairo_gl_dispatch_t *dispatch);
+
+cairo_private cairo_bool_t
+_cairo_gl_has_extension (cairo_gl_dispatch_t *dispatch, const char *ext);
+
+cairo_private cairo_status_t
+_cairo_gl_dispatch_init(cairo_gl_dispatch_t *dispatch,
+ cairo_gl_get_proc_addr_func_t get_proc_addr,
+ void *data);
+
+cairo_private cairo_int_status_t
+_cairo_gl_operand_init (cairo_gl_operand_t *operand,
+ const cairo_pattern_t *pattern,
+ cairo_gl_surface_t *dst,
+ const cairo_rectangle_int_t *sample,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t use_texgen,
+ cairo_bool_t encode_color_as_attribute);
+
+cairo_private void
+_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand,
+ const cairo_color_t *color);
+
+cairo_private cairo_filter_t
+_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand);
+
+cairo_private GLint
+_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand);
+
+cairo_private cairo_extend_t
+_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand);
+
+cairo_private cairo_extend_t
+_cairo_gl_operand_get_atlas_extend (cairo_gl_operand_t *operand);
+
+cairo_private unsigned int
+_cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand);
+
+cairo_private cairo_bool_t
+_cairo_gl_operand_get_use_atlas (cairo_gl_operand_t *operand);
+
+cairo_private cairo_bool_t
+_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest,
+ cairo_gl_operand_t *source,
+ unsigned int vertex_offset);
+
+cairo_private void
+_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
+ cairo_gl_operand_t *operand,
+ cairo_gl_tex_t tex_unit);
+
+cairo_private void
+_cairo_gl_operand_emit (cairo_gl_operand_t *operand,
+ GLfloat ** vb,
+ GLfloat x,
+ GLfloat y);
+
+cairo_private void
+_cairo_gl_operand_copy (cairo_gl_operand_t *dst,
+ const cairo_gl_operand_t *src);
+
+cairo_private void
+_cairo_gl_operand_translate (cairo_gl_operand_t *operand,
+ double tx, double ty);
+
+cairo_private void
+_cairo_gl_operand_destroy (cairo_gl_operand_t *operand);
+
+cairo_private const cairo_compositor_t *
+_cairo_gl_msaa_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_gl_span_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_gl_traps_compositor_get (void);
+
+cairo_private cairo_int_status_t
+_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_glyphs (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_glyphs_with_clip (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info,
+ cairo_clip_t *clip);
+
+cairo_private void
+_cairo_gl_image_node_destroy (cairo_rtree_node_t *node);
+
+cairo_private void
+_cairo_gl_image_node_fini (void *data);
+
+cairo_private void
+_cairo_gl_image_cache_unlock (cairo_gl_context_t *ctx);
+
+cairo_private cairo_int_status_t
+_cairo_gl_image_cache_init (cairo_gl_context_t *ctx, int width, int height,
+ cairo_gl_image_cache_t **image_cache);
+cairo_private void
+_cairo_gl_image_cache_fini (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface);
+
+cairo_private cairo_surface_t *
+_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_private cairo_surface_t *
+_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_private cairo_surface_t *
+_cairo_gl_pattern_to_source (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y);
+
+cairo_private cairo_int_status_t
+_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx,
+ cairo_gl_composite_t *setup,
+ cairo_clip_t *clip);
+
+cairo_private cairo_surface_t *
+_cairo_gl_white_source (void);
+
+cairo_private void
+_cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface,
+ const cairo_rectangle_int_t *r);
+
+static inline cairo_gl_operand_t *
+source_to_operand (cairo_surface_t *surface)
+{
+ cairo_gl_source_t *source = (cairo_gl_source_t *)surface;
+ return source ? &source->operand : NULL;
+}
+
+static inline void
+_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache)
+{
+ _cairo_rtree_unpin (&cache->rtree);
+}
+
+
+
+cairo_private cairo_bool_t
+_cairo_gl_hairline_style_is_hairline (const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm);
+
+cairo_private cairo_status_t
+_cairo_gl_hairline_move_to (void *closure,
+ const cairo_point_t *point);
+
+cairo_private cairo_status_t
+_cairo_gl_hairline_line_to (void *closure,
+ const cairo_point_t *point);
+
+cairo_private cairo_status_t
+_cairo_gl_hairline_line_to_dashed (void *closure,
+ const cairo_point_t *point);
+
+cairo_private cairo_status_t
+_cairo_gl_hairline_curve_to (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2);
+
+cairo_private cairo_status_t
+_cairo_gl_hairline_close_path (void *closure);
+
+cairo_private cairo_status_t
+_cairo_gl_path_fixed_stroke_to_hairline (const cairo_path_fixed_t *path,
+ cairo_gl_hairline_closure_t *closure,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ 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);
+
+cairo_private cairo_gl_surface_t *
+_cairo_gl_gaussian_filter (cairo_gl_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_gl_surface_t *src,
+ cairo_rectangle_int_t *extents_out);
+
+static inline cairo_bool_t
+_cairo_gl_surface_is_scratch (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (surface == ctx->source_scratch_surfaces[i] ||
+ surface == ctx->mask_scratch_surfaces[i] ||
+ surface == ctx->shadow_scratch_surfaces[i])
+ return TRUE;
+ }
+
+ if (surface == ctx->shadow_scratch_surfaces[2])
+ return TRUE;
+
+ return FALSE;
+}
+
+slim_hidden_proto (cairo_gl_surface_create);
+slim_hidden_proto (cairo_gl_surface_create_for_texture);
+
+#endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
new file mode 100755
index 000000000..0f8073ba6
--- /dev/null
+++ b/src/cairo-gl-shaders.c
@@ -0,0 +1,1453 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 T. Zachary Laine
+ * Copyright © 2010 Eric Anholt
+ * Copyright © 2010 Red Hat, Inc
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is T. Zachary Laine.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Eric Anholt <eric@anholt.net>
+ * T. Zachary Laine <whatwasthataddress@gmail.com>
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+
+static GLint
+_cairo_gl_shader_get_uniform_location (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader,
+ cairo_gl_uniform_t uniform)
+{
+ /* This should be kept in sync with the enum
+ * definition in cairo-gl-private.h. */
+ const char *names[CAIRO_GL_UNIFORM_MAX] = {
+ "source_texdims",
+ "source_texgen",
+ "source_constant",
+ "source_sampler",
+ "source_a",
+ "source_circle_d",
+ "source_radius_0",
+ "source_blur_radius",
+ "source_blurs",
+ "source_blur_step",
+ "source_blur_x_axis",
+ "source_blur_y_axis",
+ "source_alpha",
+ "mask_texdims",
+ "mask_texgen",
+ "mask_constant",
+ "mask_sampler",
+ "mask_a",
+ "mask_circle_d",
+ "mask_radius_0",
+ "mask_blur_radius",
+ "mask_blurs",
+ "mask_blur_step",
+ "mask_blur_x_axis",
+ "mask_blur_y_axis",
+ "mask_alpha",
+ "ModelViewProjectionMatrix"
+ };
+
+ if (shader->uniforms[uniform] != -1)
+ return shader->uniforms[uniform];
+
+ shader->uniforms[uniform] =
+ ctx->dispatch.GetUniformLocation (shader->program,
+ names[uniform]);
+ return shader->uniforms[uniform];
+}
+
+cairo_gl_uniform_t
+_cairo_gl_shader_uniform_for_texunit (cairo_gl_uniform_t uniform,
+ cairo_gl_tex_t tex_unit)
+{
+ assert (uniform < CAIRO_GL_UNIFORM_MASK_TEXDIMS);
+ assert (tex_unit == CAIRO_GL_TEX_SOURCE || tex_unit == CAIRO_GL_TEX_MASK);
+ if (tex_unit == CAIRO_GL_TEX_SOURCE)
+ return uniform;
+ else
+ return uniform + CAIRO_GL_UNIFORM_MASK_TEXDIMS;
+}
+
+static cairo_status_t
+_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader,
+ cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ cairo_bool_t use_coverage,
+ const char *fragment_text,
+ cairo_extend_t src_atlas_extend,
+ cairo_extend_t mask_atlas_extend,
+ cairo_bool_t src_use_atlas,
+ cairo_bool_t mask_use_atlas);
+
+typedef struct _cairo_shader_cache_entry {
+ cairo_cache_entry_t base;
+
+ unsigned vertex;
+
+ cairo_gl_operand_type_t src;
+ cairo_gl_operand_type_t mask;
+ cairo_gl_operand_type_t dest;
+ cairo_bool_t use_coverage;
+
+ cairo_gl_shader_in_t in;
+ GLint src_gl_filter;
+ cairo_bool_t src_border_fade;
+ cairo_extend_t src_extend;
+ GLint mask_gl_filter;
+ cairo_bool_t mask_border_fade;
+ cairo_extend_t mask_extend;
+
+ cairo_bool_t src_use_atlas;
+ cairo_bool_t mask_use_atlas;
+
+ cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */
+ cairo_gl_shader_t shader;
+} cairo_shader_cache_entry_t;
+
+static cairo_bool_t
+_cairo_gl_shader_cache_equal_desktop (const void *key_a, const void *key_b)
+{
+ const cairo_shader_cache_entry_t *a = key_a;
+ const cairo_shader_cache_entry_t *b = key_b;
+ cairo_bool_t both_have_npot_repeat =
+ a->ctx->has_npot_repeat && b->ctx->has_npot_repeat;
+
+ return (a->vertex == b->vertex &&
+ a->src == b->src &&
+ a->mask == b->mask &&
+ a->dest == b->dest &&
+ a->use_coverage == b->use_coverage &&
+ a->in == b->in &&
+ (both_have_npot_repeat || a->src_extend == b->src_extend) &&
+ (both_have_npot_repeat || a->mask_extend == b->mask_extend));
+}
+
+/*
+ * For GLES2 we use more complicated shaders to implement missing GL
+ * features. In this case we need more parameters to uniquely identify
+ * a shader (vs _cairo_gl_shader_cache_equal_desktop()).
+ */
+static cairo_bool_t
+_cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b)
+{
+ const cairo_shader_cache_entry_t *a = key_a;
+ const cairo_shader_cache_entry_t *b = key_b;
+ cairo_bool_t both_have_npot_repeat =
+ a->ctx->has_npot_repeat && b->ctx->has_npot_repeat;
+
+ return (a->vertex == b->vertex &&
+ a->src == b->src &&
+ a->mask == b->mask &&
+ a->dest == b->dest &&
+ a->use_coverage == b->use_coverage &&
+ a->in == b->in &&
+ a->src_gl_filter == b->src_gl_filter &&
+ a->src_border_fade == b->src_border_fade &&
+ (both_have_npot_repeat || a->src_extend == b->src_extend) &&
+ a->mask_gl_filter == b->mask_gl_filter &&
+ a->mask_border_fade == b->mask_border_fade &&
+ (both_have_npot_repeat || a->mask_extend == b->mask_extend));
+}
+
+static unsigned long
+_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry)
+{
+ return ((entry->src << 15) |
+ (entry->mask << 12) |
+ (entry->dest << 9) |
+ (entry->in << 7) |
+ (entry->mask_extend << 5) |
+ (entry->src_extend << 3) |
+ (entry->mask_use_atlas << 2) |
+ (entry->src_use_atlas << 1) |
+ entry->use_coverage) ^ entry->vertex;
+}
+
+static void
+_cairo_gl_shader_cache_destroy (void *data)
+{
+ cairo_shader_cache_entry_t *entry = data;
+
+ _cairo_gl_shader_fini (entry->ctx, &entry->shader);
+ if (entry->ctx->current_shader == &entry->shader)
+ entry->ctx->current_shader = NULL;
+ free (entry);
+}
+
+static void
+_cairo_gl_shader_init (cairo_gl_shader_t *shader)
+{
+ int i;
+ shader->fragment_shader = 0;
+ shader->program = 0;
+
+ for (i = 0; i < CAIRO_GL_UNIFORM_MAX; i++)
+ shader->uniforms[i] = -1;
+}
+
+cairo_status_t
+_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx)
+{
+ static const char *fill_fs_source =
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "uniform vec4 color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+ cairo_status_t status;
+
+ if (_cairo_gl_get_version (&ctx->dispatch) >= CAIRO_GL_VERSION_ENCODE (2, 0) ||
+ (_cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_shader_objects") &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_fragment_shader") &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_vertex_shader"))) {
+ ctx->has_shader_support = TRUE;
+ } else {
+ ctx->has_shader_support = FALSE;
+ fprintf (stderr, "Error: The cairo gl backend requires shader support!\n");
+ return CAIRO_STATUS_DEVICE_ERROR;
+ }
+
+ memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders));
+
+ status = _cairo_cache_init (&ctx->shaders,
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ?
+ _cairo_gl_shader_cache_equal_desktop :
+ _cairo_gl_shader_cache_equal_gles2,
+ NULL,
+ _cairo_gl_shader_cache_destroy,
+ CAIRO_GL_MAX_SHADERS_PER_CONTEXT);
+ if (unlikely (status))
+ return status;
+
+ _cairo_gl_shader_init (&ctx->fill_rectangles_shader);
+ status = _cairo_gl_shader_compile_and_link (ctx,
+ &ctx->fill_rectangles_shader,
+ CAIRO_GL_VAR_NONE,
+ CAIRO_GL_VAR_NONE,
+ FALSE,
+ fill_fs_source,
+ CAIRO_EXTEND_NONE, CAIRO_EXTEND_NONE,
+ FALSE, FALSE);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx)
+{
+ int i;
+
+ for (i = 0; i < CAIRO_GL_VAR_TYPE_MAX; i++) {
+ if (ctx->vertex_shaders[i])
+ ctx->dispatch.DeleteShader (ctx->vertex_shaders[i]);
+ }
+
+ _cairo_cache_fini (&ctx->shaders);
+}
+
+void
+_cairo_gl_shader_fini (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader)
+{
+ if (shader->fragment_shader)
+ ctx->dispatch.DeleteShader (shader->fragment_shader);
+
+ if (shader->program)
+ ctx->dispatch.DeleteProgram (shader->program);
+}
+
+static const char *operand_names[] = { "source", "mask", "dest" };
+
+static cairo_gl_var_type_t
+cairo_gl_operand_get_var_type (cairo_gl_operand_t *operand)
+{
+ switch (operand->type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ if (operand->constant.encode_as_attribute)
+ return CAIRO_GL_VAR_COLOR;
+ else
+ return CAIRO_GL_VAR_NONE;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ return operand->gradient.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ return operand->texture.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS;
+ }
+}
+
+static void
+cairo_gl_shader_emit_variable (cairo_output_stream_t *stream,
+ cairo_gl_var_type_t type,
+ cairo_gl_tex_t name,
+ cairo_bool_t use_atlas)
+{
+ switch (type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_VAR_NONE:
+ break;
+ case CAIRO_GL_VAR_TEXCOORDS:
+ _cairo_output_stream_printf (stream,
+ "attribute vec4 MultiTexCoord%d;\n"
+ "varying vec2 %s_texcoords;\n",
+ name,
+ operand_names[name]);
+ if (use_atlas)
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_start_coords;\n"
+ "varying vec2 %s_stop_coords;\n",
+ operand_names[name], operand_names[name]);
+ break;
+ case CAIRO_GL_VAR_TEXGEN:
+ _cairo_output_stream_printf (stream,
+ "uniform mat3 %s_texgen;\n"
+ "varying vec2 %s_texcoords;\n",
+ operand_names[name],
+ operand_names[name]);
+ /*if (use_atlas)
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_start_coords;\n"
+ "varying vec2 %s_stop_coords;\n",
+ operand_names[name], operand_names[name]);
+*/
+ break;
+ case CAIRO_GL_VAR_COLOR:
+ _cairo_output_stream_printf (stream,
+ "varying vec4 fragment_color;\n");
+ break;
+ }
+}
+
+static void
+cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream,
+ cairo_gl_var_type_t type,
+ cairo_gl_tex_t name)
+{
+ switch (type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_VAR_NONE:
+ break;
+ case CAIRO_GL_VAR_TEXCOORDS:
+ _cairo_output_stream_printf (stream,
+ " %s_texcoords = MultiTexCoord%d.xy;\n",
+ operand_names[name], name);
+ break;
+
+ case CAIRO_GL_VAR_TEXGEN:
+ _cairo_output_stream_printf (stream,
+ " %s_texcoords = (%s_texgen * Vertex.xyw).xy;\n",
+ operand_names[name], operand_names[name]);
+ break;
+ case CAIRO_GL_VAR_COLOR:
+ _cairo_output_stream_printf (stream, " fragment_color = Color;\n");
+ break;
+ }
+}
+
+static void
+cairo_gl_shader_dcl_coverage (cairo_output_stream_t *stream)
+{
+ _cairo_output_stream_printf (stream, "varying float coverage;\n");
+}
+
+static void
+cairo_gl_shader_def_coverage (cairo_output_stream_t *stream)
+{
+ _cairo_output_stream_printf (stream, " coverage = Color.a;\n");
+}
+
+static void
+cairo_gl_shader_def_use_atlas (cairo_output_stream_t *stream,
+ cairo_gl_var_type_t type,
+ cairo_gl_tex_t name)
+{
+ _cairo_output_stream_printf (stream,
+ " %s_start_coords = StartCoords%d.xy;\n"
+ " %s_stop_coords = StopCoords%d.xy;\n",
+ operand_names[name], name,
+ operand_names[name], name);
+}
+
+static void
+cairo_gl_shader_emit_varying (cairo_output_stream_t *stream,
+ cairo_gl_tex_t name)
+{
+ const char *namestr = operand_names[name];
+
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_start_coords;\n"
+ "varying vec2 %s_stop_coords;\n",
+ namestr, namestr);
+}
+
+static cairo_status_t
+cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ cairo_bool_t src_use_atlas,
+ cairo_bool_t mask_use_atlas,
+ cairo_bool_t use_coverage,
+ cairo_gl_var_type_t dest,
+ char **out)
+{
+ cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+ unsigned char *source;
+ unsigned long length;
+ cairo_status_t status;
+
+ cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE, src_use_atlas);
+ cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK, mask_use_atlas);
+ if (use_coverage)
+ cairo_gl_shader_dcl_coverage (stream);
+
+ if (src_use_atlas && src == CAIRO_GL_VAR_TEXGEN)
+ cairo_gl_shader_emit_varying (stream, CAIRO_GL_TEX_SOURCE);
+
+ if (mask_use_atlas && mask == CAIRO_GL_VAR_TEXGEN)
+ cairo_gl_shader_emit_varying (stream, CAIRO_GL_TEX_MASK);
+
+ _cairo_output_stream_printf (stream,
+ "attribute vec4 Vertex;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 StartCoords0;\n"
+ "attribute vec2 StartCoords1;\n"
+ "attribute vec2 StopCoords0;\n"
+ "attribute vec2 StopCoords1;\n"
+ "uniform mat4 ModelViewProjectionMatrix;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = ModelViewProjectionMatrix * Vertex;\n");
+
+ cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK);
+ if (use_coverage)
+ cairo_gl_shader_def_coverage (stream);
+
+ if (src_use_atlas)
+ cairo_gl_shader_def_use_atlas (stream, src, CAIRO_GL_TEX_SOURCE);
+ if (mask_use_atlas)
+ cairo_gl_shader_def_use_atlas (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)) {
+ free (source);
+ return status;
+ }
+
+ *out = (char *) source;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Returns whether an operand needs a special border fade fragment shader
+ * to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2.
+ */
+static cairo_bool_t
+_cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand)
+{
+ cairo_extend_t extend =_cairo_gl_operand_get_extend (operand);
+
+ return extend == CAIRO_EXTEND_NONE &&
+ (operand->type == CAIRO_GL_OPERAND_TEXTURE ||
+ operand->type == CAIRO_GL_OPERAND_GAUSSIAN ||
+ operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT ||
+ operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE ||
+ operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0);
+}
+
+static void
+cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
+ cairo_gl_context_t *ctx,
+ cairo_gl_operand_t *op,
+ cairo_gl_tex_t name)
+{
+ const char *namestr = operand_names[name];
+ const char *rectstr = (ctx->tex_target == GL_TEXTURE_RECTANGLE ? "Rect" : "");
+ cairo_bool_t use_atlas = _cairo_gl_operand_get_use_atlas (op);
+
+ switch (op->type) {
+ case CAIRO_GL_OPERAND_COUNT:
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ case CAIRO_GL_OPERAND_NONE:
+ _cairo_output_stream_printf (stream,
+ "vec4 get_%s()\n"
+ "{\n"
+ " return vec4 (0, 0, 0, 1);\n"
+ "}\n",
+ namestr);
+ break;
+ case CAIRO_GL_OPERAND_CONSTANT:
+ if (op->constant.encode_as_attribute) {
+ _cairo_output_stream_printf (stream,
+ "varying vec4 fragment_color;\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " return fragment_color;\n"
+ "}\n",
+ namestr);
+ } else {
+ _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:
+ case CAIRO_GL_OPERAND_GAUSSIAN:
+ if (! use_atlas) {
+ _cairo_output_stream_printf (stream,
+ "uniform sampler2D%s %s_sampler;\n"
+ "uniform vec2 %s_texdims;\n"
+ "varying vec2 %s_texcoords;\n",
+ rectstr, namestr, namestr, namestr);
+ } else {
+ _cairo_output_stream_printf (stream,
+ "uniform sampler2D%s %s_sampler;\n"
+ "uniform vec2 %s_texdims;\n"
+ "varying vec2 %s_texcoords;\n"
+ "varying vec2 %s_start_coords;\n"
+ "varying vec2 %s_stop_coords;\n",
+ rectstr, namestr, namestr, namestr, namestr, namestr);
+ }
+
+ if (op->type != CAIRO_GL_OPERAND_TEXTURE) {
+ _cairo_output_stream_printf (stream,
+ "uniform int %s_blur_radius;\n"
+ "uniform float %s_blur_step;\n"
+ "uniform float %s_blurs[17];\n"
+ "uniform float %s_blur_x_axis;\n"
+ "uniform float %s_blur_y_axis;\n"
+ "uniform float %s_alpha;\n",
+ namestr, namestr, namestr, namestr, namestr, namestr);
+ }
+
+ _cairo_output_stream_printf (stream,
+ "vec4 get_%s()\n"
+ "{\n",
+ namestr);
+
+ if (op->type == CAIRO_GL_OPERAND_TEXTURE) {
+ if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) &&
+ _cairo_gl_shader_needs_border_fade (op))
+ {
+ if (! use_atlas) {
+ _cairo_output_stream_printf (stream,
+ " vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n"
+ " vec4 texel = texture2D%s (%s_sampler, %s_texcoords);\n"
+ " return texel * border_fade.x * border_fade.y;\n"
+ "}\n",
+ namestr, namestr, namestr, rectstr, namestr, namestr);
+ }
+ else {
+ _cairo_output_stream_printf (stream,
+ " vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n"
+ " vec2 co = %s_wrap (%s_texcoords, %s_start_coords, %s_stop_coords);\n"
+ " if (co.x == -1.0 && co.y == -1.0)\n"
+ " return vec4(0.0, 0.0, 0.0, 0.0);\n"
+ " vec4 texel = texture2D%s (%s_sampler, co);\n"
+ " return texel * border_fade.x * border_fade.y;\n"
+ "}\n",
+ namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, rectstr, namestr);
+ }
+ }
+ else
+ {
+ if (! use_atlas) {
+ _cairo_output_stream_printf (stream,
+ " return texture2D%s (%s_sampler, %s_wrap (%s_texcoords));\n"
+ "}\n",
+ rectstr, namestr, namestr, namestr);
+ } else {
+ _cairo_output_stream_printf (stream,
+ " return texture2D%s (%s_sampler, %s_wrap (%s_texcoords, %s_start_coords, %s_stop_coords));\n"
+ "}\n",
+ rectstr, namestr, namestr, namestr, namestr, namestr);
+ }
+ }
+ }
+ else if (op->type == CAIRO_GL_OPERAND_GAUSSIAN) {
+ _cairo_output_stream_printf (stream,
+ " int i;\n"
+ " vec2 texcoords;\n"
+ " float alpha = %s_alpha;\n"
+ " vec4 texel = vec4 (0.0, 0.0, 0.0, 0.0);\n"
+ " vec2 wrapped_coords = %s_wrap (%s_texcoords, %s_start_coords, %s_stop_coords);\n"
+ " if (wrapped_coords == vec2 (-1.0, -1.0))\n"
+ " return texel;\n"
+ " texel += texture2D%s (%s_sampler, wrapped_coords);\n"
+ " texel = texel * %s_blurs[%s_blur_radius];\n"
+ " for (i = -%s_blur_radius; i <= %s_blur_radius; i++) {\n"
+ " if (i == 0)\n"
+ " continue;\n"
+ " texcoords = %s_texcoords + vec2 (%s_blur_step * float(i) * %s_blur_x_axis, %s_blur_step * float(i) * %s_blur_y_axis);\n"
+
+ " wrapped_coords = %s_wrap (texcoords, %s_start_coords, %s_stop_coords);\n"
+ " if (wrapped_coords == vec2 (-1.0, -1.0))\n"
+ " texel += vec4 (0.0, 0.0, 0.0, alpha) * %s_blurs[i+%s_blur_radius];\n"
+ " else\n"
+ " texel += texture2D%s (%s_sampler, wrapped_coords) * %s_blurs[i+%s_blur_radius];\n"
+ " }\n"
+ " return texel;\n"
+ "}\n",
+ namestr, namestr, namestr, namestr, namestr,
+ rectstr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr,
+ rectstr, namestr, namestr, namestr);
+ }
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_texcoords;\n"
+ "uniform vec2 %s_texdims;\n"
+ "uniform sampler2D%s %s_sampler;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n",
+ namestr, namestr, rectstr, namestr, namestr);
+ if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) &&
+ _cairo_gl_shader_needs_border_fade (op))
+ {
+ _cairo_output_stream_printf (stream,
+ " float border_fade = %s_border_fade (%s_texcoords.x, %s_texdims.x);\n"
+ " vec4 texel = texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n"
+ " return texel * border_fade;\n"
+ "}\n",
+ namestr, namestr, namestr, rectstr, namestr, namestr);
+ }
+ else
+ {
+ _cairo_output_stream_printf (stream,
+ " return texture2D%s (%s_sampler, %s_wrap (vec2 (%s_texcoords.x, 0.5)));\n"
+ "}\n",
+ rectstr, namestr, namestr, namestr);
+ }
+ break;
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_texcoords;\n"
+ "uniform vec2 %s_texdims;\n"
+ "uniform sampler2D%s %s_sampler;\n"
+ "uniform vec3 %s_circle_d;\n"
+ "uniform float %s_radius_0;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+ " \n"
+ " float B = dot (pos, %s_circle_d);\n"
+ " float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+ " \n"
+ " float t = 0.5 * C / B;\n"
+ " float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n",
+ namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr, namestr);
+ if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) &&
+ _cairo_gl_shader_needs_border_fade (op))
+ {
+ _cairo_output_stream_printf (stream,
+ " float border_fade = %s_border_fade (t, %s_texdims.x);\n"
+ " vec4 texel = texture2D%s (%s_sampler, vec2 (t, 0.5));\n"
+ " return mix (vec4 (0.0), texel * border_fade, is_valid);\n"
+ "}\n",
+ namestr, namestr, rectstr, namestr);
+ }
+ else
+ {
+ _cairo_output_stream_printf (stream,
+ " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n"
+ " return mix (vec4 (0.0), texel, is_valid);\n"
+ "}\n",
+ rectstr, namestr, namestr);
+ }
+ break;
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_texcoords;\n"
+ "uniform vec2 %s_texdims;\n"
+ "uniform sampler2D%s %s_sampler;\n"
+ "uniform vec3 %s_circle_d;\n"
+ "uniform float %s_a;\n"
+ "uniform float %s_radius_0;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+ " \n"
+ " float B = dot (pos, %s_circle_d);\n"
+ " float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+ " \n"
+ " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+ " float sqrtdet = sqrt (abs (det));\n"
+ " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+ " \n"
+ " vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n"
+ " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+ " \n"
+ " float upper_t = mix (t.y, t.x, is_valid.x);\n",
+ namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr, namestr, namestr);
+ if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) &&
+ _cairo_gl_shader_needs_border_fade (op))
+ {
+ _cairo_output_stream_printf (stream,
+ " float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n"
+ " vec4 texel = texture2D%s (%s_sampler, vec2 (upper_t, 0.5));\n"
+ " return mix (vec4 (0.0), texel * border_fade, has_color);\n"
+ "}\n",
+ namestr, namestr, rectstr, namestr);
+ }
+ else
+ {
+ _cairo_output_stream_printf (stream,
+ " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
+ " return mix (vec4 (0.0), texel, has_color);\n"
+ "}\n",
+ rectstr, namestr, namestr);
+ }
+ break;
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_texcoords;\n"
+ "uniform sampler2D%s %s_sampler;\n"
+ "uniform vec3 %s_circle_d;\n"
+ "uniform float %s_a;\n"
+ "uniform float %s_radius_0;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+ " \n"
+ " float B = dot (pos, %s_circle_d);\n"
+ " float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+ " \n"
+ " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+ " float sqrtdet = sqrt (abs (det));\n"
+ " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+ " \n"
+ " vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n"
+ " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+ " \n"
+ " float upper_t = mix (t.y, t.x, is_valid.x);\n"
+ " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
+ " return mix (vec4 (0.0), texel, has_color);\n"
+ "}\n",
+ namestr, rectstr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, rectstr, namestr, namestr);
+ break;
+ }
+}
+
+/*
+ * Emits the border fade functions used by an operand.
+ *
+ * If bilinear filtering is used, the emitted function performs a linear
+ * fade to transparency effect in the intervals [-1/2n, 1/2n] and
+ * [1 - 1/2n, 1 + 1/2n] (n: texture size).
+ *
+ * If nearest filtering is used, the emitted function just returns
+ * 0.0 for all values outside [0, 1).
+ */
+static void
+_cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream,
+ cairo_gl_operand_t *operand,
+ cairo_gl_tex_t name)
+{
+ const char *namestr = operand_names[name];
+ GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand);
+
+ /* 2D version */
+ _cairo_output_stream_printf (stream,
+ "vec2 %s_border_fade (vec2 coords, vec2 dims)\n"
+ "{\n",
+ namestr);
+
+ if (gl_filter == GL_LINEAR)
+ _cairo_output_stream_printf (stream,
+ " return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n");
+ else
+ _cairo_output_stream_printf (stream,
+ " bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n"
+ " bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n"
+ " return vec2 (float (all (in_tex1) && all (in_tex2)));\n");
+
+ _cairo_output_stream_printf (stream, "}\n");
+
+ /* 1D version */
+ _cairo_output_stream_printf (stream,
+ "float %s_border_fade (float x, float dim)\n"
+ "{\n",
+ namestr);
+ if (gl_filter == GL_LINEAR)
+ _cairo_output_stream_printf (stream,
+ " return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n");
+ else
+ _cairo_output_stream_printf (stream,
+ " bool in_tex = x >= 0.0 && x < 1.0;\n"
+ " return float (in_tex);\n");
+
+ _cairo_output_stream_printf (stream, "}\n");
+}
+
+/*
+ * Emits the wrap function used by an operand.
+ *
+ * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are
+ * only available for NPOT textures if the GL_OES_texture_npot is supported.
+ * If GL_OES_texture_npot is not supported, we need to implement the wrapping
+ * functionality in the shader.
+ */
+static void
+_cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx,
+ cairo_output_stream_t *stream,
+ cairo_gl_operand_t *operand,
+ cairo_gl_tex_t name)
+{
+ const char *namestr = operand_names[name];
+ cairo_extend_t extend = _cairo_gl_operand_get_extend (operand);
+ cairo_bool_t use_atlas = _cairo_gl_operand_get_use_atlas (operand);
+
+ if (use_atlas)
+ _cairo_output_stream_printf (stream,
+ "vec2 %s_wrap (vec2 coords, vec2 start_coords, vec2 stop_coords)\n"
+ "{\n",
+ namestr);
+ else
+ _cairo_output_stream_printf (stream,
+ "vec2 %s_wrap(vec2 coords)\n"
+ "{\n",
+ namestr);
+
+ if (use_atlas) {
+ if (extend == CAIRO_EXTEND_REPEAT) {
+ _cairo_output_stream_printf (stream,
+ " vec2 range = stop_coords - start_coords;\n"
+ " return mod (coords - start_coords, range) + start_coords;\n");
+ } else if (extend == CAIRO_EXTEND_REFLECT){
+ _cairo_output_stream_printf (stream,
+ " vec2 range = stop_coords - start_coords;\n"
+ " vec2 frac = mod (coords - start_coords, range);\n"
+ " return mix(frac + start_coords, range - frac + start_coords, mod(floor((coords - start_coords) / range), 2.0));\n");
+ }
+ else if (extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (stream,
+ " bvec2 compare_to_start = lessThan (coords, start_coords);\n"
+ " bvec2 compare_to_stop = greaterThan (coords, stop_coords);\n"
+ " if (all (compare_to_start))\n"
+ " return start_coords;\n"
+ " else if (all (compare_to_stop))\n"
+ " return stop_coords;\n"
+ " else if (compare_to_start.x && compare_to_stop.y)\n"
+ " return vec2 (start_coords.x, stop_coords.y);\n"
+ " else if (compare_to_start.x && ! compare_to_stop.y)\n"
+ " return vec2 (start_coords.x, coords.y);\n"
+ " else if (compare_to_stop.x && compare_to_start.y)\n"
+ " return vec2 (stop_coords.x, start_coords.y);\n"
+ " else if (compare_to_stop.x && ! compare_to_stop.y)\n"
+ " return vec2 (stop_coords.x, coords.y);\n"
+ " else if (compare_to_start.y && ! compare_to_start.x)\n"
+ " return vec2 (coords.x, start_coords.y);\n"
+ " else if (compare_to_stop.y && ! compare_to_start.x)\n"
+ " return vec2 (coords.x, stop_coords.y);\n"
+ " else\n"
+ " return coords;\n");
+ }
+ else {
+ _cairo_output_stream_printf (stream,
+ " if (any (lessThan (coords, start_coords)))\n"
+ //" return vec2 (coords - start_coords);\n"
+ " return vec2 (-1.0);\n"
+ " if (any (greaterThan (coords, stop_coords)))\n"
+ //" return vec2 (coords - stop_coords + vec2 (1.0, 1.0));\n"
+ " return vec2 (-1.0);\n"
+ " else\n"
+ " return coords;\n");
+ }
+ }
+ else {
+ if (! ctx->has_npot_repeat &&
+ (extend == CAIRO_EXTEND_REPEAT ||
+ extend == CAIRO_EXTEND_REFLECT)) {
+ if (extend == CAIRO_EXTEND_REPEAT) {
+ _cairo_output_stream_printf (stream,
+ " return fract(coords);\n");
+ } else { /* CAIRO_EXTEND_REFLECT */
+ _cairo_output_stream_printf (stream,
+ " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n");
+ }
+ }
+ else
+ {
+ _cairo_output_stream_printf (stream, " return coords;\n");
+ }
+ }
+
+ _cairo_output_stream_printf (stream, "}\n");
+}
+
+static cairo_status_t
+cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx,
+ cairo_gl_shader_in_t in,
+ cairo_gl_operand_t *src,
+ cairo_gl_operand_t *mask,
+ cairo_bool_t use_coverage,
+ cairo_gl_operand_type_t dest_type,
+ char **out)
+{
+ cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+ unsigned char *source;
+ unsigned long length;
+ cairo_status_t status;
+ const char *coverage_str;
+
+ // dy5.kim: Use highp only for gradients to handle the following test case
+ // http://w3c-test.org/html/tests/approved/canvas/2d.gradient.radial.touch1.html
+ if (src->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT ||
+ src->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0 ||
+ src->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE ||
+ src->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT)
+ _cairo_output_stream_printf (stream,
+ "#ifdef GL_ES\n"
+ "precision highp float;\n"
+ "#endif\n");
+ else
+ _cairo_output_stream_printf (stream,
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n");
+
+ _cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE);
+ _cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK);
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) {
+ if (_cairo_gl_shader_needs_border_fade (src))
+ _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE);
+ if (_cairo_gl_shader_needs_border_fade (mask))
+ _cairo_gl_shader_emit_border_fade (stream, mask, CAIRO_GL_TEX_MASK);
+ }
+
+ cairo_gl_shader_emit_color (stream, ctx, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_color (stream, ctx, mask, CAIRO_GL_TEX_MASK);
+
+ coverage_str = "";
+ if (use_coverage) {
+ _cairo_output_stream_printf (stream, "varying float coverage;\n");
+ coverage_str = " * coverage";
+ }
+
+ _cairo_output_stream_printf (stream,
+ "void main()\n"
+ "{\n");
+ switch (in) {
+ case CAIRO_GL_SHADER_IN_COUNT:
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_SHADER_IN_NORMAL:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source() * get_mask().a%s;\n",
+ coverage_str);
+ break;
+ case CAIRO_GL_SHADER_IN_CA_SOURCE:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source() * get_mask()%s;\n",
+ coverage_str);
+ break;
+ case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source().a * get_mask()%s;\n",
+ coverage_str);
+ break;
+ }
+
+ _cairo_output_stream_write (stream,
+ "}\n\0", 3);
+
+ status = _cairo_memory_stream_destroy (stream, &source, &length);
+ if (unlikely (status)) {
+ free (source);
+ return status;
+ }
+
+ *out = (char *) source;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+compile_shader (cairo_gl_context_t *ctx,
+ GLuint *shader,
+ GLenum type,
+ const char *source)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint success, log_size, num_chars;
+ char *log;
+
+ *shader = dispatch->CreateShader (type);
+ dispatch->ShaderSource (*shader, 1, &source, 0);
+ dispatch->CompileShader (*shader);
+ dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &success);
+
+ if (success)
+ return;
+
+ dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
+ if (log_size < 0) {
+ printf ("OpenGL shader compilation failed.\n");
+ ASSERT_NOT_REACHED;
+ return;
+ }
+
+ log = _cairo_malloc (log_size + 1);
+ if (log == NULL) {
+ printf ("OpenGL shader compilation failed.\n");
+ ASSERT_NOT_REACHED;
+ }
+
+ dispatch->GetShaderInfoLog (*shader, log_size, &num_chars, log);
+ log[num_chars] = '\0';
+
+ printf ("OpenGL shader compilation failed. Shader:\n%s\n", source);
+ printf ("OpenGL compilation log:\n%s\n", log);
+
+ free (log);
+ ASSERT_NOT_REACHED;
+}
+
+static void
+link_shader_program (cairo_gl_context_t *ctx,
+ GLuint *program,
+ GLuint vert,
+ GLuint frag)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint success, log_size, num_chars;
+ char *log;
+
+ *program = dispatch->CreateProgram ();
+ dispatch->AttachShader (*program, vert);
+ dispatch->AttachShader (*program, frag);
+
+ dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX,
+ "Vertex");
+ dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX,
+ "Color");
+ dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX,
+ "MultiTexCoord0");
+ dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX,
+ "MultiTexCoord1");
+ dispatch->BindAttribLocation (*program, CAIRO_GL_START_COORD0_ATTRIB_INDEX,
+ "StartCoords0");
+ dispatch->BindAttribLocation (*program, CAIRO_GL_START_COORD1_ATTRIB_INDEX,
+ "StartCoords1");
+ dispatch->BindAttribLocation (*program, CAIRO_GL_STOP_COORD0_ATTRIB_INDEX,
+ "StopCoords0");
+ dispatch->BindAttribLocation (*program, CAIRO_GL_STOP_COORD1_ATTRIB_INDEX,
+ "StopCoords1");
+
+ dispatch->LinkProgram (*program);
+ dispatch->GetProgramiv (*program, GL_LINK_STATUS, &success);
+ if (success)
+ return;
+
+ dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
+ if (log_size < 0) {
+ printf ("OpenGL shader link failed.\n");
+ ASSERT_NOT_REACHED;
+ return;
+ }
+
+ log = _cairo_malloc (log_size + 1);
+ if (log == NULL) {
+ printf ("OpenGL shader link failed.\n");
+ ASSERT_NOT_REACHED;
+ }
+
+ dispatch->GetProgramInfoLog (*program, log_size, &num_chars, log);
+ log[num_chars] = '\0';
+
+ printf ("OpenGL shader link failed:\n%s\n", log);
+ free (log);
+ ASSERT_NOT_REACHED;
+}
+
+static cairo_status_t
+_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader,
+ cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ cairo_bool_t use_coverage,
+ const char *fragment_text,
+ cairo_extend_t src_atlas_extend,
+ cairo_extend_t mask_atlas_extend,
+ cairo_bool_t src_use_atlas,
+ cairo_bool_t mask_use_atlas)
+{
+ unsigned int vertex_shader;
+ cairo_status_t status;
+
+ assert (shader->program == 0);
+
+ vertex_shader = cairo_gl_var_type_hash (src, mask,
+ src_atlas_extend,
+ mask_atlas_extend,
+ src_use_atlas,
+ mask_use_atlas,
+ use_coverage,
+ CAIRO_GL_VAR_NONE);
+ if (ctx->vertex_shaders[vertex_shader] == 0) {
+ char *source;
+
+ status = cairo_gl_shader_get_vertex_source (src,
+ mask,
+ src_use_atlas,
+ mask_use_atlas,
+ use_coverage,
+ CAIRO_GL_VAR_NONE,
+ &source);
+ if (unlikely (status))
+ goto FAILURE;
+
+ compile_shader (ctx, &ctx->vertex_shaders[vertex_shader],
+ GL_VERTEX_SHADER, source);
+ free (source);
+ }
+
+ compile_shader (ctx, &shader->fragment_shader,
+ GL_FRAGMENT_SHADER, fragment_text);
+
+ link_shader_program (ctx, &shader->program,
+ ctx->vertex_shaders[vertex_shader],
+ shader->fragment_shader);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ FAILURE:
+ _cairo_gl_shader_fini (ctx, shader);
+ shader->fragment_shader = 0;
+ shader->program = 0;
+
+ return status;
+}
+
+/* We always bind the source to texture unit 0 if present, and mask to
+ * texture unit 1 if present, so we can just initialize these once at
+ * compile time.
+ */
+static void
+_cairo_gl_shader_set_samplers (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location;
+ GLint saved_program;
+
+ /* We have to save/restore the current program because we might be
+ * asked for a different program while a shader is bound. This shouldn't
+ * be a performance issue, since this is only called once per compile.
+ */
+ dispatch->GetIntegerv (GL_CURRENT_PROGRAM, &saved_program);
+ dispatch->UseProgram (shader->program);
+
+ location = _cairo_gl_shader_get_uniform_location (ctx, shader,
+ CAIRO_GL_UNIFORM_SAMPLER);
+ if (location != -1) {
+ dispatch->Uniform1i (location, CAIRO_GL_TEX_SOURCE);
+ }
+
+ location = _cairo_gl_shader_get_uniform_location (ctx, shader,
+ CAIRO_GL_UNIFORM_MASK_SAMPLER);
+ if (location != -1) {
+ dispatch->Uniform1i (location, CAIRO_GL_TEX_MASK);
+ }
+
+ dispatch->UseProgram (saved_program);
+}
+
+void
+_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+ assert (location != -1);
+ dispatch->Uniform1f (location, value);
+}
+
+void
+_cairo_gl_shader_bind_int (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ int value)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+ assert (location != -1);
+ dispatch->Uniform1i (location, value);
+}
+
+void
+_cairo_gl_shader_bind_float_array (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ int num, float *values)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+ assert (location != -1);
+ dispatch->Uniform1fv (location, num, values);
+}
+
+void
+_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value0,
+ float value1)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+ assert (location != -1);
+ dispatch->Uniform2f (location, value0, value1);
+}
+
+void
+_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value0,
+ float value1,
+ float value2)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+ assert (location != -1);
+ dispatch->Uniform3f (location, value0, value1, value2);
+}
+
+void
+_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ float value0, float value1,
+ float value2, float value3)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+ assert (location != -1);
+ dispatch->Uniform4f (location, value0, value1, value2, value3);
+}
+
+void
+_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ const cairo_matrix_t* m)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+
+ float gl_m[9] = {
+ m->xx, m->yx, 0,
+ m->xy, m->yy, 0,
+ m->x0, m->y0, 1
+ };
+ assert (location != -1);
+ dispatch->UniformMatrix3fv (location, 1, GL_FALSE, gl_m);
+}
+
+void
+_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
+ cairo_gl_uniform_t uniform,
+ GLfloat* gl_m)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ GLint location = _cairo_gl_shader_get_uniform_location (ctx,
+ ctx->current_shader,
+ uniform);
+ assert (location != -1);
+ dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m);
+}
+
+void
+_cairo_gl_set_shader (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader)
+{
+ if (ctx->current_shader == shader)
+ return;
+
+ if (shader)
+ ctx->dispatch.UseProgram (shader->program);
+ else
+ ctx->dispatch.UseProgram (0);
+
+ ctx->current_shader = shader;
+}
+
+cairo_status_t
+_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
+ cairo_gl_operand_t *source,
+ cairo_gl_operand_t *mask,
+ cairo_bool_t use_coverage,
+ cairo_gl_shader_in_t in,
+ cairo_gl_shader_t **shader)
+{
+ cairo_shader_cache_entry_t lookup, *entry;
+ char *fs_source;
+ cairo_status_t status;
+
+ lookup.ctx = ctx;
+
+ lookup.vertex = cairo_gl_var_type_hash (cairo_gl_operand_get_var_type (source),
+ cairo_gl_operand_get_var_type (mask),
+ _cairo_gl_operand_get_atlas_extend (source),
+ _cairo_gl_operand_get_atlas_extend (mask),
+ _cairo_gl_operand_get_use_atlas (source),
+ _cairo_gl_operand_get_use_atlas (mask),
+ use_coverage,
+ CAIRO_GL_VAR_NONE);
+
+ lookup.src = source->type;
+ lookup.mask = mask->type;
+ lookup.dest = CAIRO_GL_OPERAND_NONE;
+ lookup.use_coverage = use_coverage;
+ lookup.in = in;
+ lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source);
+ lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source);
+ lookup.src_extend = _cairo_gl_operand_get_atlas_extend (source);
+ lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask);
+ lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask);
+ lookup.mask_extend = _cairo_gl_operand_get_atlas_extend (mask);
+ lookup.src_use_atlas = _cairo_gl_operand_get_use_atlas (source);
+ lookup.mask_use_atlas = _cairo_gl_operand_get_use_atlas (mask);
+ lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup);
+ lookup.base.size = 1;
+
+ entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base);
+ if (entry) {
+ assert (entry->shader.program);
+ *shader = &entry->shader;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = cairo_gl_shader_get_fragment_source (ctx,
+ in,
+ source,
+ mask,
+ use_coverage,
+ CAIRO_GL_OPERAND_NONE,
+ &fs_source);
+ if (unlikely (status))
+ return status;
+
+ entry = malloc (sizeof (cairo_shader_cache_entry_t));
+ if (unlikely (entry == NULL)) {
+ free (fs_source);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t));
+
+ entry->ctx = ctx;
+ _cairo_gl_shader_init (&entry->shader);
+ status = _cairo_gl_shader_compile_and_link (ctx,
+ &entry->shader,
+ cairo_gl_operand_get_var_type (source),
+ cairo_gl_operand_get_var_type (mask),
+ use_coverage,
+ fs_source,
+ _cairo_gl_operand_get_atlas_extend (source),
+ _cairo_gl_operand_get_atlas_extend (mask),
+ _cairo_gl_operand_get_use_atlas (source),
+ _cairo_gl_operand_get_use_atlas (mask));
+ free (fs_source);
+
+ if (unlikely (status)) {
+ free (entry);
+ return status;
+ }
+
+ _cairo_gl_shader_set_samplers (ctx, &entry->shader);
+
+ status = _cairo_cache_insert (&ctx->shaders, &entry->base);
+ if (unlikely (status)) {
+ _cairo_gl_shader_fini (ctx, &entry->shader);
+ free (entry);
+ return status;
+ }
+
+ *shader = &entry->shader;
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-gl-source.c b/src/cairo-gl-source.c
new file mode 100755
index 000000000..53c3ed35a
--- /dev/null
+++ b/src/cairo-gl-source.c
@@ -0,0 +1,111 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-surface-backend-private.h"
+
+static cairo_status_t
+_cairo_gl_source_finish (void *abstract_surface)
+{
+ cairo_gl_source_t *source = abstract_surface;
+
+ _cairo_gl_operand_destroy (&source->operand);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_gl_source_backend = {
+ CAIRO_SURFACE_TYPE_GL,
+ _cairo_gl_source_finish,
+ NULL, /* read-only wrapper */
+};
+
+cairo_surface_t *
+_cairo_gl_pattern_to_source (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_gl_source_t *source;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (pattern == NULL)
+ return _cairo_gl_white_source ();
+
+ source = malloc (sizeof (*source));
+ if (unlikely (source == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&source->base,
+ &cairo_gl_source_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ *src_x = *src_y = 0;
+ status = _cairo_gl_operand_init (&source->operand, pattern,
+ (cairo_gl_surface_t *)dst,
+ sample, extents,
+ FALSE, FALSE);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&source->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &source->base;
+}
+
+cairo_surface_t *
+_cairo_gl_white_source (void)
+{
+ cairo_gl_source_t *source;
+
+ source = malloc (sizeof (*source));
+ if (unlikely (source == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&source->base,
+ &cairo_gl_source_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ _cairo_gl_solid_operand_init (&source->operand, CAIRO_COLOR_WHITE);
+
+ return &source->base;
+}
diff --git a/src/cairo-gl-spans-compositor.c b/src/cairo-gl-spans-compositor.c
new file mode 100755
index 000000000..724ae6661
--- /dev/null
+++ b/src/cairo-gl-spans-compositor.c
@@ -0,0 +1,553 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef struct _cairo_gl_span_renderer {
+ cairo_span_renderer_t base;
+
+ cairo_gl_composite_t setup;
+ double opacity;
+
+ cairo_gl_emit_span_t emit;
+
+ int xmin, xmax;
+ int ymin, ymax;
+
+ cairo_gl_context_t *ctx;
+} cairo_gl_span_renderer_t;
+
+static cairo_status_t
+_cairo_gl_bounded_opaque_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_span_renderer_t *r = abstract_renderer;
+ cairo_gl_emit_span_t emit = r->emit;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ emit (r->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ spans[0].coverage);
+ }
+
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_span_renderer_t *r = abstract_renderer;
+ cairo_gl_emit_span_t emit = r->emit;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ emit (r->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ r->opacity * spans[0].coverage);
+ }
+
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_span_renderer_t *r = abstract_renderer;
+ cairo_gl_emit_span_t emit = r->emit;
+
+ if (y > r->ymin) {
+ emit (r->ctx,
+ r->xmin, r->ymin,
+ r->xmax, y,
+ 0);
+ }
+
+ if (num_spans == 0) {
+ emit (r->ctx,
+ r->xmin, y,
+ r->xmax, y + height,
+ 0);
+ } else {
+ if (spans[0].x != r->xmin) {
+ emit (r->ctx,
+ r->xmin, y,
+ spans[0].x, y + height,
+ 0);
+ }
+
+ do {
+ emit (r->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != r->xmax) {
+ emit (r->ctx,
+ spans[0].x, y,
+ r->xmax, y + height,
+ 0);
+ }
+ }
+
+ r->ymin = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* XXX */
+static cairo_status_t
+_cairo_gl_clipped_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_span_renderer_t *r = abstract_renderer;
+ cairo_gl_emit_span_t emit = r->emit;
+
+ if (y > r->ymin) {
+ emit (r->ctx,
+ r->xmin, r->ymin,
+ r->xmax, y,
+ 0);
+ }
+
+ if (num_spans == 0) {
+ emit (r->ctx,
+ r->xmin, y,
+ r->xmax, y + height,
+ 0);
+ } else {
+ if (spans[0].x != r->xmin) {
+ emit (r->ctx,
+ r->xmin, y,
+ spans[0].x, y + height,
+ 0);
+ }
+
+ do {
+ emit (r->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != r->xmax) {
+ emit (r->ctx,
+ spans[0].x, y,
+ r->xmax, y + height,
+ 0);
+ }
+ }
+
+ r->ymin = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_finish_unbounded_spans (void *abstract_renderer)
+{
+ cairo_gl_span_renderer_t *r = abstract_renderer;
+ cairo_gl_emit_span_t emit = r->emit;
+
+ if (r->ymax > r->ymin) {
+ emit (r->ctx,
+ r->xmin, r->ymin,
+ r->xmax, r->ymax,
+ 0);
+ }
+
+ return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+_cairo_gl_finish_bounded_spans (void *abstract_renderer)
+{
+ cairo_gl_span_renderer_t *r = abstract_renderer;
+
+ return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static void
+emit_aligned_boxes (cairo_gl_context_t *ctx,
+ const cairo_boxes_t *boxes)
+{
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx);
+ int i;
+
+ TRACE ((stderr, "%s: num_boxes=%d\n", __FUNCTION__, boxes->num_boxes));
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+ emit (ctx, x1, y1, x2, y2);
+ }
+ }
+}
+
+static cairo_int_status_t
+fill_boxes (void *_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_solid_source (&setup, color);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ emit_aligned_boxes (ctx, boxes);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+ return status;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy)
+{
+ cairo_gl_surface_t *dst = _dst;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ if (_cairo_gl_surface_is_texture (dst))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+ int x = _cairo_fixed_integer_part (b->p1.x);
+ int y = _cairo_fixed_integer_part (b->p1.y);
+ int w = _cairo_fixed_integer_part (b->p2.x) - x;
+ int h = _cairo_fixed_integer_part (b->p2.y) - y;
+ cairo_status_t status;
+
+ status = _cairo_gl_surface_draw_image (dst, image,
+ x + dx, y + dy,
+ w, h,
+ x, y, TRUE);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t copy_boxes (void *_dst,
+ cairo_surface_t *_src,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents,
+ int dx, int dy)
+{
+ cairo_gl_surface_t *dst = _dst;
+ cairo_gl_surface_t *src = (cairo_gl_surface_t *)_src;
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (! _cairo_gl_surface_is_texture (src))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (src->base.device != dst->base.device)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_source_operand (&setup, &src->operand);
+ _cairo_gl_operand_translate (&setup.src, -dx, -dy);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ emit_aligned_boxes (ctx, boxes);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+ return status;
+}
+
+static cairo_int_status_t
+composite_boxes (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+ cairo_gl_operand_t tmp_operand;
+ cairo_gl_operand_t *src_operand;
+
+ TRACE ((stderr, "%s mask=(%d,%d), dst=(%d, %d)\n", __FUNCTION__,
+ mask_x, mask_y, dst_x, dst_y));
+
+ if (abstract_mask) {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ _cairo_gl_solid_operand_init (&tmp_operand, CAIRO_COLOR_WHITE);
+ src_operand = &tmp_operand;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ } else if (op == CAIRO_OPERATOR_SOURCE) {
+ /* requires a LERP in the shader between dest and source */
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ } else
+ src_operand = source_to_operand (abstract_src);
+ } else
+ src_operand = source_to_operand (abstract_src);
+
+ status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_source_operand (&setup,
+ src_operand);
+ _cairo_gl_operand_translate (&setup.src, -src_x, -src_y);
+
+ _cairo_gl_composite_set_mask_operand (&setup,
+ source_to_operand (abstract_mask));
+ _cairo_gl_operand_translate (&setup.mask, -mask_x, -mask_y);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ emit_aligned_boxes (ctx, boxes);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+ if (src_operand == &tmp_operand)
+ _cairo_gl_operand_destroy (&tmp_operand);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_span_renderer_init (cairo_abstract_span_renderer_t *_r,
+ const cairo_composite_rectangles_t *composite,
+ cairo_antialias_t antialias,
+ cairo_bool_t needs_clip)
+{
+ cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *)_r;
+ const cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_operator_t op = composite->op;
+ cairo_int_status_t status;
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ if (! _cairo_pattern_is_opaque (&composite->source_pattern.base,
+ &composite->source_sample_area))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ op = CAIRO_OPERATOR_OVER;
+ }
+
+ /* XXX earlier! */
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ source = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ } else if (composite->surface->is_clear &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ op == CAIRO_OPERATOR_OVER ||
+ op == CAIRO_OPERATOR_ADD)) {
+ op = CAIRO_OPERATOR_SOURCE;
+ } else if (! _cairo_gl_operator_is_supported (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_gl_composite_init (&r->setup,
+ op, (cairo_gl_surface_t *)composite->surface,
+ FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _cairo_gl_composite_set_source (&r->setup, source,
+ &composite->source_sample_area,
+ &composite->unbounded,
+ TRUE, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ r->opacity = 1.0;
+ if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+ r->opacity = composite->mask_pattern.solid.color.alpha;
+ } else {
+ status = _cairo_gl_composite_set_mask (&r->setup,
+ &composite->mask_pattern.base,
+ &composite->mask_sample_area,
+ &composite->unbounded,
+ TRUE);
+ if (unlikely (status))
+ goto FAIL;
+ }
+
+ _cairo_gl_composite_set_spans (&r->setup);
+
+ status = _cairo_gl_composite_begin (&r->setup, &r->ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ r->emit = _cairo_gl_context_choose_emit_span (r->ctx);
+ if (composite->is_bounded) {
+ if (r->opacity == 1.)
+ r->base.render_rows = _cairo_gl_bounded_opaque_spans;
+ else
+ r->base.render_rows = _cairo_gl_bounded_spans;
+ r->base.finish = _cairo_gl_finish_bounded_spans;
+ } else {
+ if (needs_clip)
+ r->base.render_rows = _cairo_gl_clipped_spans;
+ else
+ r->base.render_rows = _cairo_gl_unbounded_spans;
+ r->base.finish = _cairo_gl_finish_unbounded_spans;
+ r->xmin = composite->unbounded.x;
+ r->xmax = composite->unbounded.x + composite->unbounded.width;
+ r->ymin = composite->unbounded.y;
+ r->ymax = composite->unbounded.y + composite->unbounded.height;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+FAIL:
+ return status;
+}
+
+static void
+_cairo_gl_span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+ cairo_int_status_t status)
+{
+ cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *) _r;
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ return;
+
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ r->base.finish (r);
+
+ _cairo_gl_composite_fini (&r->setup);
+}
+
+const cairo_compositor_t *
+_cairo_gl_span_compositor_get (void)
+{
+ static cairo_spans_compositor_t spans;
+ static cairo_compositor_t shape;
+
+ if (spans.base.delegate == NULL) {
+ /* The fallback to traps here is essentially just for glyphs... */
+ _cairo_shape_mask_compositor_init (&shape,
+ _cairo_gl_traps_compositor_get());
+ shape.glyphs = NULL;
+
+ _cairo_spans_compositor_init (&spans, &shape);
+ spans.fill_boxes = fill_boxes;
+ spans.draw_image_boxes = draw_image_boxes;
+ spans.copy_boxes = copy_boxes;
+ //spans.check_composite_boxes = check_composite_boxes;
+ spans.pattern_to_surface = _cairo_gl_pattern_to_source;
+ spans.composite_boxes = composite_boxes;
+ //spans.check_span_renderer = check_span_renderer;
+ spans.renderer_init = _cairo_gl_span_renderer_init;
+ spans.renderer_fini = _cairo_gl_span_renderer_fini;
+ }
+
+ return &spans.base;
+}
diff --git a/src/cairo-gl-surface-legacy.c b/src/cairo-gl-surface-legacy.c
new file mode 100755
index 000000000..92b27c905
--- /dev/null
+++ b/src/cairo-gl-surface-legacy.c
@@ -0,0 +1,603 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-gl-private.h"
+#include "cairo-image-surface-inline.h"
+
+cairo_status_t
+_cairo_gl_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ status = _cairo_gl_surface_deferred_clear (surface);
+ if (unlikely (status))
+ return status;
+
+ *image_extra = NULL;
+ return _cairo_gl_surface_get_image (surface, interest_rect, image_out,
+ image_rect_out);
+}
+
+void
+_cairo_gl_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_status_t status;
+
+ status = _cairo_gl_surface_draw_image (abstract_surface, image,
+ 0, 0,
+ image->width, image->height,
+ image_rect->x, image_rect->y,
+ TRUE);
+ /* as we created the image, its format should be directly applicable */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_surface_destroy (&image->base);
+}
+
+cairo_status_t
+_cairo_gl_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */
+ if (src->device == surface->base.device &&
+ _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) {
+ status = _cairo_gl_surface_deferred_clear ((cairo_gl_surface_t *)src);
+ if (unlikely (status))
+ return status;
+
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ } else if (_cairo_surface_is_image (src)) {
+ cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
+ cairo_gl_surface_t *clone;
+
+ clone = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_similar (&surface->base,
+ src->content,
+ width, height);
+ if (clone == NULL)
+ return UNSUPPORTED ("create_similar failed");
+ if (clone->base.status)
+ return clone->base.status;
+
+ status = _cairo_gl_surface_draw_image (clone, image_src,
+ src_x, src_y,
+ width, height,
+ 0, 0, TRUE);
+ if (status) {
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+
+ *clone_out = &clone->base;
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return UNSUPPORTED ("unknown src surface type in clone_similar");
+}
+
+/* Creates a cairo-gl pattern surface for the given trapezoids */
+static cairo_status_t
+_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst,
+ int dst_x, int dst_y,
+ int width, int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_antialias_t antialias,
+ cairo_surface_pattern_t *pattern)
+{
+ pixman_format_code_t pixman_format;
+ pixman_image_t *image;
+ cairo_surface_t *surface;
+ int i;
+
+ pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
+ image = pixman_image_create_bits (pixman_format, width, height, NULL, 0);
+ if (unlikely (image == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (i = 0; i < num_traps; i++) {
+ pixman_trapezoid_t trap;
+
+ trap.top = _cairo_fixed_to_16_16 (traps[i].top);
+ trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom);
+
+ trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x);
+ trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y);
+ trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x);
+ trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y);
+
+ trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x);
+ trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y);
+ trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x);
+ trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y);
+
+ pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+ }
+
+ surface = _cairo_image_surface_create_for_pixman_image (image,
+ pixman_format);
+ if (unlikely (surface->status)) {
+ pixman_image_unref (image);
+ return surface->status;
+ }
+
+ _cairo_pattern_init_for_surface (pattern, surface);
+ cairo_surface_destroy (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_gl_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+ cairo_gl_composite_t setup;
+ cairo_rectangle_int_t rect = { dst_x, dst_y, width, height };
+ int dx, dy;
+
+ status = _cairo_gl_surface_deferred_clear (dst);
+ if (unlikely (status))
+ return status;
+
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ mask == NULL &&
+ src->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) &&
+ _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) {
+ cairo_image_surface_t *image = (cairo_image_surface_t *)
+ ((cairo_surface_pattern_t *) src)->surface;
+ dx += src_x;
+ dy += src_y;
+ if (dx >= 0 &&
+ dy >= 0 &&
+ dx + width <= (unsigned int) image->width &&
+ dy + height <= (unsigned int) image->height) {
+ status = _cairo_gl_surface_draw_image (dst, image,
+ dx, dy,
+ width, height,
+ dst_x, dst_y, TRUE);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+ }
+
+ status = _cairo_gl_composite_init (&setup, op, dst,
+ mask && mask->has_component_alpha,
+ &rect);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_set_source (&setup, src,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_set_mask (&setup, mask,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ if (clip_region != NULL) {
+ int i, num_rectangles;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, i, &rect);
+ _cairo_gl_composite_emit_rect (ctx,
+ rect.x, rect.y,
+ rect.x + rect.width, rect.y + rect.height,
+ 0);
+ }
+ } else {
+ _cairo_gl_composite_emit_rect (ctx,
+ dst_x, dst_y,
+ dst_x + width, dst_y + height,
+ 0);
+ }
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ CLEANUP:
+ _cairo_gl_composite_fini (&setup);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_gl_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_surface_pattern_t traps_pattern;
+ cairo_int_status_t status;
+
+ if (! _cairo_gl_operator_is_supported (op))
+ return UNSUPPORTED ("unsupported operator");
+
+ status = _cairo_gl_surface_deferred_clear (dst);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_get_traps_pattern (dst,
+ dst_x, dst_y, width, height,
+ traps, num_traps, antialias,
+ &traps_pattern);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_surface_composite (op,
+ pattern, &traps_pattern.base, dst,
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+
+ _cairo_pattern_fini (&traps_pattern.base);
+
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ return status;
+}
+
+cairo_int_status_t
+_cairo_gl_surface_fill_rectangles (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_solid_pattern_t solid;
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+ cairo_gl_composite_t setup;
+ int i;
+
+ status = _cairo_gl_surface_deferred_clear (dst);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_composite_init (&setup, op, dst,
+ FALSE,
+ /* XXX */ NULL);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ _cairo_pattern_init_solid (&solid, color);
+ status = _cairo_gl_composite_set_source (&setup, &solid.base,
+ 0, 0,
+ 0, 0,
+ 0, 0);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_set_mask (&setup, NULL,
+ 0, 0,
+ 0, 0,
+ 0, 0);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ for (i = 0; i < num_rects; i++) {
+ _cairo_gl_composite_emit_rect (ctx,
+ rects[i].x,
+ rects[i].y,
+ rects[i].x + rects[i].width,
+ rects[i].y + rects[i].height,
+ 0);
+ }
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ CLEANUP:
+ _cairo_gl_composite_fini (&setup);
+
+ return status;
+}
+
+typedef struct _cairo_gl_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ cairo_gl_composite_t setup;
+
+ int xmin, xmax;
+ int ymin, ymax;
+
+ cairo_gl_context_t *ctx;
+} cairo_gl_surface_span_renderer_t;
+
+static cairo_status_t
+_cairo_gl_render_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ spans[0].coverage);
+ }
+
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_render_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (y > renderer->ymin) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, renderer->ymin,
+ renderer->xmax, y,
+ 0);
+ }
+
+ if (num_spans == 0) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, y,
+ renderer->xmax, y + height,
+ 0);
+ } else {
+ if (spans[0].x != renderer->xmin) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, y,
+ spans[0].x, y + height,
+ 0);
+ }
+
+ do {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != renderer->xmax) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ spans[0].x, y,
+ renderer->xmax, y + height,
+ 0);
+ }
+ }
+
+ renderer->ymin = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_finish_unbounded_spans (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (renderer->ymax > renderer->ymin) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, renderer->ymin,
+ renderer->xmax, renderer->ymax,
+ 0);
+ }
+
+ return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+_cairo_gl_finish_bounded_spans (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static void
+_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (!renderer)
+ return;
+
+ _cairo_gl_composite_fini (&renderer->setup);
+
+ free (renderer);
+}
+
+cairo_bool_t
+_cairo_gl_surface_check_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias)
+{
+ if (! _cairo_gl_operator_is_supported (op))
+ return FALSE;
+
+ return TRUE;
+
+ (void) pattern;
+ (void) abstract_dst;
+ (void) antialias;
+}
+
+cairo_span_renderer_t *
+_cairo_gl_surface_create_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_gl_surface_span_renderer_t *renderer;
+ cairo_status_t status;
+ const cairo_rectangle_int_t *extents;
+
+ status = _cairo_gl_surface_deferred_clear (dst);
+ if (unlikely (status))
+ return _cairo_span_renderer_create_in_error (status);
+
+ renderer = calloc (1, sizeof (*renderer));
+ if (unlikely (renderer == NULL))
+ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy;
+ if (rects->is_bounded) {
+ renderer->base.render_rows = _cairo_gl_render_bounded_spans;
+ renderer->base.finish = _cairo_gl_finish_bounded_spans;
+ extents = &rects->bounded;
+ } else {
+ renderer->base.render_rows = _cairo_gl_render_unbounded_spans;
+ renderer->base.finish = _cairo_gl_finish_unbounded_spans;
+ extents = &rects->unbounded;
+ }
+ renderer->xmin = extents->x;
+ renderer->xmax = extents->x + extents->width;
+ renderer->ymin = extents->y;
+ renderer->ymax = extents->y + extents->height;
+
+ status = _cairo_gl_composite_init (&renderer->setup,
+ op, dst,
+ FALSE, extents);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _cairo_gl_composite_set_source (&renderer->setup, src,
+ extents->x, extents->y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_spans (&renderer->setup);
+ _cairo_gl_composite_set_clip_region (&renderer->setup,
+ _cairo_clip_get_region (rects->clip));
+
+ status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ return &renderer->base;
+
+FAIL:
+ _cairo_gl_composite_fini (&renderer->setup);
+ free (renderer);
+ return _cairo_span_renderer_create_in_error (status);
+}
+
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
new file mode 100755
index 000000000..ac19a13ad
--- /dev/null
+++ b/src/cairo-gl-surface.c
@@ -0,0 +1,2142 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-shadow-private.h"
+#include "cairo-surface-scale-translate-private.h"
+
+static const cairo_surface_backend_t _cairo_gl_surface_backend;
+
+static cairo_status_t
+_cairo_gl_surface_flush (void *abstract_surface, unsigned flags);
+
+static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
+{
+ return surface->backend == &_cairo_gl_surface_backend;
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_shadow_surface (void *surface,
+ const cairo_bool_t has_blur,
+ int width, int height,
+ int *width_out, int *height_out)
+{
+ int shadow_width, shadow_height;
+ cairo_gl_surface_t *shadow_surface = NULL;
+
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ if (ctx == NULL)
+ return NULL;
+
+ shadow_surface = ctx->shadow_scratch_surfaces[0];
+
+ if (shadow_surface) {
+ shadow_width = shadow_surface->width;
+ shadow_height = shadow_surface->height;
+
+ if (! has_blur) {
+ if(shadow_width >= width &&
+ shadow_height >= height) {
+ *width_out = width;
+ *height_out = height;
+ return cairo_surface_reference (&shadow_surface->base);
+ }
+ else {
+ cairo_surface_destroy (&shadow_surface->base);
+ shadow_surface = NULL;
+ }
+ }
+ else {
+ if (shadow_width * 2 < width &&
+ shadow_height * 2 < height) {
+ if (shadow_width < MAX_SCRATCH_SIZE ||
+ shadow_height < MAX_SCRATCH_SIZE) {
+ cairo_surface_destroy (&shadow_surface->base);
+ shadow_surface = NULL;
+ }
+ }
+ else if (shadow_width > 4 * width &&
+ shadow_height > 4 * height) {
+ cairo_surface_destroy (&shadow_surface->base);
+ shadow_surface = NULL;
+ }
+ }
+ }
+
+ if (! shadow_surface) {
+ shadow_width = shadow_height = MIN_SCRATCH_SIZE;
+ if (has_blur) {
+ while (shadow_width * 2 < width) {
+ shadow_width *= 2;
+ if (shadow_width == MAX_SCRATCH_SIZE)
+ break;
+ else if (shadow_width > MAX_SCRATCH_SIZE) {
+ shadow_width *= 0.5;
+ break;
+ }
+ }
+ while (shadow_height * 2 < height) {
+ shadow_height *= 2;
+ if (shadow_height == MAX_SCRATCH_SIZE)
+ break;
+ else if (shadow_height > MAX_SCRATCH_SIZE) {
+ shadow_height *= 0.5;
+ break;
+ }
+ }
+ }
+ else {
+ while (shadow_width < width) {
+ shadow_width *= 2;
+ if (shadow_width == MAX_SCRATCH_SIZE)
+ break;
+ else if (shadow_width > MAX_SCRATCH_SIZE) {
+ shadow_width *= 0.5;
+ break;
+ }
+ }
+ while (shadow_height < height) {
+ shadow_height *= 2;
+ if (shadow_height == MAX_SCRATCH_SIZE)
+ break;
+ else if (shadow_height > MAX_SCRATCH_SIZE) {
+ shadow_height *= 0.5;
+ break;
+ }
+ }
+ }
+
+
+ shadow_surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ shadow_width,
+ shadow_height);
+ if (unlikely (shadow_surface->base.status)) {
+ cairo_surface_destroy (&shadow_surface->base);
+ return NULL;
+ }
+
+ _cairo_surface_release_device_reference (&shadow_surface->base);
+ }
+
+ ctx->shadow_scratch_surfaces[0] = shadow_surface;
+
+ shadow_surface->needs_to_cache = FALSE;
+ shadow_surface->force_no_cache = TRUE;
+
+ *width_out = width;
+ *height_out = height;
+
+ if (has_blur) {
+ while (*width_out > shadow_width) {
+ *width_out *= 0.5;
+ }
+
+ while (*height_out > shadow_height) {
+ *height_out *= 0.5;
+ }
+ }
+ else {
+ if (*width_out > MAX_SCRATCH_SIZE)
+ *width_out *= 0.5;
+ if (*height_out > MAX_SCRATCH_SIZE)
+ *height_out *= 0.5;
+ }
+
+ return cairo_surface_reference (&shadow_surface->base);
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_shadow_mask_surface (void *surface,
+ int width, int height,
+ unsigned int index)
+{
+ cairo_gl_surface_t *mask_surface = NULL;
+
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ if (ctx == NULL)
+ return NULL;
+
+ if (index > 1)
+ return NULL;
+
+ mask_surface = ctx->shadow_masks[index];
+
+ if (mask_surface) {
+ if (mask_surface->width != width ||
+ mask_surface->height != height) {
+ cairo_surface_destroy (&mask_surface->base);
+ mask_surface = NULL;
+ ctx->shadow_masks[index] = NULL;
+ }
+ }
+
+ if (! mask_surface) {
+ mask_surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width,
+ height);
+ if (unlikely (mask_surface->base.status)) {
+ cairo_surface_destroy (&mask_surface->base);
+ return NULL;
+ }
+ _cairo_surface_release_device_reference (&mask_surface->base);
+ }
+
+ ctx->shadow_masks[index] = mask_surface;
+
+ mask_surface->needs_to_cache = FALSE;
+ mask_surface->force_no_cache = TRUE;
+
+ return cairo_surface_reference (&mask_surface->base);
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_glyph_shadow_surface (void *surface,
+ int width, int height,
+ cairo_bool_t for_source)
+{
+ int shadow_width, shadow_height;
+ cairo_gl_surface_t *shadow_surface = NULL;
+
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ if (ctx == NULL)
+ return NULL;
+
+ if (! for_source)
+ shadow_surface = ctx->shadow_scratch_surfaces[1];
+ else
+ shadow_surface = ctx->shadow_scratch_surfaces[2];
+
+ if (shadow_surface) {
+ shadow_width = shadow_surface->width;
+ shadow_height = shadow_surface->height;
+
+ if (shadow_width < width ||
+ shadow_height < height) {
+ cairo_surface_destroy (&shadow_surface->base);
+ shadow_surface = NULL;
+ }
+ }
+
+ if (! shadow_surface) {
+ shadow_surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
+ if (unlikely (shadow_surface->base.status)) {
+ cairo_surface_destroy (&shadow_surface->base);
+ return NULL;
+ }
+ _cairo_surface_release_device_reference (&shadow_surface->base);
+ }
+
+ if (! for_source)
+ ctx->shadow_scratch_surfaces[1] = shadow_surface;
+ else
+ ctx->shadow_scratch_surfaces[2] = shadow_surface;
+
+ shadow_surface->needs_to_cache = FALSE;
+ shadow_surface->force_no_cache = TRUE;
+
+ return cairo_surface_reference (&shadow_surface->base);
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_glyph_shadow_mask_surface (void *surface,
+ int width, int height,
+ unsigned index)
+{
+ cairo_gl_surface_t *mask_surface = NULL;
+
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ if (ctx == NULL)
+ return NULL;
+
+ if (index > 1)
+ return NULL;
+
+ mask_surface = ctx->shadow_masks[index + 2];
+
+ if (mask_surface) {
+ if (mask_surface->width != width ||
+ mask_surface->height != height) {
+ cairo_surface_destroy (&mask_surface->base);
+ mask_surface = NULL;
+ ctx->shadow_masks[index + 2] = NULL;
+ }
+ }
+
+ if (! mask_surface) {
+ mask_surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_ALPHA,
+ width,
+ height);
+ if (unlikely (mask_surface->base.status)) {
+ cairo_surface_destroy (&mask_surface->base);
+ return NULL;
+ }
+ _cairo_surface_release_device_reference (&mask_surface->base);
+ }
+
+ ctx->shadow_masks[index + 2] = mask_surface;
+
+ mask_surface->needs_to_cache = FALSE;
+ mask_surface->force_no_cache = TRUE;
+
+ return cairo_surface_reference (&mask_surface->base);
+}
+
+static cairo_bool_t
+_cairo_gl_get_image_format_and_type_gles2 (pixman_format_code_t pixman_format,
+ GLenum *internal_format, GLenum *format,
+ GLenum *type, cairo_bool_t *has_alpha,
+ cairo_bool_t *needs_swap)
+{
+ cairo_bool_t is_little_endian = _cairo_is_little_endian ();
+
+ *has_alpha = TRUE;
+
+ switch ((int) pixman_format) {
+ case PIXMAN_a8r8g8b8:
+ *internal_format = GL_BGRA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_BYTE;
+ *needs_swap = !is_little_endian;
+ return TRUE;
+
+ case PIXMAN_x8r8g8b8:
+ *internal_format = GL_BGRA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_BYTE;
+ *has_alpha = FALSE;
+ *needs_swap = !is_little_endian;
+ return TRUE;
+
+ case PIXMAN_a8b8g8r8:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_BYTE;
+ *needs_swap = !is_little_endian;
+ return TRUE;
+
+ case PIXMAN_x8b8g8r8:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_BYTE;
+ *has_alpha = FALSE;
+ *needs_swap = !is_little_endian;
+ return TRUE;
+
+ case PIXMAN_b8g8r8a8:
+ *internal_format = GL_BGRA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_BYTE;
+ *needs_swap = is_little_endian;
+ return TRUE;
+
+ case PIXMAN_b8g8r8x8:
+ *internal_format = GL_BGRA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_BYTE;
+ *has_alpha = FALSE;
+ *needs_swap = is_little_endian;
+ return TRUE;
+
+ case PIXMAN_r8g8b8:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_BYTE;
+ *needs_swap = is_little_endian;
+ return TRUE;
+
+ case PIXMAN_b8g8r8:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_BYTE;
+ *needs_swap = !is_little_endian;
+ return TRUE;
+
+ case PIXMAN_r5g6b5:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5;
+ *needs_swap = FALSE;
+ return TRUE;
+
+ case PIXMAN_b5g6r5:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5;
+ *needs_swap = TRUE;
+ return TRUE;
+
+ case PIXMAN_a1b5g5r5:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_SHORT_5_5_5_1;
+ *needs_swap = TRUE;
+ return TRUE;
+
+ case PIXMAN_x1b5g5r5:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_SHORT_5_5_5_1;
+ *has_alpha = FALSE;
+ *needs_swap = TRUE;
+ return TRUE;
+
+ case PIXMAN_a8:
+ *internal_format = GL_ALPHA;
+ *format = GL_ALPHA;
+ *type = GL_UNSIGNED_BYTE;
+ *needs_swap = FALSE;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+_cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format,
+ GLenum *internal_format, GLenum *format,
+ GLenum *type, cairo_bool_t *has_alpha,
+ cairo_bool_t *needs_swap)
+{
+ *has_alpha = TRUE;
+ *needs_swap = FALSE;
+
+ switch (pixman_format) {
+ case PIXMAN_a8r8g8b8:
+ *internal_format = GL_RGBA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ return TRUE;
+ case PIXMAN_x8r8g8b8:
+ *internal_format = GL_RGB;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_a8b8g8r8:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ return TRUE;
+ case PIXMAN_x8b8g8r8:
+ *internal_format = GL_RGB;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_b8g8r8a8:
+ *internal_format = GL_RGBA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8;
+ return TRUE;
+ case PIXMAN_b8g8r8x8:
+ *internal_format = GL_RGB;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_INT_8_8_8_8;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_r8g8b8:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_BYTE;
+ return TRUE;
+ case PIXMAN_b8g8r8:
+ *internal_format = GL_RGB;
+ *format = GL_BGR;
+ *type = GL_UNSIGNED_BYTE;
+ return TRUE;
+ case PIXMAN_r5g6b5:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5;
+ return TRUE;
+ case PIXMAN_b5g6r5:
+ *internal_format = GL_RGB;
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5_REV;
+ return TRUE;
+ case PIXMAN_a1r5g5b5:
+ *internal_format = GL_RGBA;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ return TRUE;
+ case PIXMAN_x1r5g5b5:
+ *internal_format = GL_RGB;
+ *format = GL_BGRA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_a1b5g5r5:
+ *internal_format = GL_RGBA;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ return TRUE;
+ case PIXMAN_x1b5g5r5:
+ *internal_format = GL_RGB;
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ *has_alpha = FALSE;
+ return TRUE;
+ case PIXMAN_a8:
+ *internal_format = GL_ALPHA;
+ *format = GL_ALPHA;
+ *type = GL_UNSIGNED_BYTE;
+ return TRUE;
+
+ case PIXMAN_a2b10g10r10:
+ case PIXMAN_x2b10g10r10:
+ case PIXMAN_a4r4g4b4:
+ case PIXMAN_x4r4g4b4:
+ case PIXMAN_a4b4g4r4:
+ case PIXMAN_x4b4g4r4:
+ case PIXMAN_r3g3b2:
+ case PIXMAN_b2g3r3:
+ case PIXMAN_a2r2g2b2:
+ case PIXMAN_a2b2g2r2:
+ case PIXMAN_c8:
+ case PIXMAN_x4a4:
+ /* case PIXMAN_x4c4: */
+ case PIXMAN_x4g4:
+ case PIXMAN_a4:
+ case PIXMAN_r1g2b1:
+ case PIXMAN_b1g2r1:
+ case PIXMAN_a1r1g1b1:
+ case PIXMAN_a1b1g1r1:
+ case PIXMAN_c4:
+ case PIXMAN_g4:
+ case PIXMAN_a1:
+ case PIXMAN_g1:
+ case PIXMAN_yuy2:
+ case PIXMAN_yv12:
+ case PIXMAN_x2r10g10b10:
+ case PIXMAN_a2r10g10b10:
+ case PIXMAN_r8g8b8x8:
+ case PIXMAN_r8g8b8a8:
+ case PIXMAN_x14r6g6b6:
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ * Extracts pixel data from an image surface.
+ */
+static cairo_status_t
+_cairo_gl_surface_extract_image_data (cairo_image_surface_t *image,
+ int x, int y,
+ int width, int height,
+ void **output)
+{
+ int cpp = PIXMAN_FORMAT_BPP (image->pixman_format) / 8;
+ char *data = _cairo_malloc_ab (width * height, cpp);
+ char *dst = data;
+ unsigned char *src = image->data + y * image->stride + x * cpp;
+ int i;
+
+ if (unlikely (data == NULL))
+ return CAIRO_STATUS_NO_MEMORY;
+
+ for (i = 0; i < height; i++) {
+ memcpy (dst, src, width * cpp);
+ src += image->stride;
+ dst += width * cpp;
+ }
+
+ *output = data;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor,
+ pixman_format_code_t pixman_format,
+ GLenum *internal_format, GLenum *format,
+ GLenum *type, cairo_bool_t *has_alpha,
+ cairo_bool_t *needs_swap)
+{
+ if (flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return _cairo_gl_get_image_format_and_type_gl (pixman_format,
+ internal_format, format,
+ type, has_alpha,
+ needs_swap);
+ else
+ return _cairo_gl_get_image_format_and_type_gles2 (pixman_format,
+ internal_format, format,
+ type, has_alpha,
+ needs_swap);
+
+}
+
+cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op)
+{
+ return op < CAIRO_OPERATOR_SATURATE;
+}
+
+static void
+_cairo_gl_surface_embedded_operand_init (cairo_gl_surface_t *surface)
+{
+ cairo_gl_operand_t *operand = &surface->operand;
+ cairo_surface_attributes_t *attributes = &operand->texture.attributes;
+
+ memset (operand, 0, sizeof (cairo_gl_operand_t));
+
+ operand->type = CAIRO_GL_OPERAND_TEXTURE;
+ operand->texture.surface = surface;
+ operand->texture.tex = surface->tex;
+ operand->pass = 0;
+
+ if (_cairo_gl_device_requires_power_of_two_textures (surface->base.device)) {
+ cairo_matrix_init_identity (&attributes->matrix);
+ } else {
+ cairo_matrix_init_scale (&attributes->matrix,
+ 1.0 / surface->width,
+ 1.0 / surface->height);
+ }
+
+ attributes->extend = CAIRO_EXTEND_NONE;
+ attributes->filter = CAIRO_FILTER_NEAREST;
+}
+
+void
+_cairo_gl_surface_init (cairo_device_t *device,
+ cairo_gl_surface_t *surface,
+ cairo_content_t content,
+ int width, int height)
+{
+ assert (width > 0 && height > 0);
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_gl_surface_backend,
+ device,
+ content);
+
+ surface->width = width;
+ surface->height = height;
+ surface->needs_update = FALSE;
+ surface->size_changed = FALSE;
+ surface->needs_to_cache = FALSE;
+ surface->image_node = NULL;
+ surface->force_no_cache = FALSE;
+
+ surface->image_content_scale_x = 1.0;
+ surface->image_content_scale_y = 1.0;
+ surface->blur_stage = CAIRO_GL_BLUR_STAGE_NONE;
+
+ surface->clip_on_stencil_buffer = NULL;
+
+ surface->content_synced = TRUE;
+ surface->content_cleared = FALSE;
+
+ _cairo_gl_surface_embedded_operand_init (surface);
+}
+
+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_bool_t set_tex_param)
+{
+ 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));
+
+ surface->tex = tex;
+ _cairo_gl_surface_init (&ctx->base, surface, content, width, height);
+
+ surface->supports_msaa = ctx->supports_msaa;
+ surface->num_samples = ctx->num_samples;
+ surface->supports_stencil = TRUE;
+
+ /* Create the texture used to store the surface's data. */
+ if (!set_tex_param) {
+ _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+ ctx->dispatch.BindTexture (ctx->tex_target, surface->tex);
+ ctx->dispatch.TexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ ctx->dispatch.TexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+
+ return &surface->base;
+}
+
+static cairo_surface_t *
+_create_scratch_internal (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height,
+ cairo_bool_t for_caching)
+{
+ cairo_gl_surface_t *surface;
+ GLenum format;
+ GLuint tex;
+
+ ctx->dispatch.GenTextures (1, &tex);
+ surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch_for_texture (ctx, content,
+ tex, width, height, FALSE);
+ 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:
+ if (ctx->can_read_bgra)
+ format = GL_BGRA;
+ else
+ format = GL_RGBA;
+ break;
+ case CAIRO_CONTENT_ALPHA:
+ /* When using GL_ALPHA, compositing doesn't work properly, but for
+ * caching surfaces, we are just uploading pixel data, so it isn't
+ * an issue. */
+ if (for_caching)
+ format = GL_ALPHA;
+ else
+ format = GL_RGBA;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ /* GL_RGB is almost what we want here -- sampling 1 alpha when
+ * texturing, using 1 as destination alpha factor in blending,
+ * etc. However, when filtering with GL_CLAMP_TO_BORDER, the
+ * alpha channel of the border color will also be clamped to
+ * 1, when we actually want the border color we explicitly
+ * specified. So, we have to store RGBA, and fill the alpha
+ * channel with 1 when blending.
+ */
+ if (ctx->can_read_bgra)
+ format = GL_BGRA;
+ else
+ format = GL_RGBA;
+ break;
+ }
+
+ ctx->dispatch.TexImage2D (ctx->tex_target, 0, format,
+ width, height, 0,
+ format, GL_UNSIGNED_BYTE, NULL);
+
+ return &surface->base;
+}
+
+cairo_surface_t *
+_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ return _create_scratch_internal (ctx, content, width, height, FALSE);
+}
+
+cairo_surface_t *
+_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ return _create_scratch_internal (ctx, content, width, height, TRUE);
+}
+
+static cairo_status_t
+_cairo_gl_surface_clear (cairo_gl_surface_t *surface,
+ const cairo_color_t *color)
+{
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+ double r, g, b, a;
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ if (ctx->current_target == surface)
+ _cairo_gl_composite_flush (ctx);
+
+ /* FIXME: for glesv3 and glesv2 with ANGLE extension of multisample
+ supports, it is much more expensive to paint texture back to
+ multisample renderbuffer. Therefore, instead of clear
+ texture, we clear the renderbuffer.
+ In case, the next draw render target is texture, it will
+ blit renderbuffer back to texture */
+ if (ctx->gl_flavor != CAIRO_GL_FLAVOR_DESKTOP)
+ _cairo_gl_context_set_destination (ctx, surface, TRUE);
+ else
+ _cairo_gl_context_set_destination (ctx, surface, surface->msaa_active);
+ if (surface->base.content & CAIRO_CONTENT_COLOR) {
+ r = color->red * color->alpha;
+ g = color->green * color->alpha;
+ b = color->blue * color->alpha;
+ } else {
+ r = g = b = 0;
+ }
+ if (surface->base.content & CAIRO_CONTENT_ALPHA) {
+ a = color->alpha;
+ } else {
+ a = 1.0;
+ }
+
+ _disable_scissor_buffer (ctx);
+ if (ctx->states_cache.clear_red != r ||
+ ctx->states_cache.clear_green != g ||
+ ctx->states_cache.clear_blue != b ||
+ ctx->states_cache.clear_alpha != a) {
+
+ ctx->states_cache.clear_red = r;
+ ctx->states_cache.clear_green = g;
+ ctx->states_cache.clear_blue = b;
+ ctx->states_cache.clear_alpha = a;
+
+ ctx->dispatch.ClearColor (r, g, b, a);
+ }
+
+ /* optimize for mobile gl driver with deferred rendering */
+ if (surface->clip_on_stencil_buffer ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT);
+ else {
+ if (surface->clip_on_stencil_buffer) {
+ _cairo_clip_destroy(surface->clip_on_stencil_buffer);
+ surface->clip_on_stencil_buffer = NULL;
+ }
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ }
+
+ if (a == 0)
+ surface->base.is_clear = TRUE;
+
+ surface->content_changed = TRUE;
+ surface->content_synced = FALSE;
+ surface->content_cleared = TRUE;
+ return _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_create_and_clear_scratch (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_gl_surface_t *surface;
+ cairo_int_status_t status;
+
+ surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx, content, width, height);
+ if (unlikely (surface->base.status))
+ return &surface->base;
+
+ /* Cairo surfaces start out initialized to transparent (black) */
+ status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &surface->base;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create (cairo_device_t *abstract_device,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_gl_context_t *ctx;
+ cairo_gl_surface_t *surface;
+ cairo_status_t status;
+
+ if (! CAIRO_CONTENT_VALID (content))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+ if (abstract_device == NULL)
+ return _cairo_image_surface_create_with_content (content, width, height);
+
+ if (abstract_device->status)
+ return _cairo_surface_create_in_error (abstract_device->status);
+
+ if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+ status = _cairo_gl_context_acquire (abstract_device, &ctx);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height);
+ if (unlikely (surface->base.status)) {
+ status = _cairo_gl_context_release (ctx, surface->base.status);
+ cairo_surface_destroy (&surface->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &surface->base;
+}
+slim_hidden_def (cairo_gl_surface_create);
+
+/**
+ * cairo_gl_surface_create_for_texture:
+ * @content: type of content in the surface
+ * @tex: name of texture to use for storage of surface pixels
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a GL surface for the specified texture with the specified
+ * content and dimensions. The texture must be kept around until the
+ * #cairo_surface_t is destroyed or cairo_surface_finish() is called
+ * on the surface. The initial contents of @tex will be used as the
+ * initial image contents; you must explicitly clear the buffer,
+ * using, for example, cairo_rectangle() and cairo_fill() if you want
+ * it cleared. The format of @tex should be compatible with @content,
+ * in the sense that it must have the color components required by
+ * @content.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: TBD
+ **/
+cairo_surface_t *
+cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device,
+ cairo_content_t content,
+ unsigned int tex,
+ int width,
+ int height)
+{
+ cairo_gl_context_t *ctx;
+ cairo_gl_surface_t *surface;
+ cairo_status_t status;
+
+ if (! CAIRO_CONTENT_VALID (content))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+ if (abstract_device == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+
+ if (abstract_device->status)
+ return _cairo_surface_create_in_error (abstract_device->status);
+
+ if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH));
+
+ status = _cairo_gl_context_acquire (abstract_device, &ctx);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch_for_texture (ctx, content,
+ tex, width, height, TRUE);
+ status = _cairo_gl_context_release (ctx, status);
+
+ return &surface->base;
+}
+slim_hidden_def (cairo_gl_surface_create_for_texture);
+
+
+void
+cairo_gl_surface_set_size (cairo_surface_t *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_gl (abstract_surface) ||
+ _cairo_gl_surface_is_texture (surface)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (surface->width != width || surface->height != height) {
+ surface->size_changed = TRUE;
+ surface->width = width;
+ surface->height = height;
+ }
+}
+
+int
+cairo_gl_surface_get_width (cairo_surface_t *abstract_surface)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_gl (abstract_surface))
+ return 0;
+
+ return surface->width;
+}
+
+int
+cairo_gl_surface_get_height (cairo_surface_t *abstract_surface)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_gl (abstract_surface))
+ return 0;
+
+ return surface->height;
+}
+
+void
+cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_gl (abstract_surface)) {
+ _cairo_surface_set_error (abstract_surface,
+ CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return;
+ }
+
+ if (! _cairo_gl_surface_is_texture (surface)) {
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return;
+
+ /* And in any case we should flush any pending operations. */
+ _cairo_gl_composite_flush (ctx);
+
+ /* For swapping on EGL, at least, we need a valid context/target. */
+ _cairo_gl_context_set_destination (ctx, surface, FALSE);
+
+ ctx->swap_buffers (ctx, surface);
+
+ /* according to khronos specs on egl 1.4, stencil buffer is
+ * not preserved after eglSwapBuffers */
+ if (surface->clip_on_stencil_buffer) {
+ _cairo_clip_destroy (surface->clip_on_stencil_buffer);
+ surface->clip_on_stencil_buffer = NULL;
+ }
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (status)
+ status = _cairo_surface_set_error (abstract_surface, status);
+ }
+}
+
+static cairo_bool_t
+_cairo_gl_surface_size_valid (cairo_gl_surface_t *surface,
+ int width, int height)
+{
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
+ return width > 0 && height > 0 &&
+ width <= ctx->max_framebuffer_size &&
+ height <= ctx->max_framebuffer_size;
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_surface_t *surface = abstract_surface;
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+
+ if (! _cairo_gl_surface_size_valid (abstract_surface, width, height))
+ return _cairo_image_surface_create_with_content (content, width, height);
+
+ status = _cairo_gl_context_acquire (surface->device, &ctx);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height);
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return surface;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst,
+ cairo_gl_context_t *ctx,
+ int x, int y,
+ int width, int height)
+{
+ cairo_gl_composite_t setup;
+ cairo_status_t status;
+
+ _cairo_gl_composite_flush (ctx);
+ ctx->dispatch.ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+
+ status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE,
+ dst, FALSE);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_BLACK);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ _cairo_gl_context_emit_rect (ctx, x, y, x + width, y + height);
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ CLEANUP:
+ _cairo_gl_composite_fini (&setup);
+
+ _cairo_gl_composite_flush (ctx);
+ ctx->dispatch.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
+ cairo_image_surface_t *src,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y,
+ cairo_bool_t force_flush)
+{
+ GLenum internal_format, format, type;
+ cairo_bool_t has_alpha, needs_swap;
+ cairo_image_surface_t *clone = NULL;
+ cairo_gl_context_t *ctx;
+ int cpp;
+ cairo_image_surface_t *rgba_clone = NULL;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES2 ||
+ _cairo_gl_get_flavor (&ctx->dispatch) == CAIRO_GL_FLAVOR_ES3) {
+ pixman_format_code_t pixman_format;
+ cairo_surface_pattern_t pattern;
+ cairo_bool_t require_conversion = FALSE;
+ pixman_format = _cairo_is_little_endian () ? PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8;
+
+ if (src->base.content != CAIRO_CONTENT_ALPHA) {
+ if (src->pixman_format != pixman_format)
+ require_conversion = TRUE;
+ }
+ else if (dst->base.content != CAIRO_CONTENT_ALPHA)
+ require_conversion = TRUE;
+ else {
+ if (src->pixman_format == PIXMAN_a1) {
+ pixman_format = PIXMAN_a8;
+ require_conversion = TRUE;
+ }
+ }
+
+ if (require_conversion) {
+ rgba_clone = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ pixman_format,
+ src->width,
+ src->height,
+ 0);
+ if (unlikely (rgba_clone->base.status))
+ goto FAIL;
+
+ _cairo_pattern_init_for_surface (&pattern, &src->base);
+ status = _cairo_surface_paint (&rgba_clone->base,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status))
+ goto FAIL;
+
+ src = rgba_clone;
+ }
+ }
+
+ if (! _cairo_gl_get_image_format_and_type (ctx->gl_flavor,
+ src->pixman_format,
+ &internal_format,
+ &format,
+ &type,
+ &has_alpha,
+ &needs_swap))
+ {
+ cairo_bool_t is_supported;
+
+ clone = _cairo_image_surface_coerce (src);
+ if (unlikely (status = clone->base.status))
+ goto FAIL;
+
+ is_supported =
+ _cairo_gl_get_image_format_and_type (ctx->gl_flavor,
+ clone->pixman_format,
+ &internal_format,
+ &format,
+ &type,
+ &has_alpha,
+ &needs_swap);
+ assert (is_supported);
+ assert (!needs_swap);
+ src = clone;
+ }
+
+ cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
+
+ if (force_flush) {
+ status = _cairo_gl_surface_flush (&dst->base, 0);
+ if (unlikely (status))
+ goto FAIL;
+ }
+
+ if (_cairo_gl_surface_is_texture (dst)) {
+ void *data_start = src->data + src_y * src->stride + src_x * cpp;
+ void *data_start_gles2 = NULL;
+
+ /*
+ * Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the
+ * image data ourselves in some cases. In particular, we must extract
+ * the pixels if:
+ * a. we don't want full-length lines or
+ * b. the row stride cannot be handled by GL itself using a 4 byte
+ * alignment constraint
+ */
+ if (src->stride < 0 ||
+ (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ (src->width * cpp < src->stride - 3 ||
+ width != src->width)))
+ {
+ ctx->dispatch.PixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ status = _cairo_gl_surface_extract_image_data (src, src_x, src_y,
+ width, height,
+ &data_start_gles2);
+ if (unlikely (status))
+ goto FAIL;
+
+ data_start = data_start_gles2;
+ }
+ else
+ {
+ ctx->dispatch.PixelStorei (GL_UNPACK_ALIGNMENT, 4);
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3)
+ ctx->dispatch.PixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
+ }
+
+ /* we must resolve the renderbuffer to texture before we
+ upload image */
+ status = _cairo_gl_surface_resolve_multisampling (dst);
+ if (unlikely (status)) {
+ free (data_start_gles2);
+ goto FAIL;
+ }
+
+ _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+ ctx->dispatch.BindTexture (ctx->tex_target, dst->tex);
+ ctx->dispatch.TexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ ctx->dispatch.TexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ ctx->dispatch.TexSubImage2D (ctx->tex_target, 0,
+ dst_x, dst_y, width, height,
+ format, type, data_start);
+
+ free (data_start_gles2);
+
+ /* If we just treated some rgb-only data as rgba, then we have to
+ * go back and fix up the alpha channel where we filled in this
+ * texture data.
+ */
+ if (!has_alpha) {
+ _cairo_gl_surface_fill_alpha_channel (dst, ctx,
+ dst_x, dst_y,
+ width, height);
+
+ }
+ dst->content_synced = FALSE;
+ } else {
+ cairo_surface_t *tmp;
+
+ tmp = _cairo_gl_surface_create_scratch (ctx,
+ dst->base.content,
+ width, height);
+ if (unlikely (tmp->status))
+ goto FAIL;
+
+ status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
+ src,
+ src_x, src_y,
+ width, height,
+ 0, 0, force_flush);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ cairo_surface_pattern_t tmp_pattern;
+ cairo_rectangle_int_t r;
+ cairo_clip_t *clip;
+
+ _cairo_pattern_init_for_surface (&tmp_pattern, tmp);
+ cairo_matrix_init_translate (&tmp_pattern.base.matrix,
+ -dst_x, -dst_y);
+ tmp_pattern.base.filter = CAIRO_FILTER_NEAREST;
+ tmp_pattern.base.extend = CAIRO_EXTEND_NONE;
+
+ r.x = dst_x;
+ r.y = dst_y;
+ r.width = width;
+ r.height = height;
+ clip = _cairo_clip_intersect_rectangle (NULL, &r);
+ status = _cairo_surface_paint (&dst->base,
+ CAIRO_OPERATOR_SOURCE,
+ &tmp_pattern.base,
+ clip);
+ _cairo_clip_destroy (clip);
+ _cairo_pattern_fini (&tmp_pattern.base);
+ }
+
+ cairo_surface_destroy (tmp);
+ }
+
+FAIL:
+ status = _cairo_gl_context_release (ctx, status);
+
+ if (clone)
+ cairo_surface_destroy (&clone->base);
+
+ if (rgba_clone)
+ cairo_surface_destroy (&rgba_clone->base);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+ }
+
+ return status;
+}
+
+static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface)
+{
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
+ return ctx->gl_flavor;
+}
+
+static cairo_status_t
+_cairo_gl_surface_finish (void *abstract_surface)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_gl_context_t *ctx;
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE ||
+ ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_GAUSSIAN) &&
+ 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].type == CAIRO_GL_OPERAND_GAUSSIAN) &&
+ ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface)
+ _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
+ if (ctx->current_target == surface)
+ ctx->current_target = NULL;
+
+ if (surface->fb)
+ ctx->dispatch.DeleteFramebuffers (1, &surface->fb);
+ if (surface->depth_stencil)
+ ctx->dispatch.DeleteRenderbuffers (1, &surface->depth_stencil);
+ if (surface->owns_tex)
+ ctx->dispatch.DeleteTextures (1, &surface->tex);
+
+ if (surface->msaa_depth_stencil)
+ ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_depth_stencil);
+ if (surface->msaa_fb)
+ ctx->dispatch.DeleteFramebuffers (1, &surface->msaa_fb);
+ if (surface->msaa_rb)
+ ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb);
+
+ if (surface->image_node) {
+ surface->image_node->node.pinned = FALSE;
+ _cairo_rtree_node_remove (&ctx->image_cache->rtree,
+ &surface->image_node->node);
+ }
+
+ if (surface->clip_on_stencil_buffer)
+ _cairo_clip_destroy (surface->clip_on_stencil_buffer);
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_image_surface_t *
+_cairo_gl_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_image_surface_t *image;
+ cairo_gl_context_t *ctx;
+ GLenum format, type;
+ pixman_format_code_t pixman_format;
+ unsigned int cpp;
+ cairo_bool_t flipped, mesa_invert;
+ cairo_status_t status;
+ int y;
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status)) {
+ return _cairo_image_surface_create_in_error (status);
+ }
+
+ /* Want to use a switch statement here but the compiler gets whiny. */
+ if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
+ format = GL_BGRA;
+ pixman_format = PIXMAN_a8r8g8b8;
+ type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ cpp = 4;
+ } else if (surface->base.content == CAIRO_CONTENT_COLOR) {
+ format = GL_BGRA;
+ pixman_format = PIXMAN_x8r8g8b8;
+ type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ cpp = 4;
+ } else if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+ format = GL_ALPHA;
+ pixman_format = PIXMAN_a8;
+ type = GL_UNSIGNED_BYTE;
+ cpp = 1;
+ } else {
+ ASSERT_NOT_REACHED;
+ return NULL;
+ }
+
+ if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES2 ||
+ _cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES3) {
+ /* If only RGBA is supported, we must download data in a compatible
+ * format. This means that pixman will convert the data on the CPU when
+ * interacting with other image surfaces. For ALPHA, GLES2 does not
+ * support GL_PACK_ROW_LENGTH anyway, and this makes sure that the
+ * pixman image that is created has row_stride = row_width * bpp. */
+ if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->can_read_bgra) {
+ cairo_bool_t little_endian = _cairo_is_little_endian ();
+ format = GL_RGBA;
+
+ if (surface->base.content == CAIRO_CONTENT_COLOR) {
+ pixman_format = little_endian ?
+ PIXMAN_x8b8g8r8 : PIXMAN_r8g8b8x8;
+ } else {
+ pixman_format = little_endian ?
+ PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8;
+ }
+ }
+
+ /* GLES2 only supports GL_UNSIGNED_BYTE. */
+ type = GL_UNSIGNED_BYTE;
+ cpp = 4;
+ }
+
+ image = (cairo_image_surface_t*)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ pixman_format,
+ extents->width,
+ extents->height,
+ -1);
+ if (unlikely (image->base.status)) {
+ status = _cairo_gl_context_release (ctx, status);
+ return image;
+ }
+
+ cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y);
+
+ /* If the original surface has not been modified or
+ * is clear, we can avoid downloading data. */
+ if (surface->base.is_clear || surface->base.serial == 0) {
+ status = _cairo_gl_context_release (ctx, status);
+ return image;
+ }
+
+ /* This is inefficient, as we'd rather just read the thing without making
+ * it the destination. But then, this is the fallback path, so let's not
+ * fall back instead.
+ */
+ _cairo_gl_composite_flush (ctx);
+
+ _cairo_gl_context_set_destination (ctx, surface, FALSE);
+
+ flipped = ! _cairo_gl_surface_is_texture (surface);
+ mesa_invert = flipped && ctx->has_mesa_pack_invert;
+
+ ctx->dispatch.PixelStorei (GL_PACK_ALIGNMENT, 4);
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3)
+ ctx->dispatch.PixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
+ if (mesa_invert)
+ ctx->dispatch.PixelStorei (GL_PACK_INVERT_MESA, 1);
+
+ y = extents->y;
+ if (flipped)
+ y = surface->height - extents->y - extents->height;
+
+ ctx->dispatch.ReadPixels (extents->x, y,
+ extents->width, extents->height,
+ format, type, image->data);
+ if (mesa_invert)
+ ctx->dispatch.PixelStorei (GL_PACK_INVERT_MESA, 0);
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image->base);
+ return _cairo_image_surface_create_in_error (status);
+ }
+
+ /* We must invert the image manualy if we lack GL_MESA_pack_invert */
+ if (flipped && ! mesa_invert) {
+ uint8_t stack[1024], *row = stack;
+ uint8_t *top = image->data;
+ uint8_t *bot = image->data + (image->height-1)*image->stride;
+
+ if (image->stride > (int)sizeof(stack)) {
+ row = malloc (image->stride);
+ if (unlikely (row == NULL)) {
+ cairo_surface_destroy (&image->base);
+ return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ while (top < bot) {
+ memcpy (row, top, image->stride);
+ memcpy (top, bot, image->stride);
+ memcpy (bot, row, image->stride);
+ top += image->stride;
+ bot -= image->stride;
+ }
+
+ if (row != stack)
+ free(row);
+ }
+
+ image->base.is_clear = FALSE;
+
+ return image;
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ if (extents) {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+static cairo_status_t
+_cairo_gl_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_rectangle_int_t extents;
+
+ *image_extra = NULL;
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ *image_out = (cairo_image_surface_t *)
+ _cairo_gl_surface_map_to_image (surface, &extents);
+ return (*image_out)->base.status;
+}
+
+static void
+_cairo_gl_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_int_status_t status;
+
+ status = _cairo_gl_surface_draw_image (abstract_surface, image,
+ 0, 0,
+ image->width, image->height,
+ image->base.device_transform_inverse.x0,
+ image->base.device_transform_inverse.y0,
+ TRUE);
+
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return status;
+}
+
+static cairo_bool_t
+_cairo_gl_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_gl_surface_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_gl_context_t *ctx;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ if (((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE ||
+ ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_GAUSSIAN) &&
+ 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].type == CAIRO_GL_OPERAND_GAUSSIAN) &&
+ ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) ||
+ (ctx->current_target == surface))
+ _cairo_gl_composite_flush (ctx);
+
+ status = _cairo_gl_surface_resolve_multisampling (surface);
+
+ if (ctx->msaa_type != CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE)
+ ctx->dispatch.Flush ();
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+cairo_int_status_t
+_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface)
+{
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+
+ if (surface->base.device == NULL)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ if (! surface->content_cleared) {
+ /* GLES surfaces do not need explicit resolution. */
+ if (! surface->msaa_active)
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ !((cairo_gl_context_t *) surface->base.device)->has_angle_multisampling)
+ return CAIRO_INT_STATUS_SUCCESS;
+ else if (! _cairo_gl_surface_is_texture (surface))
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ _cairo_gl_context_set_destination (ctx, surface, FALSE);
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ surface->content_cleared = FALSE;
+
+ return status;
+}
+
+static const cairo_compositor_t *
+get_compositor (cairo_gl_surface_t *surface)
+{
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
+ return ctx->compositor;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_paint (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+
+ status = cairo_device_acquire (dst->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_paint (surface, op, source, clip,
+ &source->shadow);
+ ctx->source_scratch_in_use = FALSE;
+ if (unlikely (status)) {
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ if (source->shadow.draw_shadow_only) {
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+#if 0 // Currently glClear does not get flushed to GPU so do not use this fast path for the time being
+ /* simplify the common case of clearing the surface */
+ if (clip == NULL) {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+ else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) {
+ status = _cairo_gl_surface_clear (surface,
+ &((cairo_solid_pattern_t *) source)->color);
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+ }
+#endif
+
+ status = _cairo_compositor_paint (get_compositor (surface), surface,
+ op, source, clip);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_mask (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *) surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+
+ status = cairo_device_acquire (dst->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_mask (surface, op, source, mask, clip,
+ &source->shadow);
+ ctx->source_scratch_in_use = FALSE;
+ if (unlikely (status)) {
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ if (source->shadow.draw_shadow_only) {
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ status = _cairo_compositor_mask (get_compositor (surface), surface,
+ op, source, mask, clip);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ status = _cairo_compositor_mask (get_compositor (surface), surface,
+ op, source, mask, clip);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_stroke (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ status = cairo_device_acquire (dst->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (surface, op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip, &source->shadow);
+
+ ctx->source_scratch_in_use = FALSE;
+ if (unlikely (status)) {
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_stroke (get_compositor (surface), surface,
+ op, source, path, style,
+ ctm, ctm_inverse, tolerance,
+ antialias, clip);
+ if (unlikely (status)) {
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (surface, op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip, &source->shadow);
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_fill (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t*path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ status = cairo_device_acquire (dst->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (surface, op, source, path,
+ fill_rule, tolerance, antialias,
+ clip, &source->shadow);
+
+ ctx->source_scratch_in_use = FALSE;
+ if (unlikely (status)) {
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+
+ if (! source->shadow.draw_shadow_only) {
+ if (! source->shadow.path_is_fill_with_spread ||
+ source->shadow.type != CAIRO_SHADOW_INSET)
+ status = _cairo_compositor_fill (get_compositor (surface),
+ surface,
+ op, source, path,
+ fill_rule, tolerance,
+ antialias,
+ clip);
+ else
+ status = _cairo_compositor_paint (get_compositor (surface),
+ surface, op, source,
+ clip);
+ }
+
+ if (unlikely (status)) {
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (surface, op, source, path,
+ fill_rule, tolerance, antialias,
+ clip, &source->shadow);
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *font,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_gl_surface_t *dst = (cairo_gl_surface_t *)surface;
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *)dst->base.device;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ status = cairo_device_acquire (dst->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (surface, op, source,
+ font,
+ glyphs, num_glyphs,
+ clip, &source->shadow);
+
+ ctx->source_scratch_in_use = FALSE;
+ if (unlikely (status)) {
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ dst->content_changed = TRUE;
+ dst->content_synced = FALSE;
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_glyphs (get_compositor (surface), surface,
+ op, source, glyphs, num_glyphs,
+ font, clip);
+
+ if (unlikely (status)) {
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+ }
+
+ ctx->source_scratch_in_use = FALSE;
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (surface, op, source,
+ font,
+ glyphs, num_glyphs,
+ clip, &source->shadow);
+
+ ctx->source_scratch_in_use = FALSE;
+ cairo_device_release (dst->base.device);
+ return status;
+}
+
+static const cairo_surface_backend_t _cairo_gl_surface_backend = {
+ CAIRO_SURFACE_TYPE_GL,
+ _cairo_gl_surface_finish,
+ _cairo_default_context_create,
+
+ _cairo_gl_surface_create_similar,
+ NULL, /* similar image */
+ _cairo_gl_surface_map_to_image,
+ _cairo_gl_surface_unmap_image,
+
+ _cairo_gl_surface_source,
+ _cairo_gl_surface_acquire_source_image,
+ _cairo_gl_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_gl_surface_get_extents,
+ _cairo_image_surface_get_font_options,
+
+ _cairo_gl_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_gl_surface_paint,
+ _cairo_gl_surface_mask,
+ _cairo_gl_surface_stroke,
+ _cairo_gl_surface_fill,
+ NULL, /* fill/stroke */
+ _cairo_gl_surface_glyphs,
+ NULL, /* has_text_glyphs */
+ NULL, /* show_text_glyphs */
+ NULL, /* get_supported_mime_types */
+ _cairo_gl_surface_shadow_surface,
+ _cairo_gl_surface_glyph_shadow_surface,
+ _cairo_gl_surface_shadow_mask_surface,
+ _cairo_gl_surface_glyph_shadow_mask_surface,
+};
+
+cairo_status_t
+cairo_gl_surface_set_binding_texture (cairo_surface_t *abstract_surface,
+ unsigned int texture)
+{
+ cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+ if ((cairo_surface_get_type (&surface->base) != CAIRO_SURFACE_TYPE_GL) ||
+ surface->tex)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ surface->bounded_tex = texture;
+ surface->operand.texture.tex = texture;
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-gl-traps-compositor.c b/src/cairo-gl-traps-compositor.c
new file mode 100755
index 000000000..022a37d77
--- /dev/null
+++ b/src/cairo-gl-traps-compositor.c
@@ -0,0 +1,664 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-offset-private.h"
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+ cairo_region_t *region)
+{
+ cairo_gl_surface_t *surface = _surface;
+
+ surface->clip_region = region;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy)
+{
+ cairo_gl_surface_t *dst = _dst;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ if (_cairo_gl_surface_is_texture (dst))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+ int x = _cairo_fixed_integer_part (b->p1.x);
+ int y = _cairo_fixed_integer_part (b->p1.y);
+ int w = _cairo_fixed_integer_part (b->p2.x) - x;
+ int h = _cairo_fixed_integer_part (b->p2.y) - y;
+ cairo_status_t status;
+
+ status = _cairo_gl_surface_draw_image (dst, image,
+ x + dx, y + dy,
+ w, h,
+ x, y,
+ TRUE);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+emit_aligned_boxes (cairo_gl_context_t *ctx,
+ const cairo_boxes_t *boxes)
+{
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx);
+ int i;
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+ emit (ctx, x1, y1, x2, y2);
+ }
+ }
+}
+
+static cairo_int_status_t
+fill_boxes (void *_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+
+ status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_solid_source (&setup, color);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ emit_aligned_boxes (ctx, boxes);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+ return status;
+}
+
+static cairo_int_status_t
+composite_boxes (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+
+ status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_source_operand (&setup,
+ source_to_operand (abstract_src));
+ _cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y);
+
+ _cairo_gl_composite_set_mask_operand (&setup,
+ source_to_operand (abstract_mask));
+ _cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ emit_aligned_boxes (ctx, boxes);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+ return status;
+}
+
+static cairo_int_status_t
+composite (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+ cairo_gl_operand_t *temp_operand = NULL;
+
+ status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ temp_operand = source_to_operand (abstract_src);
+ if (temp_operand == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto FAIL;
+ }
+
+ _cairo_gl_composite_set_source_operand (&setup, temp_operand);
+ _cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y);
+
+ _cairo_gl_composite_set_mask_operand (&setup,
+ source_to_operand (abstract_mask));
+ _cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ /* XXX clip */
+ _cairo_gl_context_emit_rect (ctx, dst_x, dst_y, dst_x+width, dst_y+height);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+ return status;
+}
+
+static cairo_int_status_t
+lerp (void *dst,
+ cairo_surface_t *src,
+ cairo_surface_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_int_status_t status;
+
+ /* we could avoid some repetition... */
+ status = composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ mask_x, mask_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+
+ if (unlikely (status))
+ return status;
+
+ status = composite (dst, CAIRO_OPERATOR_ADD, src, mask,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp_color_glyph (void *dst,
+ cairo_surface_t *src,
+ cairo_surface_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_int_status_t status;
+
+ /*we could avoid some repetition... */
+ status = composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, src,
+ mask_x, mask_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+
+ if (unlikely (status))
+ return status;
+
+ status = composite (dst, CAIRO_OPERATOR_ADD, src, mask,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+traps_to_operand (void *_dst,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps,
+ cairo_gl_operand_t *operand,
+ int dst_x, int dst_y)
+{
+ pixman_format_code_t pixman_format;
+ pixman_image_t *pixman_image;
+ cairo_surface_t *image, *mask;
+ cairo_surface_pattern_t pattern;
+ cairo_status_t status;
+
+ pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1;
+ pixman_image = pixman_image_create_bits (pixman_format,
+ extents->width,
+ extents->height,
+ NULL, 0);
+ if (unlikely (pixman_image == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _pixman_image_add_traps (pixman_image, extents->x, extents->y, traps);
+ image = _cairo_image_surface_create_for_pixman_image (pixman_image,
+ pixman_format);
+ if (unlikely (image->status)) {
+ pixman_image_unref (pixman_image);
+ status = image->status;
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ /* GLES2 only supports RGB/RGBA when uploading */
+#if 0
+ if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES2) {
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *rgba_image;
+
+ /* XXX perform this fixup inside _cairo_gl_draw_image() */
+
+ rgba_image =
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ _cairo_is_little_endian () ? PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8,
+ extents->width,
+ extents->height,
+ 0);
+ if (unlikely (rgba_image->status)) {
+ cairo_surface_destroy (image);
+ return rgba_image->status;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, image);
+ status = _cairo_surface_paint (rgba_image, CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ cairo_surface_destroy (image);
+ image = rgba_image;
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+ }
+#endif
+
+ mask = _cairo_surface_create_similar_scratch (_dst,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ extents->width,
+ extents->height);
+ if (unlikely (mask->status)) {
+ cairo_surface_destroy (image);
+ return mask->status;
+ }
+
+ status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask,
+ (cairo_image_surface_t *)image,
+ 0, 0,
+ extents->width, extents->height,
+ 0, 0,
+ TRUE);
+ cairo_surface_destroy (image);
+
+ if (unlikely (status))
+ goto error;
+
+ _cairo_pattern_init_for_surface (&pattern, mask);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -extents->x+dst_x, -extents->y+dst_y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ pattern.base.extend = CAIRO_EXTEND_NONE;
+ status = _cairo_gl_operand_init (operand, &pattern.base, _dst,
+ &_cairo_unbounded_rectangle,
+ &_cairo_unbounded_rectangle,
+ FALSE, FALSE);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status))
+ goto error;
+
+ operand->texture.owns_surface = mask;
+ return CAIRO_STATUS_SUCCESS;
+
+error:
+ cairo_surface_destroy (mask);
+ return status;
+}
+
+static cairo_int_status_t
+composite_traps (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_int_status_t status;
+ cairo_surface_t *src = (cairo_surface_t *) abstract_src;
+ cairo_surface_t *dst = (cairo_surface_t *) _dst;
+ cairo_surface_t *new_surface = NULL;
+ cairo_surface_t *new_src = (cairo_surface_t *) abstract_src;
+ int new_src_x, new_src_y;
+ cairo_surface_pattern_t new_surface_pattern;
+
+ if (dst == src) {
+ new_surface = _cairo_surface_create_similar_scratch (src,
+ src->content,
+ extents->width,
+ extents->height);
+ status = new_surface->status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (new_surface);
+ return status;
+ }
+
+ _cairo_pattern_init_for_surface (&new_surface_pattern, src);
+ new_surface_pattern.base.extend = CAIRO_EXTEND_NONE;
+ new_surface_pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (new_surface, CAIRO_OPERATOR_SOURCE,
+ &new_surface_pattern.base, NULL);
+
+ if (unlikely (status)) {
+ _cairo_pattern_fini (&new_surface_pattern.base);
+ cairo_surface_destroy (new_surface);
+ return status;
+ }
+
+ new_src = _cairo_gl_pattern_to_source (dst,
+ &new_surface_pattern.base,
+ FALSE,
+ extents, extents,
+ &new_src_x, &new_src_y);
+ }
+
+ status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_source_operand (&setup,
+ source_to_operand (new_src));
+
+ if (src == new_src)
+ _cairo_gl_operand_translate (&setup.src, -src_x-dst_x, -src_y-dst_y);
+ status = traps_to_operand (_dst, extents, antialias, traps, &setup.mask, dst_x, dst_y);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ /* XXX clip */
+ _cairo_gl_context_emit_rect (ctx,
+ extents->x-dst_x, extents->y-dst_y,
+ extents->x-dst_x+extents->width,
+ extents->y-dst_y+extents->height);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+
+ if (new_src != src) {
+ cairo_surface_destroy (new_src);
+ cairo_surface_destroy (new_surface);
+ _cairo_pattern_fini (&new_surface_pattern.base);
+ }
+
+ return status;
+}
+
+static cairo_gl_surface_t *
+tristrip_to_surface (void *_dst,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_tristrip_t *strip)
+{
+ pixman_format_code_t pixman_format;
+ pixman_image_t *pixman_image;
+ cairo_surface_t *image, *mask;
+ cairo_status_t status;
+
+ pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
+ pixman_image = pixman_image_create_bits (pixman_format,
+ extents->width,
+ extents->height,
+ NULL, 0);
+ if (unlikely (pixman_image == NULL))
+ return (cairo_gl_surface_t *)_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _pixman_image_add_tristrip (pixman_image, extents->x, extents->y, strip);
+ image = _cairo_image_surface_create_for_pixman_image (pixman_image,
+ pixman_format);
+ if (unlikely (image->status)) {
+ pixman_image_unref (pixman_image);
+ return (cairo_gl_surface_t *)image;
+ }
+
+ mask = _cairo_surface_create_similar_scratch (_dst,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ extents->width,
+ extents->height);
+ if (unlikely (mask->status)) {
+ cairo_surface_destroy (image);
+ return (cairo_gl_surface_t *)mask;
+ }
+
+ status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask,
+ (cairo_image_surface_t *)image,
+ 0, 0,
+ extents->width, extents->height,
+ 0, 0,
+ TRUE);
+ cairo_surface_destroy (image);
+ if (unlikely (status)) {
+ cairo_surface_destroy (mask);
+ return (cairo_gl_surface_t*)_cairo_surface_create_in_error (status);
+ }
+
+ return (cairo_gl_surface_t*)mask;
+}
+
+static cairo_int_status_t
+composite_tristrip (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_tristrip_t *strip)
+{
+ cairo_gl_composite_t setup;
+ cairo_gl_context_t *ctx;
+ cairo_gl_surface_t *mask;
+ cairo_int_status_t status;
+ cairo_gl_operand_t *temp_operand = NULL;
+
+ mask = tristrip_to_surface (_dst, extents, antialias, strip);
+ if (unlikely (mask->base.status)) {
+ status = mask->base.status;
+ cairo_surface_destroy (&mask->base);
+ return status;
+ }
+
+ status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
+ if (unlikely (status))
+ goto FAIL;
+
+ temp_operand = source_to_operand (abstract_src);
+ if (temp_operand == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto FAIL;
+ }
+ _cairo_gl_composite_set_source_operand (&setup,
+ temp_operand);
+
+ //_cairo_gl_composite_set_mask_surface (&setup, mask, 0, 0);
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ /* XXX clip */
+ _cairo_gl_context_emit_rect (ctx,
+ dst_x, dst_y,
+ dst_x+extents->width,
+ dst_y+extents->height);
+ status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+ _cairo_gl_composite_fini (&setup);
+ cairo_surface_destroy (&mask->base);
+ return status;
+}
+
+static cairo_int_status_t
+check_composite (const cairo_composite_rectangles_t *extents)
+{
+ if (! _cairo_gl_operator_is_supported (extents->op))
+ return UNSUPPORTED ("unsupported operator");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_compositor_t *
+_cairo_gl_traps_compositor_get (void)
+{
+ static cairo_traps_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_traps_compositor_init (&compositor, &_cairo_fallback_compositor);
+ compositor.acquire = acquire;
+ compositor.release = release;
+ compositor.set_clip_region = set_clip_region;
+ compositor.pattern_to_surface = _cairo_gl_pattern_to_source;
+ compositor.draw_image_boxes = draw_image_boxes;
+ //compositor.copy_boxes = copy_boxes;
+ compositor.fill_boxes = fill_boxes;
+ compositor.check_composite = check_composite;
+ compositor.composite = composite;
+ compositor.lerp = lerp;
+ //FIXME:
+ compositor.lerp_color_glyph = lerp_color_glyph;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ //compositor.check_composite_traps = check_composite_traps;
+ compositor.composite_traps = composite_traps;
+ //compositor.check_composite_tristrip = check_composite_traps;
+ compositor.composite_tristrip = composite_tristrip;
+ compositor.check_composite_glyphs = _cairo_gl_check_composite_glyphs;
+ compositor.composite_glyphs = _cairo_gl_composite_glyphs;
+ }
+
+ return &compositor.base;
+}
diff --git a/src/cairo-gl.h b/src/cairo-gl.h
new file mode 100755
index 000000000..69738a265
--- /dev/null
+++ b/src/cairo-gl.h
@@ -0,0 +1,158 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Eric Anholt.
+ */
+
+/*
+ * cairo-gl.h:
+ *
+ * The cairo-gl backend provides an implementation of possibly
+ * hardware-accelerated cairo rendering by targeting the OpenGL API.
+ * The goal of the cairo-gl backend is to provide better performance
+ * with equal functionality to cairo-image where possible. It does
+ * not directly provide for applying additional OpenGL effects to
+ * cairo surfaces.
+ *
+ * Cairo-gl allows interoperability with other GL rendering through GL
+ * context sharing. Cairo-gl surfaces are created in reference to a
+ * #cairo_device_t, which represents a GL context created by the user.
+ * When that GL context is created with its sharePtr set to another
+ * context (or vice versa), its objects (textures backing cairo-gl
+ * surfaces) can be accessed in the other OpenGL context. This allows
+ * cairo-gl to maintain its drawing state in one context while the
+ * user's 3D rendering occurs in the user's other context.
+ *
+ * However, as only one context can be current to a thread at a time,
+ * cairo-gl may make its context current to the thread on any cairo
+ * call which interacts with a cairo-gl surface or the cairo-gl
+ * device. As a result, the user must make their own context current
+ * between any cairo calls and their own OpenGL rendering.
+ **/
+
+#ifndef CAIRO_GL_H
+#define CAIRO_GL_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create (cairo_device_t *device,
+ cairo_content_t content,
+ int width, int height);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device,
+ cairo_content_t content,
+ unsigned int tex,
+ int width, int height);
+cairo_public void
+cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height);
+
+cairo_public int
+cairo_gl_surface_get_width (cairo_surface_t *abstract_surface);
+
+cairo_public int
+cairo_gl_surface_get_height (cairo_surface_t *abstract_surface);
+
+cairo_public void
+cairo_gl_surface_swapbuffers (cairo_surface_t *surface);
+
+cairo_public void
+cairo_gl_device_set_thread_aware (cairo_device_t *device,
+ cairo_bool_t thread_aware);
+
+cairo_public cairo_status_t
+cairo_gl_surface_set_binding_texture (cairo_surface_t *abstract_surface,
+ unsigned int texture);
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+#include <GL/glx.h>
+
+cairo_public cairo_device_t *
+cairo_glx_device_create (Display *dpy, GLXContext gl_ctx);
+
+cairo_public Display *
+cairo_glx_device_get_display (cairo_device_t *device);
+
+cairo_public GLXContext
+cairo_glx_device_get_context (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_window (cairo_device_t *device,
+ Window win,
+ int width, int height);
+#endif
+
+#if CAIRO_HAS_WGL_FUNCTIONS
+#include <windows.h>
+
+cairo_public cairo_device_t *
+cairo_wgl_device_create (HGLRC rc);
+
+cairo_public HGLRC
+cairo_wgl_device_get_context (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_dc (cairo_device_t *device,
+ HDC dc,
+ int width,
+ int height);
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+#include <EGL/egl.h>
+
+cairo_public cairo_device_t *
+cairo_egl_device_create (EGLDisplay dpy, EGLContext egl);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_egl (cairo_device_t *device,
+ EGLSurface egl,
+ int width,
+ int height);
+
+cairo_public EGLDisplay
+cairo_egl_device_get_display (cairo_device_t *device);
+
+cairo_public EGLSurface
+cairo_egl_device_get_context (cairo_device_t *device);
+
+#endif
+
+CAIRO_END_DECLS
+#else /* CAIRO_HAS_GL_SURFACE */
+# error Cairo was not compiled with support for the GL backend
+#endif /* CAIRO_HAS_GL_SURFACE */
+
+#endif /* CAIRO_GL_H */
diff --git a/src/cairo-glx-context.c b/src/cairo-glx-context.c
new file mode 100755
index 000000000..b4d706007
--- /dev/null
+++ b/src/cairo-glx-context.c
@@ -0,0 +1,372 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-error-private.h"
+
+#include <X11/Xutil.h>
+
+/* XXX needs hooking into XCloseDisplay() */
+
+typedef struct _cairo_glx_context {
+ cairo_gl_context_t base;
+
+ Display *display;
+ Window dummy_window;
+ GLXContext context;
+ GLXDrawable current_drawable;
+
+ GLXContext previous_context;
+
+ cairo_bool_t has_multithread_makecurrent;
+} cairo_glx_context_t;
+
+typedef struct _cairo_glx_surface {
+ cairo_gl_surface_t base;
+
+ Window win;
+} cairo_glx_surface_t;
+
+static cairo_bool_t
+_context_acquisition_changed_glx_state (cairo_glx_context_t *ctx)
+{
+ return ctx->previous_context != ctx->context;
+}
+
+static GLXDrawable
+_glx_get_current_drawable (cairo_glx_context_t *ctx)
+{
+ if (ctx->base.current_target == NULL ||
+ _cairo_gl_surface_is_texture (ctx->base.current_target)) {
+ return ctx->dummy_window;
+ }
+
+ return ((cairo_glx_surface_t *) ctx->base.current_target)->win;
+}
+
+static void *
+_glx_query_current_state (cairo_glx_context_t * ctx)
+{
+ ctx->previous_context = glXGetCurrentContext ();
+}
+
+static void
+_glx_acquire (void *abstract_ctx)
+{
+ cairo_glx_context_t *ctx = abstract_ctx;
+ GLXDrawable current_drawable = _glx_get_current_drawable (ctx);
+
+ _glx_query_current_state (ctx);
+ if (!_context_acquisition_changed_glx_state (ctx))
+ return;
+
+ _cairo_gl_context_reset (&ctx->base);
+ glXMakeCurrent (ctx->display, current_drawable, ctx->context);
+ ctx->current_drawable = current_drawable;
+}
+
+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. */
+ if (ctx->current_drawable != surface->win) {
+ glXMakeCurrent (ctx->display, surface->win, ctx->context);
+ ctx->current_drawable = surface->win;
+ }
+}
+
+static void
+_glx_release (void *abstract_ctx)
+{
+ cairo_glx_context_t *ctx = abstract_ctx;
+
+ if (ctx->has_multithread_makecurrent || !ctx->base.thread_aware ||
+ !_context_acquisition_changed_glx_state (ctx)) {
+ return;
+ }
+
+ glXMakeCurrent (ctx->display, None, None);
+ ctx->current_drawable = None;
+}
+
+static void
+_glx_swap_buffers (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{
+ cairo_glx_context_t *ctx = abstract_ctx;
+ cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
+
+ glXSwapBuffers (ctx->display, surface->win);
+}
+
+static void
+_glx_destroy (void *abstract_ctx)
+{
+ cairo_glx_context_t *ctx = abstract_ctx;
+
+ if (ctx->dummy_window != None)
+ XDestroyWindow (ctx->display, ctx->dummy_window);
+
+ glXMakeCurrent (ctx->display, None, None);
+}
+
+static cairo_status_t
+_glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy)
+{
+ int attr[3] = { GLX_FBCONFIG_ID, 0, None };
+ GLXFBConfig *config;
+ XVisualInfo *vi;
+ Colormap cmap;
+ XSetWindowAttributes swa;
+ Window win = None;
+ int cnt;
+
+ /* Create a dummy window created for the target GLX context that we can
+ * use to query the available GL/GLX extensions.
+ */
+ glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]);
+
+ cnt = 0;
+ config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt);
+ if (unlikely (cnt == 0))
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+
+ vi = glXGetVisualFromFBConfig (dpy, config[0]);
+ XFree (config);
+
+ if (unlikely (vi == NULL))
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+
+ cmap = XCreateColormap (dpy,
+ RootWindow (dpy, vi->screen),
+ vi->visual,
+ AllocNone);
+ swa.colormap = cmap;
+ swa.border_pixel = 0;
+ win = XCreateWindow (dpy, RootWindow (dpy, vi->screen),
+ -1, -1, 1, 1, 0,
+ vi->depth,
+ InputOutput,
+ vi->visual,
+ CWBorderPixel | CWColormap, &swa);
+ XFreeColormap (dpy, cmap);
+ XFree (vi);
+
+ XFlush (dpy);
+ if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) {
+ XDestroyWindow (dpy, win);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ *dummy = win;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_gl_generic_func_t
+_cairo_glx_get_proc_address (void *data, const char *name)
+{
+ int i;
+ struct {
+ cairo_gl_generic_func_t func;
+ const char *name;
+ } func_map[] = {
+ { (cairo_gl_generic_func_t)glActiveTexture, "glActiveTexture" },
+ { (cairo_gl_generic_func_t)glBindTexture, "glBindTexture" },
+ { (cairo_gl_generic_func_t)glBlendFunc, "glBlendFunc" },
+ { (cairo_gl_generic_func_t)glBlendFuncSeparate,"glBlendFuncSeparate"},
+ { (cairo_gl_generic_func_t)glClear, "glClear" },
+ { (cairo_gl_generic_func_t)glClearColor, "glClearColor" },
+ { (cairo_gl_generic_func_t)glClearStencil, "glClearStencil" },
+ { (cairo_gl_generic_func_t)glColorMask, "glColorMask" },
+ { (cairo_gl_generic_func_t)glDeleteTextures,"glDeleteTextures" },
+ { (cairo_gl_generic_func_t)glDepthMask, "glDepthMask" },
+ { (cairo_gl_generic_func_t)glDisable, "glDisable" },
+ { (cairo_gl_generic_func_t)glDrawArrays, "glDrawArrays" },
+ { (cairo_gl_generic_func_t)glDrawBuffer, "glDrawBuffer" },
+ { (cairo_gl_generic_func_t)glDrawElements, "glDrawElements" },
+ { (cairo_gl_generic_func_t)glEnable, "glEnable" },
+ { (cairo_gl_generic_func_t)glGenTextures, "glGenTextures" },
+ { (cairo_gl_generic_func_t)glGetBooleanv, "glGetBooleanv" },
+ { (cairo_gl_generic_func_t)glGetError, "glGetError" },
+ { (cairo_gl_generic_func_t)glGetFloatv, "glGetFloatv" },
+ { (cairo_gl_generic_func_t)glGetIntegerv, "glGetIntegerv" },
+ { (cairo_gl_generic_func_t)glGetString, "glGetString" },
+ { (cairo_gl_generic_func_t)glFlush, "glFlush" },
+ { (cairo_gl_generic_func_t)glPixelStorei, "glPixelStorei" },
+ { (cairo_gl_generic_func_t)glReadBuffer, "glReadBuffer" },
+ { (cairo_gl_generic_func_t)glReadPixels, "glReadPixels" },
+ { (cairo_gl_generic_func_t)glScissor, "glScissor" },
+ { (cairo_gl_generic_func_t)glStencilFunc, "glStencilFunc" },
+ { (cairo_gl_generic_func_t)glStencilMask, "glStencilMask" },
+ { (cairo_gl_generic_func_t)glStencilOp, "glStencilOp" },
+ { (cairo_gl_generic_func_t)glTexImage2D, "glTexImage2D" },
+ { (cairo_gl_generic_func_t)glTexSubImage2D, "glTexSubImage2D" },
+ { (cairo_gl_generic_func_t)glTexParameteri, "glTexParameteri" },
+ { (cairo_gl_generic_func_t)glViewport, "glViewport" },
+ { NULL, NULL }
+ };
+
+ for (i = 0; func_map[i].name; i++) {
+ if (! strcmp (func_map[i].name, name))
+ return func_map[i].func;
+ }
+
+ return glXGetProcAddress (name);
+}
+
+cairo_device_t *
+cairo_glx_device_create (Display *dpy, GLXContext gl_ctx)
+{
+ cairo_glx_context_t *ctx;
+ cairo_status_t status;
+ Window dummy = None;
+ const char *glx_extensions;
+
+ ctx = calloc (1, sizeof (cairo_glx_context_t));
+ if (unlikely (ctx == NULL))
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* glx_dummy_window will call glXMakeCurrent, so we need to
+ * query the current state of the context now. */
+ _glx_query_current_state (ctx);
+
+ status = _glx_dummy_window (dpy, gl_ctx, &dummy);
+ if (unlikely (status)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ ctx->current_drawable = dummy;
+
+ ctx->display = dpy;
+ ctx->dummy_window = dummy;
+ ctx->context = gl_ctx;
+
+ ctx->base.acquire = _glx_acquire;
+ ctx->base.release = _glx_release;
+ ctx->base.make_current = _glx_make_current;
+ ctx->base.swap_buffers = _glx_swap_buffers;
+ ctx->base.destroy = _glx_destroy;
+
+ status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
+ (cairo_gl_get_proc_addr_func_t) _cairo_glx_get_proc_address,
+ NULL);
+ if (unlikely (status)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ status = _cairo_gl_context_init (&ctx->base);
+ if (unlikely (status)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy));
+ if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) {
+ ctx->has_multithread_makecurrent = TRUE;
+ }
+
+ ctx->base.release (ctx);
+
+ return &ctx->base.base;
+}
+
+Display *
+cairo_glx_device_get_display (cairo_device_t *device)
+{
+ cairo_glx_context_t *ctx;
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ ctx = (cairo_glx_context_t *) device;
+
+ return ctx->display;
+}
+
+GLXContext
+cairo_glx_device_get_context (cairo_device_t *device)
+{
+ cairo_glx_context_t *ctx;
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ ctx = (cairo_glx_context_t *) device;
+
+ return ctx->context;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_window (cairo_device_t *device,
+ Window win,
+ int width,
+ int height)
+{
+ cairo_glx_surface_t *surface;
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+ if (width <= 0 || height <= 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ surface = calloc (1, sizeof (cairo_glx_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_gl_surface_init (device, &surface->base,
+ CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ surface->win = win;
+
+ return &surface->base.base;
+}
diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h
new file mode 100755
index 000000000..92d3bb3c3
--- /dev/null
+++ b/src/cairo-gstate-private.h
@@ -0,0 +1,415 @@
+/* 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 opacity;
+ double tolerance;
+ cairo_antialias_t antialias;
+
+ cairo_stroke_style_t stroke_style;
+
+ cairo_fill_rule_t fill_rule;
+
+ cairo_font_face_t *font_face;
+ cairo_scaled_font_t *scaled_font; /* Specific to the current CTM */
+ cairo_scaled_font_t *previous_scaled_font; /* holdover */
+ cairo_matrix_t font_matrix;
+ cairo_font_options_t font_options;
+
+ cairo_clip_t *clip;
+
+ cairo_surface_t *target; /* The target to which all rendering is directed */
+ cairo_surface_t *parent_target; /* The previous target which was receiving rendering */
+ cairo_surface_t *original_target; /* The original target the initial gstate was created with */
+
+ /* the user is allowed to update the device after we have cached the matrices... */
+ cairo_observer_t device_transform_observer;
+
+ cairo_matrix_t ctm;
+ cairo_matrix_t ctm_inverse;
+ cairo_matrix_t source_ctm_inverse; /* At the time ->source was set */
+ cairo_bool_t is_identity;
+
+ cairo_pattern_t *source;
+
+ cairo_shadow_t shadow;
+
+ struct _cairo_gstate *next;
+};
+
+/* cairo-gstate.c */
+cairo_private cairo_status_t
+_cairo_gstate_init (cairo_gstate_t *gstate,
+ cairo_surface_t *target);
+
+cairo_private void
+_cairo_gstate_fini (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist);
+
+cairo_private cairo_status_t
+_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist);
+
+cairo_private cairo_bool_t
+_cairo_gstate_is_group (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child);
+
+cairo_private cairo_surface_t *
+_cairo_gstate_get_target (cairo_gstate_t *gstate);
+
+cairo_private cairo_surface_t *
+_cairo_gstate_get_original_target (cairo_gstate_t *gstate);
+
+cairo_private cairo_clip_t *
+_cairo_gstate_get_clip (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source);
+
+cairo_private cairo_pattern_t *
+_cairo_gstate_get_source (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op);
+
+cairo_private cairo_operator_t
+_cairo_gstate_get_operator (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double opacity);
+
+cairo_private double
+_cairo_gstate_get_opacity (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance);
+
+cairo_private double
+_cairo_gstate_get_tolerance (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_fill_rule_t
+_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width);
+
+cairo_private double
+_cairo_gstate_get_line_width (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap);
+
+cairo_private cairo_line_cap_t
+_cairo_gstate_get_line_cap (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join);
+
+cairo_private cairo_line_join_t
+_cairo_gstate_get_line_join (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset);
+
+cairo_private void
+_cairo_gstate_get_dash (cairo_gstate_t *gstate, double *dash, int *num_dashes, double *offset);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit);
+
+cairo_private double
+_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate);
+
+cairo_private void
+_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix);
+
+cairo_private cairo_status_t
+_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty);
+
+cairo_private cairo_status_t
+_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy);
+
+cairo_private cairo_status_t
+_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle);
+
+cairo_private cairo_status_t
+_cairo_gstate_transform (cairo_gstate_t *gstate,
+ const cairo_matrix_t *matrix);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_matrix (cairo_gstate_t *gstate,
+ const cairo_matrix_t *matrix);
+
+cairo_private void
+_cairo_gstate_identity_matrix (cairo_gstate_t *gstate);
+
+cairo_private void
+_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y);
+
+cairo_private void
+_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy);
+
+cairo_private void
+_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y);
+
+cairo_private void
+_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy);
+
+cairo_private void
+_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y);
+
+static inline void
+_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y)
+{
+ if (! gstate->is_identity)
+ _do_cairo_gstate_user_to_backend (gstate, x, y);
+}
+
+cairo_private void
+_do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y);
+
+static inline void
+_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y)
+{
+ if (! gstate->is_identity)
+ _do_cairo_gstate_user_to_backend_distance (gstate, x, y);
+}
+
+cairo_private void
+_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y);
+
+static inline void
+_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
+{
+ if (! gstate->is_identity)
+ _do_cairo_gstate_backend_to_user (gstate, x, y);
+}
+
+cairo_private void
+_do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y);
+
+static inline void
+_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y)
+{
+ if (! gstate->is_identity)
+ _do_cairo_gstate_backend_to_user_distance (gstate, x, y);
+}
+
+cairo_private void
+_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
+ double *x1, double *y1,
+ double *x2, double *y2,
+ cairo_bool_t *is_tight);
+
+cairo_private void
+_cairo_gstate_path_extents (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+cairo_private cairo_status_t
+_cairo_gstate_paint (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_mask (cairo_gstate_t *gstate,
+ cairo_pattern_t *mask);
+
+cairo_private cairo_status_t
+_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_copy_page (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_show_page (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+cairo_private cairo_status_t
+_cairo_gstate_fill_extents (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+cairo_private cairo_status_t
+_cairo_gstate_in_stroke (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double x,
+ double y,
+ cairo_bool_t *inside_ret);
+
+cairo_private cairo_bool_t
+_cairo_gstate_in_fill (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double x,
+ double y);
+
+cairo_private cairo_bool_t
+_cairo_gstate_in_clip (cairo_gstate_t *gstate,
+ double x,
+ double y);
+
+cairo_private cairo_status_t
+_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_reset_clip (cairo_gstate_t *gstate);
+
+cairo_private cairo_bool_t
+_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2);
+
+cairo_private cairo_rectangle_list_t*
+_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_show_surface (cairo_gstate_t *gstate,
+ cairo_surface_t *surface,
+ double x,
+ double y,
+ double width,
+ double height);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
+ double size);
+
+cairo_private void
+_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate,
+ cairo_matrix_t *matrix);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate,
+ const cairo_matrix_t *matrix);
+
+cairo_private void
+_cairo_gstate_get_font_options (cairo_gstate_t *gstate,
+ cairo_font_options_t *options);
+
+cairo_private void
+_cairo_gstate_set_font_options (cairo_gstate_t *gstate,
+ const cairo_font_options_t *options);
+
+cairo_private cairo_status_t
+_cairo_gstate_get_font_face (cairo_gstate_t *gstate,
+ cairo_font_face_t **font_face);
+
+cairo_private cairo_status_t
+_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate,
+ cairo_scaled_font_t **scaled_font);
+
+cairo_private cairo_status_t
+_cairo_gstate_get_font_extents (cairo_gstate_t *gstate,
+ cairo_font_extents_t *extents);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_font_face (cairo_gstate_t *gstate,
+ cairo_font_face_t *font_face);
+
+cairo_private cairo_status_t
+_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents);
+
+cairo_private cairo_status_t
+_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_glyph_text_info_t *info);
+
+cairo_private cairo_status_t
+_cairo_gstate_glyph_path (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_antialias (cairo_gstate_t *gstate,
+ cairo_antialias_t antialias);
+
+cairo_private cairo_antialias_t
+_cairo_gstate_get_antialias (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_shadow (cairo_gstate_t *gstate, cairo_shadow_type_t shadow);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_shadow_offset (cairo_gstate_t *gstate, double x_offset,
+ double y_offset);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_shadow_rgba (cairo_gstate_t *gstate, double r, double g,
+ double b, double a);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_shadow_blur (cairo_gstate_t *gstate, double x_blur,
+ double y_blur);
+
+cairo_private void
+_cairo_gstate_set_draw_shadow_only (cairo_gstate_t *gstate,
+ cairo_bool_t draw_shadow_only);
+
+cairo_private void
+_cairo_gstate_shadow_enable_cache (cairo_gstate_t *gstate, cairo_bool_t enable);
+
+cairo_private void
+_cairo_gstate_set_path_is_inset_shadow_with_spread (cairo_gstate_t *gstate,
+ cairo_bool_t is_spread_path);
+
+#endif /* CAIRO_GSTATE_PRIVATE_H */
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
new file mode 100755
index 000000000..84b47fe9a
--- /dev/null
+++ b/src/cairo-gstate.c
@@ -0,0 +1,2501 @@
+/* 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-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-gstate-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-traps-private.h"
+
+#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 void
+_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_glyph_t *transformed_glyphs,
+ int *num_transformed_glyphs,
+ cairo_text_cluster_t *transformed_clusters);
+
+static cairo_bool_t
+_cairo_gstate_has_shadow (cairo_gstate_t *gstate)
+{
+ if (gstate->shadow.type == CAIRO_SHADOW_NONE)
+ return FALSE;
+
+ if (gstate->shadow.x_blur == 0.0 &&
+ gstate->shadow.y_blur == 0.0 &&
+ gstate->shadow.x_offset == 0.0 &&
+ gstate->shadow.y_offset == 0.0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+_cairo_gstate_update_device_transform (cairo_observer_t *observer,
+ void *arg)
+{
+ cairo_gstate_t *gstate = cairo_container_of (observer,
+ cairo_gstate_t,
+ device_transform_observer);
+
+ gstate->is_identity = (_cairo_matrix_is_identity (&gstate->ctm) &&
+ _cairo_matrix_is_identity (&gstate->target->device_transform));
+}
+
+cairo_status_t
+_cairo_gstate_init (cairo_gstate_t *gstate,
+ cairo_surface_t *target)
+{
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
+
+ gstate->next = NULL;
+
+ gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT;
+ gstate->opacity = 1.;
+
+ gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
+ gstate->antialias = CAIRO_ANTIALIAS_DEFAULT;
+
+ _cairo_stroke_style_init (&gstate->stroke_style);
+
+ gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
+
+ gstate->font_face = NULL;
+ gstate->scaled_font = NULL;
+ gstate->previous_scaled_font = NULL;
+
+ cairo_matrix_init_scale (&gstate->font_matrix,
+ CAIRO_GSTATE_DEFAULT_FONT_SIZE,
+ CAIRO_GSTATE_DEFAULT_FONT_SIZE);
+
+ _cairo_font_options_init_default (&gstate->font_options);
+
+ gstate->clip = NULL;
+
+ gstate->target = cairo_surface_reference (target);
+ gstate->parent_target = NULL;
+ gstate->original_target = cairo_surface_reference (target);
+
+ gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform;
+ cairo_list_add (&gstate->device_transform_observer.link,
+ &gstate->target->device_transform_observers);
+
+ gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform);
+ cairo_matrix_init_identity (&gstate->ctm);
+ gstate->ctm_inverse = gstate->ctm;
+ gstate->source_ctm_inverse = gstate->ctm;
+
+ gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base;
+
+ memset (&gstate->shadow, 0, sizeof (cairo_shadow_t));
+
+ /* Now that the gstate is fully initialized and ready for the eventual
+ * _cairo_gstate_fini(), we can check for errors (and not worry about
+ * the resource deallocation). */
+ return target->status;
+}
+
+/**
+ * _cairo_gstate_init_copy:
+ *
+ * Initialize @gstate by performing a deep copy of state fields from
+ * @other. Note that gstate->next is not copied but is set to %NULL by
+ * this function.
+ **/
+static cairo_status_t
+_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
+{
+ cairo_status_t status;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
+
+ gstate->op = other->op;
+ gstate->opacity = other->opacity;
+
+ gstate->tolerance = other->tolerance;
+ gstate->antialias = other->antialias;
+
+ status = _cairo_stroke_style_init_copy (&gstate->stroke_style,
+ &other->stroke_style);
+ if (unlikely (status))
+ return status;
+
+ gstate->fill_rule = other->fill_rule;
+
+ gstate->font_face = cairo_font_face_reference (other->font_face);
+ gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font);
+ gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font);
+
+ gstate->font_matrix = other->font_matrix;
+
+ _cairo_font_options_init_copy (&gstate->font_options , &other->font_options);
+
+ gstate->clip = _cairo_clip_copy (other->clip);
+
+ gstate->target = cairo_surface_reference (other->target);
+ /* parent_target is always set to NULL; it's only ever set by redirect_target */
+ gstate->parent_target = NULL;
+ gstate->original_target = cairo_surface_reference (other->original_target);
+
+ gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform;
+ cairo_list_add (&gstate->device_transform_observer.link,
+ &gstate->target->device_transform_observers);
+
+ gstate->is_identity = other->is_identity;
+ gstate->ctm = other->ctm;
+ gstate->ctm_inverse = other->ctm_inverse;
+ gstate->source_ctm_inverse = other->source_ctm_inverse;
+
+ gstate->source = cairo_pattern_reference (other->source);
+
+ gstate->shadow = other->shadow;
+
+ gstate->next = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_fini (cairo_gstate_t *gstate)
+{
+ _cairo_stroke_style_fini (&gstate->stroke_style);
+
+ cairo_font_face_destroy (gstate->font_face);
+ gstate->font_face = NULL;
+
+ cairo_scaled_font_destroy (gstate->previous_scaled_font);
+ gstate->previous_scaled_font = NULL;
+
+ cairo_scaled_font_destroy (gstate->scaled_font);
+ gstate->scaled_font = NULL;
+
+ _cairo_clip_destroy (gstate->clip);
+
+ cairo_list_del (&gstate->device_transform_observer.link);
+
+ cairo_surface_destroy (gstate->target);
+ gstate->target = NULL;
+
+ cairo_surface_destroy (gstate->parent_target);
+ gstate->parent_target = NULL;
+
+ cairo_surface_destroy (gstate->original_target);
+ gstate->original_target = NULL;
+
+ cairo_pattern_destroy (gstate->source);
+ gstate->source = NULL;
+
+ memset (&gstate->shadow, 0, sizeof (cairo_shadow_t));
+
+ 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().
+ **/
+cairo_status_t
+_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child)
+{
+ /* If this gstate is already redirected, this is an error; we need a
+ * new gstate to be able to redirect */
+ assert (gstate->parent_target == NULL);
+
+ /* Set up our new parent_target based on our current target;
+ * gstate->parent_target will take the ref that is held by gstate->target
+ */
+ gstate->parent_target = gstate->target;
+
+ /* Now set up our new target; we overwrite gstate->target directly,
+ * since its ref is now owned by gstate->parent_target */
+ gstate->target = cairo_surface_reference (child);
+ gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform);
+ cairo_list_move (&gstate->device_transform_observer.link,
+ &gstate->target->device_transform_observers);
+
+ /* The clip is in surface backend coordinates for the previous target;
+ * translate it into the child's backend coordinates. */
+ _cairo_clip_destroy (gstate->clip);
+ gstate->clip = _cairo_clip_copy_with_translation (gstate->next->clip,
+ child->device_transform.x0 - gstate->parent_target->device_transform.x0,
+ child->device_transform.y0 - gstate->parent_target->device_transform.y0);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_gstate_is_group:
+ * @gstate: a #cairo_gstate_t
+ *
+ * Check if _cairo_gstate_redirect_target has been called on the head
+ * of the stack.
+ *
+ * Return value: %TRUE if @gstate is redirected to a target different
+ * than the previous state in the stack, %FALSE otherwise.
+ **/
+cairo_bool_t
+_cairo_gstate_is_group (cairo_gstate_t *gstate)
+{
+ return gstate->parent_target != NULL;
+}
+
+/**
+ * _cairo_gstate_get_target:
+ * @gstate: a #cairo_gstate_t
+ *
+ * Return the current drawing target; if drawing is not redirected,
+ * this will be the same as _cairo_gstate_get_original_target().
+ *
+ * Return value: the current target surface
+ **/
+cairo_surface_t *
+_cairo_gstate_get_target (cairo_gstate_t *gstate)
+{
+ return gstate->target;
+}
+
+/**
+ * _cairo_gstate_get_original_target:
+ * @gstate: a #cairo_gstate_t
+ *
+ * Return the original target with which @gstate was created. This
+ * function always returns the original target independent of any
+ * child target that may have been set with
+ * _cairo_gstate_redirect_target.
+ *
+ * Return value: the original target surface
+ **/
+cairo_surface_t *
+_cairo_gstate_get_original_target (cairo_gstate_t *gstate)
+{
+ return gstate->original_target;
+}
+
+/**
+ * _cairo_gstate_get_clip:
+ * @gstate: a #cairo_gstate_t
+ *
+ * This space left intentionally blank.
+ *
+ * Return value: a pointer to the gstate's #cairo_clip_t structure.
+ **/
+cairo_clip_t *
+_cairo_gstate_get_clip (cairo_gstate_t *gstate)
+{
+ return gstate->clip;
+}
+
+cairo_status_t
+_cairo_gstate_set_source (cairo_gstate_t *gstate,
+ cairo_pattern_t *source)
+{
+ if (source->status)
+ return source->status;
+
+ source = cairo_pattern_reference (source);
+ cairo_pattern_destroy (gstate->source);
+ gstate->source = source;
+ gstate->source_ctm_inverse = gstate->ctm_inverse;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_pattern_t *
+_cairo_gstate_get_source (cairo_gstate_t *gstate)
+{
+ if (gstate->source == &_cairo_pattern_black.base) {
+ /* do not expose the static object to the user */
+ gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK);
+ }
+
+ return gstate->source;
+}
+
+cairo_status_t
+_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op)
+{
+ gstate->op = op;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_operator_t
+_cairo_gstate_get_operator (cairo_gstate_t *gstate)
+{
+ return gstate->op;
+}
+
+cairo_status_t
+_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double op)
+{
+ gstate->opacity = op;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_opacity (cairo_gstate_t *gstate)
+{
+ return gstate->opacity;
+}
+
+cairo_status_t
+_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance)
+{
+ gstate->tolerance = tolerance;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_tolerance (cairo_gstate_t *gstate)
+{
+ return gstate->tolerance;
+}
+
+cairo_status_t
+_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule)
+{
+ gstate->fill_rule = fill_rule;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_fill_rule_t
+_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate)
+{
+ return gstate->fill_rule;
+}
+
+cairo_status_t
+_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width)
+{
+ gstate->stroke_style.line_width = width;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_line_width (cairo_gstate_t *gstate)
+{
+ return gstate->stroke_style.line_width;
+}
+
+cairo_status_t
+_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap)
+{
+ gstate->stroke_style.line_cap = line_cap;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_line_cap_t
+_cairo_gstate_get_line_cap (cairo_gstate_t *gstate)
+{
+ return gstate->stroke_style.line_cap;
+}
+
+cairo_status_t
+_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join)
+{
+ gstate->stroke_style.line_join = line_join;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_line_join_t
+_cairo_gstate_get_line_join (cairo_gstate_t *gstate)
+{
+ return gstate->stroke_style.line_join;
+}
+
+cairo_status_t
+_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset)
+{
+ double dash_total, on_total, off_total;
+ int i, j;
+
+ free (gstate->stroke_style.dash);
+
+ gstate->stroke_style.num_dashes = num_dashes;
+
+ if (gstate->stroke_style.num_dashes == 0) {
+ gstate->stroke_style.dash = NULL;
+ gstate->stroke_style.dash_offset = 0.0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double));
+ if (unlikely (gstate->stroke_style.dash == NULL)) {
+ gstate->stroke_style.num_dashes = 0;
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ on_total = off_total = dash_total = 0.0;
+ for (i = j = 0; i < num_dashes; i++) {
+ if (dash[i] < 0)
+ return _cairo_error (CAIRO_STATUS_INVALID_DASH);
+
+ if (dash[i] == 0 && i > 0 && i < num_dashes - 1) {
+ if (dash[++i] < 0)
+ return _cairo_error (CAIRO_STATUS_INVALID_DASH);
+
+ gstate->stroke_style.dash[j-1] += dash[i];
+ gstate->stroke_style.num_dashes -= 2;
+ } else
+ gstate->stroke_style.dash[j++] = dash[i];
+
+ if (dash[i]) {
+ dash_total += dash[i];
+ if ((i & 1) == 0)
+ on_total += dash[i];
+ else
+ off_total += dash[i];
+ }
+ }
+
+ if (dash_total == 0.0)
+ return _cairo_error (CAIRO_STATUS_INVALID_DASH);
+
+ /* An odd dash value indicate symmetric repeating, so the total
+ * is twice as long. */
+ if (gstate->stroke_style.num_dashes & 1) {
+ dash_total *= 2;
+ on_total += off_total;
+ }
+
+ if (dash_total - on_total < CAIRO_FIXED_ERROR_DOUBLE) {
+ /* Degenerate dash -> solid line */
+ free (gstate->stroke_style.dash);
+ gstate->stroke_style.dash = NULL;
+ gstate->stroke_style.num_dashes = 0;
+ gstate->stroke_style.dash_offset = 0.0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* The dashing code doesn't like a negative offset or a big positive
+ * offset, so we compute an equivalent offset which is guaranteed to be
+ * positive and less than twice the pattern length. */
+ offset = fmod (offset, dash_total);
+ if (offset < 0.0)
+ offset += dash_total;
+ if (offset <= 0.0) /* Take care of -0 */
+ offset = 0.0;
+ gstate->stroke_style.dash_offset = offset;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_get_dash (cairo_gstate_t *gstate,
+ double *dashes,
+ int *num_dashes,
+ double *offset)
+{
+ if (dashes) {
+ memcpy (dashes,
+ gstate->stroke_style.dash,
+ sizeof (double) * gstate->stroke_style.num_dashes);
+ }
+
+ if (num_dashes)
+ *num_dashes = gstate->stroke_style.num_dashes;
+
+ if (offset)
+ *offset = gstate->stroke_style.dash_offset;
+}
+
+cairo_status_t
+_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit)
+{
+ gstate->stroke_style.miter_limit = limit;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate)
+{
+ return gstate->stroke_style.miter_limit;
+}
+
+void
+_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix)
+{
+ *matrix = gstate->ctm;
+}
+
+cairo_status_t
+_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty)
+{
+ cairo_matrix_t tmp;
+
+ if (! ISFINITE (tx) || ! ISFINITE (ty))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ cairo_matrix_init_translate (&tmp, tx, ty);
+ cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
+ gstate->is_identity = FALSE;
+
+ /* paranoid check against gradual numerical instability */
+ if (! _cairo_matrix_is_invertible (&gstate->ctm))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ cairo_matrix_init_translate (&tmp, -tx, -ty);
+ cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy)
+{
+ cairo_matrix_t tmp;
+
+ if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+ if (! ISFINITE (sx) || ! ISFINITE (sy))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ cairo_matrix_init_scale (&tmp, sx, sy);
+ cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
+ gstate->is_identity = FALSE;
+
+ /* paranoid check against gradual numerical instability */
+ if (! _cairo_matrix_is_invertible (&gstate->ctm))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ cairo_matrix_init_scale (&tmp, 1/sx, 1/sy);
+ cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle)
+{
+ cairo_matrix_t tmp;
+
+ if (angle == 0.)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! ISFINITE (angle))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ cairo_matrix_init_rotate (&tmp, angle);
+ cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
+ gstate->is_identity = FALSE;
+
+ /* paranoid check against gradual numerical instability */
+ if (! _cairo_matrix_is_invertible (&gstate->ctm))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ cairo_matrix_init_rotate (&tmp, -angle);
+ cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_transform (cairo_gstate_t *gstate,
+ const cairo_matrix_t *matrix)
+{
+ cairo_matrix_t tmp;
+ cairo_status_t status;
+
+ if (! _cairo_matrix_is_invertible (matrix))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ if (_cairo_matrix_is_identity (matrix))
+ return CAIRO_STATUS_SUCCESS;
+
+ tmp = *matrix;
+ status = cairo_matrix_invert (&tmp);
+ if (unlikely (status))
+ return status;
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm);
+ cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+ gstate->is_identity = FALSE;
+
+ /* paranoid check against gradual numerical instability */
+ if (! _cairo_matrix_is_invertible (&gstate->ctm))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_set_matrix (cairo_gstate_t *gstate,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+
+ if (memcmp (matrix, &gstate->ctm, sizeof (cairo_matrix_t)) == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! _cairo_matrix_is_invertible (matrix))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ if (_cairo_matrix_is_identity (matrix)) {
+ _cairo_gstate_identity_matrix (gstate);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ gstate->ctm = *matrix;
+ gstate->ctm_inverse = *matrix;
+ status = cairo_matrix_invert (&gstate->ctm_inverse);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ gstate->is_identity = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_identity_matrix (cairo_gstate_t *gstate)
+{
+ if (_cairo_matrix_is_identity (&gstate->ctm))
+ return;
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ cairo_matrix_init_identity (&gstate->ctm);
+ cairo_matrix_init_identity (&gstate->ctm_inverse);
+ gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform);
+}
+
+void
+_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y)
+{
+ cairo_matrix_transform_point (&gstate->ctm, x, y);
+}
+
+void
+_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate,
+ double *dx, double *dy)
+{
+ cairo_matrix_transform_distance (&gstate->ctm, dx, dy);
+}
+
+void
+_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y)
+{
+ cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
+}
+
+void
+_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate,
+ double *dx, double *dy)
+{
+ cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy);
+}
+
+void
+_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y)
+{
+ cairo_matrix_transform_point (&gstate->ctm, x, y);
+ cairo_matrix_transform_point (&gstate->target->device_transform, x, y);
+}
+
+void
+_do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y)
+{
+ cairo_matrix_transform_distance (&gstate->ctm, x, y);
+ cairo_matrix_transform_distance (&gstate->target->device_transform, x, y);
+}
+
+void
+_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
+{
+ cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y);
+ cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
+}
+
+void
+_do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y)
+{
+ cairo_matrix_transform_distance (&gstate->target->device_transform_inverse, x, y);
+ cairo_matrix_transform_distance (&gstate->ctm_inverse, x, y);
+}
+
+void
+_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
+ double *x1, double *y1,
+ double *x2, double *y2,
+ cairo_bool_t *is_tight)
+{
+ cairo_matrix_t matrix_inverse;
+
+ if (! _cairo_matrix_is_identity (&gstate->target->device_transform_inverse) ||
+ ! _cairo_matrix_is_identity (&gstate->ctm_inverse))
+ {
+ cairo_matrix_multiply (&matrix_inverse,
+ &gstate->target->device_transform_inverse,
+ &gstate->ctm_inverse);
+ _cairo_matrix_transform_bounding_box (&matrix_inverse,
+ x1, y1, x2, y2, is_tight);
+ }
+
+ else
+ {
+ if (is_tight)
+ *is_tight = TRUE;
+ }
+}
+
+/* XXX: NYI
+cairo_status_t
+_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate)
+{
+ cairo_status_t status;
+
+ _cairo_pen_init (&gstate);
+ return CAIRO_STATUS_SUCCESS;
+}
+*/
+
+void
+_cairo_gstate_path_extents (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_box_t box;
+ double px1, py1, px2, py2;
+
+ if (_cairo_path_fixed_extents (path, &box)) {
+ px1 = _cairo_fixed_to_double (box.p1.x);
+ py1 = _cairo_fixed_to_double (box.p1.y);
+ px2 = _cairo_fixed_to_double (box.p2.x);
+ py2 = _cairo_fixed_to_double (box.p2.y);
+
+ _cairo_gstate_backend_to_user_rectangle (gstate,
+ &px1, &py1, &px2, &py2,
+ NULL);
+ } else {
+ px1 = 0.0;
+ py1 = 0.0;
+ px2 = 0.0;
+ py2 = 0.0;
+ }
+
+ if (x1)
+ *x1 = px1;
+ if (y1)
+ *y1 = py1;
+ if (x2)
+ *x2 = px2;
+ if (y2)
+ *y2 = py2;
+}
+
+static void
+_cairo_gstate_copy_pattern (cairo_pattern_t *pattern,
+ const cairo_pattern_t *original)
+{
+ /* First check if the we can replace the original with a much simpler
+ * pattern. For example, gradients that are uniform or just have a single
+ * stop can sometimes be replaced with a solid.
+ */
+
+ if (_cairo_pattern_is_clear (original)) {
+ _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
+ CAIRO_COLOR_TRANSPARENT);
+ return;
+ }
+
+ if (original->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ original->type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ cairo_color_t color;
+ if (_cairo_gradient_pattern_is_solid ((cairo_gradient_pattern_t *) original,
+ NULL,
+ &color))
+ {
+ _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
+ &color);
+ return;
+ }
+ }
+
+ _cairo_pattern_init_static_copy (pattern, original);
+}
+
+static void
+_cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate,
+ cairo_pattern_t *pattern,
+ const cairo_pattern_t *original,
+ const cairo_matrix_t *ctm_inverse)
+{
+ _cairo_gstate_copy_pattern (pattern, original);
+
+ /* apply device_transform first so that it is transformed by ctm_inverse */
+ if (original->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern;
+ cairo_surface_t *surface;
+
+ surface_pattern = (cairo_surface_pattern_t *) original;
+ surface = surface_pattern->surface;
+
+ if (_cairo_surface_has_device_transform (surface))
+ _cairo_pattern_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);
+}
+
+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;
+
+ if (gstate->shadow.type != CAIRO_SHADOW_NONE)
+ return op;
+
+ pattern = gstate->source;
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+ if (solid->color.alpha_short <= 0x00ff) {
+ op = CAIRO_OPERATOR_CLEAR;
+ } else if ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) {
+ if ((solid->color.red_short |
+ solid->color.green_short |
+ solid->color.blue_short) <= 0x00ff)
+ {
+ op = CAIRO_OPERATOR_CLEAR;
+ }
+ }
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
+ if (surface->surface->is_clear &&
+ surface->surface->content & CAIRO_CONTENT_ALPHA)
+ {
+ op = CAIRO_OPERATOR_CLEAR;
+ }
+ } else {
+ const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+ if (gradient->n_stops == 0)
+ op = CAIRO_OPERATOR_CLEAR;
+ }
+
+ return op;
+}
+
+static cairo_status_t
+_cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern)
+{
+ if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH &&
+ ((const cairo_mesh_pattern_t *) pattern)->current_patch))
+ {
+ /* If current patch != NULL, the pattern is under construction
+ * and cannot be used as a source */
+ return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION;
+ }
+
+ return pattern->status;
+}
+
+cairo_status_t
+_cairo_gstate_paint (cairo_gstate_t *gstate)
+{
+ cairo_pattern_union_t source_pattern;
+ const cairo_pattern_t *pattern;
+ cairo_status_t status;
+ cairo_operator_t op;
+ cairo_bool_t destroy_pattern = FALSE;
+
+ status = _cairo_gstate_get_pattern_status (gstate->source);
+ if (unlikely (status))
+ return status;
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (gstate->clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (gstate->source->filter == CAIRO_FILTER_GAUSSIAN)
+ status = _cairo_pattern_create_gaussian_matrix (gstate->source, 1024);
+
+ op = _reduce_op (gstate);
+ /* do not use static pattern */
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ if (! _cairo_gstate_has_shadow (gstate))
+ pattern = &_cairo_pattern_clear.base;
+ else {
+ pattern = cairo_pattern_create_rgba (0, 0, 0, 0);
+ destroy_pattern = TRUE;
+ }
+ } else {
+ _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+ pattern = &source_pattern.base;
+ }
+
+ /* FIXME: I don't like this */
+ if (_cairo_gstate_has_shadow (gstate))
+ ((cairo_pattern_t *)pattern)->shadow = gstate->shadow;
+
+ status = _cairo_surface_paint (gstate->target,
+ op, pattern,
+ gstate->clip);
+ if (destroy_pattern)
+ cairo_pattern_destroy ((cairo_pattern_t *)pattern);
+
+ 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_status_t status;
+ cairo_bool_t destroy_pattern = FALSE;
+
+ status = _cairo_gstate_get_pattern_status (mask);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gstate_get_pattern_status (gstate->source);
+ if (unlikely (status))
+ return status;
+
+ if (gstate->source->filter == CAIRO_FILTER_GAUSSIAN)
+ status = _cairo_pattern_create_gaussian_matrix (gstate->source, 1024);
+
+ if (mask->filter == CAIRO_FILTER_GAUSSIAN)
+ status = _cairo_pattern_create_gaussian_matrix (mask, 1024);
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (gstate->clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (gstate->opacity == 1.0);
+
+ if (_cairo_pattern_is_opaque (mask, NULL))
+ return _cairo_gstate_paint (gstate);
+
+ if (_cairo_pattern_is_clear (mask) &&
+ _cairo_operator_bounded_by_mask (gstate->op))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ op = _reduce_op (gstate);
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ if (! _cairo_gstate_has_shadow (gstate))
+ source = &_cairo_pattern_clear.base;
+ else {
+ source = cairo_pattern_create_rgba (0, 0, 0, 0);
+ destroy_pattern = TRUE;
+ }
+ } else {
+ _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+ source = &source_pattern.base;
+ }
+
+ /* FIXME: I don't like this */
+ if (_cairo_gstate_has_shadow (gstate))
+ ((cairo_pattern_t *)source)->shadow = gstate->shadow;
+
+ _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask);
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+ mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+ _cairo_operator_bounded_by_source (op))
+ {
+ const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+ cairo_color_t combined;
+
+ if (mask_pattern.base.has_component_alpha) {
+#define M(R, A, B, c) R.c = A.c * B.c
+ M(combined, solid->color, mask_pattern.solid.color, red);
+ M(combined, solid->color, mask_pattern.solid.color, green);
+ M(combined, solid->color, mask_pattern.solid.color, blue);
+ M(combined, solid->color, mask_pattern.solid.color, alpha);
+#undef M
+ } else {
+ combined = solid->color;
+ _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha);
+ }
+
+ _cairo_color_init_rgba (&combined, combined.red, combined.green,
+ combined.blue, combined.alpha);
+ _cairo_pattern_init_solid (&source_pattern.solid, &combined);
+
+ status = _cairo_surface_paint (gstate->target, op,
+ &source_pattern.base,
+ gstate->clip);
+ }
+ else
+ {
+ status = _cairo_surface_mask (gstate->target, op,
+ source,
+ &mask_pattern.base,
+ gstate->clip);
+ }
+
+ if (destroy_pattern)
+ cairo_pattern_destroy ((cairo_pattern_t *)source);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+ cairo_pattern_union_t source_pattern;
+ cairo_stroke_style_t style;
+ double dash[2];
+ cairo_status_t status;
+
+ status = _cairo_gstate_get_pattern_status (gstate->source);
+ if (unlikely (status))
+ return status;
+
+ if (gstate->source->filter == CAIRO_FILTER_GAUSSIAN)
+ status = _cairo_pattern_create_gaussian_matrix (gstate->source,
+ gstate->stroke_style.line_width);
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (gstate->stroke_style.line_width <= 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (gstate->clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (gstate->opacity == 1.0);
+
+ 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);
+
+ /* FIXME: I don't like this */
+ if (_cairo_gstate_has_shadow (gstate))
+ source_pattern.base.shadow = gstate->shadow;
+
+ return _cairo_surface_stroke (gstate->target,
+ gstate->op,
+ &source_pattern.base,
+ path,
+ &style,
+ &gstate->ctm,
+ &gstate->ctm_inverse,
+ gstate->tolerance,
+ gstate->antialias,
+ gstate->clip);
+}
+
+cairo_status_t
+_cairo_gstate_in_stroke (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double x,
+ double y,
+ cairo_bool_t *inside_ret)
+{
+ cairo_status_t status;
+ cairo_rectangle_int_t extents;
+ cairo_box_t limit;
+ cairo_traps_t traps;
+
+ if (gstate->stroke_style.line_width <= 0.0) {
+ *inside_ret = FALSE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_gstate_user_to_backend (gstate, &x, &y);
+
+ /* Before we perform the expensive stroke analysis,
+ * check whether the point is within the extents of the path.
+ */
+ _cairo_path_fixed_approximate_stroke_extents (path,
+ &gstate->stroke_style,
+ &gstate->ctm,
+ &extents);
+ if (x < extents.x || x > extents.x + extents.width ||
+ y < extents.y || y > extents.y + extents.height)
+ {
+ *inside_ret = FALSE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ limit.p1.x = _cairo_fixed_from_double (x) - 1;
+ limit.p1.y = _cairo_fixed_from_double (y) - 1;
+ limit.p2.x = limit.p1.x + 2;
+ limit.p2.y = limit.p1.y + 2;
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, &limit, 1);
+
+ status = _cairo_path_fixed_stroke_polygon_to_traps (path,
+ &gstate->stroke_style,
+ &gstate->ctm,
+ &gstate->ctm_inverse,
+ gstate->tolerance,
+ &traps);
+ if (unlikely (status))
+ goto BAIL;
+
+ *inside_ret = _cairo_traps_contain (&traps, x, y);
+
+BAIL:
+ _cairo_traps_fini (&traps);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+ cairo_status_t status;
+ const cairo_pattern_t *pattern;
+ cairo_bool_t destroy_pattern = FALSE;
+
+ status = _cairo_gstate_get_pattern_status (gstate->source);
+ if (unlikely (status))
+ return status;
+
+ if (gstate->source->filter == CAIRO_FILTER_GAUSSIAN)
+ status = _cairo_pattern_create_gaussian_matrix (gstate->source, 1024);
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (gstate->clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (gstate->opacity == 1.0);
+
+ if (_cairo_path_fixed_fill_is_empty (path)) {
+ if (_cairo_operator_bounded_by_mask (gstate->op))
+ return CAIRO_STATUS_SUCCESS;
+
+ pattern = &_cairo_pattern_clear.base;
+ status = _cairo_surface_paint (gstate->target,
+ CAIRO_OPERATOR_CLEAR,
+ pattern,
+ gstate->clip);
+ } else {
+ cairo_pattern_union_t source_pattern;
+ cairo_operator_t op;
+ cairo_rectangle_int_t extents;
+ cairo_box_t box;
+
+ op = _reduce_op (gstate);
+ /* FIXME: I don't like this */
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ if (_cairo_gstate_has_shadow (gstate))
+ pattern = &_cairo_pattern_clear.base;
+ else {
+ pattern = cairo_pattern_create_rgba (0, 0, 0, 0);
+ destroy_pattern = TRUE;
+ }
+ } else {
+ _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+ pattern = &source_pattern.base;
+ }
+
+ /* FIXME: I don't like this */
+ if (_cairo_gstate_has_shadow (gstate))
+ ((cairo_pattern_t *)pattern)->shadow = gstate->shadow;
+
+ /* 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) &&
+ gstate->shadow.type != CAIRO_SHADOW_INSET)
+ {
+ status = _cairo_surface_paint (gstate->target, op, pattern,
+ gstate->clip);
+ }
+ else
+ {
+ status = _cairo_surface_fill (gstate->target, op, pattern,
+ path,
+ gstate->fill_rule,
+ gstate->tolerance,
+ gstate->antialias,
+ gstate->clip);
+ }
+ }
+
+ if (destroy_pattern)
+ cairo_pattern_destroy ((cairo_pattern_t *)pattern);
+
+ return status;
+}
+
+cairo_bool_t
+_cairo_gstate_in_fill (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double x,
+ double y)
+{
+ _cairo_gstate_user_to_backend (gstate, &x, &y);
+
+ return _cairo_path_fixed_in_fill (path,
+ gstate->fill_rule,
+ gstate->tolerance,
+ x, y);
+}
+
+cairo_bool_t
+_cairo_gstate_in_clip (cairo_gstate_t *gstate,
+ double x,
+ double y)
+{
+ cairo_clip_t *clip = gstate->clip;
+ int i;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return FALSE;
+
+ if (clip == NULL)
+ return TRUE;
+
+ _cairo_gstate_user_to_backend (gstate, &x, &y);
+
+ if (x < clip->extents.x ||
+ x >= clip->extents.x + clip->extents.width ||
+ y < clip->extents.y ||
+ y >= clip->extents.y + clip->extents.height)
+ {
+ return FALSE;
+ }
+
+ if (clip->num_boxes) {
+ int fx, fy;
+
+ fx = _cairo_fixed_from_double (x);
+ fy = _cairo_fixed_from_double (y);
+ for (i = 0; i < clip->num_boxes; i++) {
+ if (fx >= clip->boxes[i].p1.x && fx <= clip->boxes[i].p2.x &&
+ fy >= clip->boxes[i].p1.y && fy <= clip->boxes[i].p2.y)
+ break;
+ }
+ if (i == clip->num_boxes)
+ return FALSE;
+ }
+
+ if (clip->path) {
+ cairo_clip_path_t *clip_path = clip->path;
+ do {
+ if (! _cairo_path_fixed_in_fill (&clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ x, y))
+ return FALSE;
+ } while ((clip_path = clip_path->prev) != NULL);
+ }
+
+ return TRUE;
+}
+
+cairo_status_t
+_cairo_gstate_copy_page (cairo_gstate_t *gstate)
+{
+ cairo_surface_copy_page (gstate->target);
+ return cairo_surface_status (gstate->target);
+}
+
+cairo_status_t
+_cairo_gstate_show_page (cairo_gstate_t *gstate)
+{
+ cairo_surface_show_page (gstate->target);
+ return cairo_surface_status (gstate->target);
+}
+
+static void
+_cairo_gstate_extents_to_user_rectangle (cairo_gstate_t *gstate,
+ const cairo_box_t *extents,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ double px1, py1, px2, py2;
+
+ px1 = _cairo_fixed_to_double (extents->p1.x);
+ py1 = _cairo_fixed_to_double (extents->p1.y);
+ px2 = _cairo_fixed_to_double (extents->p2.x);
+ py2 = _cairo_fixed_to_double (extents->p2.y);
+
+ _cairo_gstate_backend_to_user_rectangle (gstate,
+ &px1, &py1, &px2, &py2,
+ NULL);
+ if (x1)
+ *x1 = px1;
+ if (y1)
+ *y1 = py1;
+ if (x2)
+ *x2 = px2;
+ if (y2)
+ *y2 = py2;
+}
+
+cairo_status_t
+_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_int_status_t status;
+ cairo_box_t extents;
+ cairo_bool_t empty;
+
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+
+ if (gstate->stroke_style.line_width <= 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ &gstate->stroke_style,
+ &gstate->ctm,
+ gstate->antialias,
+ &boxes);
+ empty = boxes.num_boxes == 0;
+ if (! empty)
+ _cairo_boxes_extents (&boxes, &extents);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_traps_t traps;
+
+ _cairo_traps_init (&traps);
+ status = _cairo_path_fixed_stroke_polygon_to_traps (path,
+ &gstate->stroke_style,
+ &gstate->ctm,
+ &gstate->ctm_inverse,
+ gstate->tolerance,
+ &traps);
+ empty = traps.num_traps == 0;
+ if (! empty)
+ _cairo_traps_extents (&traps, &extents);
+ _cairo_traps_fini (&traps);
+ }
+ if (! empty) {
+ _cairo_gstate_extents_to_user_rectangle (gstate, &extents,
+ x1, y1, x2, y2);
+ }
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_fill_extents (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_status_t status;
+ cairo_box_t extents;
+ cairo_bool_t empty;
+
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+
+ if (_cairo_path_fixed_fill_is_empty (path))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ gstate->fill_rule,
+ gstate->antialias,
+ &boxes);
+ empty = boxes.num_boxes == 0;
+ if (! empty)
+ _cairo_boxes_extents (&boxes, &extents);
+
+ _cairo_boxes_fini (&boxes);
+ } else {
+ cairo_traps_t traps;
+
+ _cairo_traps_init (&traps);
+
+ status = _cairo_path_fixed_fill_to_traps (path,
+ gstate->fill_rule,
+ gstate->tolerance,
+ &traps);
+ empty = traps.num_traps == 0;
+ if (! empty)
+ _cairo_traps_extents (&traps, &extents);
+
+ _cairo_traps_fini (&traps);
+ }
+
+ if (! empty) {
+ _cairo_gstate_extents_to_user_rectangle (gstate, &extents,
+ x1, y1, x2, y2);
+ }
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_reset_clip (cairo_gstate_t *gstate)
+{
+ _cairo_clip_destroy (gstate->clip);
+ gstate->clip = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+ gstate->clip =
+ _cairo_clip_intersect_path (gstate->clip,
+ path,
+ gstate->fill_rule,
+ gstate->tolerance,
+ gstate->antialias);
+ /* XXX */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_gstate_int_clip_extents (cairo_gstate_t *gstate,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t is_bounded;
+
+ is_bounded = _cairo_surface_get_extents (gstate->target, extents);
+
+ if (gstate->clip) {
+ _cairo_rectangle_intersect (extents,
+ _cairo_clip_get_extents (gstate->clip));
+ is_bounded = TRUE;
+ }
+
+ return is_bounded;
+}
+
+cairo_bool_t
+_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2)
+{
+ cairo_rectangle_int_t extents;
+ double px1, py1, px2, py2;
+
+ if (! _cairo_gstate_int_clip_extents (gstate, &extents))
+ return FALSE;
+
+ px1 = extents.x;
+ py1 = extents.y;
+ px2 = extents.x + (int) extents.width;
+ py2 = extents.y + (int) extents.height;
+
+ _cairo_gstate_backend_to_user_rectangle (gstate,
+ &px1, &py1, &px2, &py2,
+ NULL);
+
+ if (x1)
+ *x1 = px1;
+ if (y1)
+ *y1 = py1;
+ if (x2)
+ *x2 = px2;
+ if (y2)
+ *y2 = py2;
+
+ return TRUE;
+}
+
+cairo_rectangle_list_t*
+_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate)
+{
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_list_t *list;
+ cairo_clip_t *clip;
+
+ if (_cairo_surface_get_extents (gstate->target, &extents))
+ clip = _cairo_clip_copy_intersect_rectangle (gstate->clip, &extents);
+ else
+ clip = gstate->clip;
+
+ list = _cairo_clip_copy_rectangle_list (clip, gstate);
+
+ if (clip != gstate->clip)
+ _cairo_clip_destroy (clip);
+
+ return list;
+}
+
+static void
+_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate)
+{
+ if (gstate->scaled_font == NULL)
+ return;
+
+ if (gstate->previous_scaled_font != NULL)
+ cairo_scaled_font_destroy (gstate->previous_scaled_font);
+
+ gstate->previous_scaled_font = gstate->scaled_font;
+ gstate->scaled_font = NULL;
+}
+
+cairo_status_t
+_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
+ double size)
+{
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ cairo_matrix_init_scale (&gstate->font_matrix, size, size);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate,
+ const cairo_matrix_t *matrix)
+{
+ if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ gstate->font_matrix = *matrix;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate,
+ cairo_matrix_t *matrix)
+{
+ *matrix = gstate->font_matrix;
+}
+
+void
+_cairo_gstate_set_font_options (cairo_gstate_t *gstate,
+ const cairo_font_options_t *options)
+{
+ if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0)
+ return;
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ _cairo_font_options_init_copy (&gstate->font_options, options);
+}
+
+void
+_cairo_gstate_get_font_options (cairo_gstate_t *gstate,
+ cairo_font_options_t *options)
+{
+ *options = gstate->font_options;
+}
+
+cairo_status_t
+_cairo_gstate_get_font_face (cairo_gstate_t *gstate,
+ cairo_font_face_t **font_face)
+{
+ cairo_status_t status;
+
+ status = _cairo_gstate_ensure_font_face (gstate);
+ if (unlikely (status))
+ return status;
+
+ *font_face = gstate->font_face;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate,
+ cairo_scaled_font_t **scaled_font)
+{
+ cairo_status_t status;
+
+ status = _cairo_gstate_ensure_scaled_font (gstate);
+ if (unlikely (status))
+ return status;
+
+ *scaled_font = gstate->scaled_font;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Like everything else in this file, fonts involve Too Many Coordinate Spaces;
+ * it is easy to get confused about what's going on.
+ *
+ * The user's view
+ * ---------------
+ *
+ * Users ask for things in user space. When cairo starts, a user space unit
+ * is about 1/96 inch, which is similar to (but importantly different from)
+ * the normal "point" units most users think in terms of. When a user
+ * selects a font, its scale is set to "one user unit". The user can then
+ * independently scale the user coordinate system *or* the font matrix, in
+ * order to adjust the rendered size of the font.
+ *
+ * Metrics are returned in user space, whether they are obtained from
+ * the currently selected font in a #cairo_t or from a #cairo_scaled_font_t
+ * which is a font specialized to a particular scale matrix, CTM, and target
+ * surface.
+ *
+ * The font's view
+ * ---------------
+ *
+ * Fonts are designed and stored (in say .ttf files) in "font space", which
+ * describes an "EM Square" (a design tile) and has some abstract number
+ * such as 1000, 1024, or 2048 units per "EM". This is basically an
+ * uninteresting space for us, but we need to remember that it exists.
+ *
+ * Font resources (from libraries or operating systems) render themselves
+ * to a particular device. Since they do not want to make most programmers
+ * worry about the font design space, the scaling API is simplified to
+ * involve just telling the font the required pixel size of the EM square
+ * (that is, in device space).
+ *
+ *
+ * Cairo's gstate view
+ * -------------------
+ *
+ * In addition to the CTM and CTM inverse, we keep a matrix in the gstate
+ * called the "font matrix" which describes the user's most recent
+ * font-scaling or font-transforming request. This is kept in terms of an
+ * abstract scale factor, composed with the CTM and used to set the font's
+ * pixel size. So if the user asks to "scale the font by 12", the matrix
+ * is:
+ *
+ * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ]
+ *
+ * It is an affine matrix, like all cairo matrices, where its tx and ty
+ * components are used to "nudging" fonts around and are handled in gstate
+ * and then ignored by the "scaled-font" layer.
+ *
+ * In order to perform any action on a font, we must build an object
+ * called a #cairo_font_scale_t; this contains the central 2x2 matrix
+ * resulting from "font matrix * CTM" (sans the font matrix translation
+ * components as stated in the previous paragraph).
+ *
+ * We pass this to the font when making requests of it, which causes it to
+ * reply for a particular [user request, device] combination, under the CTM
+ * (to accommodate the "zoom in" == "bigger fonts" issue above).
+ *
+ * The other terms in our communication with the font are therefore in
+ * device space. When we ask it to perform text->glyph conversion, it will
+ * produce a glyph string in device space. Glyph vectors we pass to it for
+ * measuring or rendering should be in device space. The metrics which we
+ * get back from the font will be in device space. The contents of the
+ * global glyph image cache will be in device space.
+ *
+ *
+ * Cairo's public view
+ * -------------------
+ *
+ * Since the values entering and leaving via public API calls are in user
+ * space, the gstate functions typically need to multiply arguments by the
+ * CTM (for user-input glyph vectors), and return values by the CTM inverse
+ * (for font responses such as metrics or glyph vectors).
+ *
+ */
+
+static cairo_status_t
+_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate)
+{
+ cairo_font_face_t *font_face;
+
+ if (gstate->font_face != NULL)
+ return gstate->font_face->status;
+
+
+ font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT,
+ CAIRO_FONT_SLANT_DEFAULT,
+ CAIRO_FONT_WEIGHT_DEFAULT);
+ if (font_face->status)
+ return font_face->status;
+
+ gstate->font_face = font_face;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate)
+{
+ cairo_status_t status;
+ cairo_font_options_t options;
+ cairo_scaled_font_t *scaled_font;
+
+ 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_set_font_face (cairo_gstate_t *gstate,
+ cairo_font_face_t *font_face)
+{
+ if (font_face && font_face->status)
+ return _cairo_error (font_face->status);
+
+ if (font_face == gstate->font_face)
+ return CAIRO_STATUS_SUCCESS;
+
+ cairo_font_face_destroy (gstate->font_face);
+ gstate->font_face = cairo_font_face_reference (font_face);
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents)
+{
+ cairo_status_t status;
+
+ status = _cairo_gstate_ensure_scaled_font (gstate);
+ if (unlikely (status))
+ return status;
+
+ cairo_scaled_font_glyph_extents (gstate->scaled_font,
+ glyphs, num_glyphs,
+ extents);
+
+ return cairo_scaled_font_status (gstate->scaled_font);
+}
+
+cairo_status_t
+_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_glyph_text_info_t *info)
+{
+ cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+ cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
+ cairo_pattern_union_t source_pattern;
+ cairo_glyph_t *transformed_glyphs;
+ const cairo_pattern_t *pattern;
+ cairo_text_cluster_t *transformed_clusters;
+ cairo_operator_t op;
+ cairo_status_t status;
+
+ status = _cairo_gstate_get_pattern_status (gstate->source);
+ if (unlikely (status))
+ return status;
+
+ if (gstate->source->filter == CAIRO_FILTER_GAUSSIAN)
+ status = _cairo_pattern_create_gaussian_matrix (gstate->source, 1024);
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (gstate->clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_gstate_ensure_scaled_font (gstate);
+ if (unlikely (status))
+ return status;
+
+ transformed_glyphs = stack_transformed_glyphs;
+ transformed_clusters = stack_transformed_clusters;
+
+ if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) {
+ transformed_glyphs = cairo_glyph_allocate (num_glyphs);
+ if (unlikely (transformed_glyphs == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (info != NULL) {
+ if (info->num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) {
+ transformed_clusters = cairo_text_cluster_allocate (info->num_clusters);
+ if (unlikely (transformed_clusters == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_GLYPHS;
+ }
+ }
+
+ _cairo_gstate_transform_glyphs_to_backend (gstate,
+ glyphs, num_glyphs,
+ info->clusters,
+ info->num_clusters,
+ info->cluster_flags,
+ transformed_glyphs,
+ &num_glyphs,
+ transformed_clusters);
+ } else {
+ _cairo_gstate_transform_glyphs_to_backend (gstate,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ transformed_glyphs,
+ &num_glyphs,
+ NULL);
+ }
+
+ if (num_glyphs == 0)
+ goto CLEANUP_GLYPHS;
+
+ op = _reduce_op (gstate);
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ pattern = &_cairo_pattern_clear.base;
+ } else {
+ _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+ pattern = &source_pattern.base;
+ }
+
+ /* FIXME: I don't like this */
+ if (_cairo_gstate_has_shadow (gstate))
+ ((cairo_pattern_t *)pattern)->shadow = gstate->shadow;
+
+ /* For really huge font sizes, we can just do path;fill instead of
+ * show_glyphs, as show_glyphs would put excess pressure on the cache,
+ * and moreover, not all components below us correctly handle huge font
+ * sizes. I wanted to set the limit at 256. But alas, seems like cairo's
+ * rasterizer is something like ten times slower than freetype's for huge
+ * sizes. So, no win just yet. For now, do it for insanely-huge sizes,
+ * just to make sure we don't make anyone unhappy. When we get a really
+ * fast rasterizer in cairo, we may want to readjust this.
+ *
+ * Needless to say, do this only if show_text_glyphs is not available. */
+ if (cairo_surface_has_show_text_glyphs (gstate->target) ||
+ _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240)
+ {
+ if (info != NULL) {
+ status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
+ info->utf8, info->utf8_len,
+ transformed_glyphs, num_glyphs,
+ transformed_clusters, info->num_clusters,
+ info->cluster_flags,
+ gstate->scaled_font,
+ gstate->clip);
+ } else {
+ status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
+ NULL, 0,
+ transformed_glyphs, num_glyphs,
+ NULL, 0, 0,
+ gstate->scaled_font,
+ gstate->clip);
+ }
+ }
+ else
+ {
+ cairo_path_fixed_t path;
+
+ _cairo_path_fixed_init (&path);
+
+ status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
+ transformed_glyphs, num_glyphs,
+ &path);
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _cairo_surface_fill (gstate->target, op, pattern,
+ &path,
+ CAIRO_FILL_RULE_WINDING,
+ gstate->tolerance,
+ gstate->scaled_font->options.antialias,
+ gstate->clip);
+ }
+
+ _cairo_path_fixed_fini (&path);
+ }
+
+CLEANUP_GLYPHS:
+ if (transformed_glyphs != stack_transformed_glyphs)
+ cairo_glyph_free (transformed_glyphs);
+ if (transformed_clusters != stack_transformed_clusters)
+ cairo_text_cluster_free (transformed_clusters);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_glyph_path (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_path_fixed_t *path)
+{
+ cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+ cairo_glyph_t *transformed_glyphs;
+ cairo_status_t status;
+
+ status = _cairo_gstate_ensure_scaled_font (gstate);
+ if (unlikely (status))
+ return status;
+
+ if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) {
+ transformed_glyphs = stack_transformed_glyphs;
+ } else {
+ transformed_glyphs = cairo_glyph_allocate (num_glyphs);
+ if (unlikely (transformed_glyphs == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_gstate_transform_glyphs_to_backend (gstate,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ transformed_glyphs,
+ &num_glyphs, NULL);
+
+ status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
+ transformed_glyphs, num_glyphs,
+ path);
+
+ if (transformed_glyphs != stack_transformed_glyphs)
+ cairo_glyph_free (transformed_glyphs);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_set_antialias (cairo_gstate_t *gstate,
+ cairo_antialias_t antialias)
+{
+ gstate->antialias = antialias;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_antialias_t
+_cairo_gstate_get_antialias (cairo_gstate_t *gstate)
+{
+ return gstate->antialias;
+}
+
+/**
+ * _cairo_gstate_transform_glyphs_to_backend:
+ * @gstate: a #cairo_gstate_t
+ * @glyphs: the array of #cairo_glyph_t objects to be transformed
+ * @num_glyphs: the number of elements in @glyphs
+ * @transformed_glyphs: a pre-allocated array of at least @num_glyphs
+ * #cairo_glyph_t objects
+ * @num_transformed_glyphs: the number of elements in @transformed_glyphs
+ * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be
+ * dropped
+ *
+ * Transform an array of glyphs to backend space by first adding the offset
+ * of the font matrix, then transforming from user space to backend space.
+ * The result of the transformation is placed in @transformed_glyphs.
+ *
+ * This also uses information from the scaled font and the surface to
+ * cull/drop glyphs that will not be visible.
+ **/
+static void
+_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_glyph_t *transformed_glyphs,
+ int *num_transformed_glyphs,
+ cairo_text_cluster_t *transformed_clusters)
+{
+ cairo_rectangle_int_t surface_extents;
+ cairo_matrix_t *ctm = &gstate->ctm;
+ cairo_matrix_t *font_matrix = &gstate->font_matrix;
+ cairo_matrix_t *device_transform = &gstate->target->device_transform;
+ cairo_bool_t drop = FALSE;
+ double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+ int i, j, k;
+
+ drop = TRUE;
+ if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) {
+ drop = FALSE; /* unbounded surface */
+ } else {
+ double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font);
+ if (surface_extents.width == 0 || surface_extents.height == 0) {
+ /* No visible area. Don't draw anything */
+ *num_transformed_glyphs = 0;
+ return;
+ }
+ /* XXX We currently drop any glyphs that 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;
+
+#define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2)
+
+ j = 0;
+ if (_cairo_matrix_is_identity (ctm) &&
+ _cairo_matrix_is_identity (device_transform) &&
+ font_matrix->x0 == 0 && font_matrix->y0 == 0)
+ {
+ if (! drop) {
+ memcpy (transformed_glyphs, glyphs,
+ num_glyphs * sizeof (cairo_glyph_t));
+ memcpy (transformed_clusters, clusters,
+ num_clusters * sizeof (cairo_text_cluster_t));
+ j = num_glyphs;
+ } else if (num_clusters == 0) {
+ for (i = 0; i < num_glyphs; i++) {
+ transformed_glyphs[j].index = glyphs[i].index;
+ transformed_glyphs[j].x = glyphs[i].x;
+ transformed_glyphs[j].y = glyphs[i].y;
+ if (KEEP_GLYPH (transformed_glyphs[j]))
+ j++;
+ }
+ } else {
+ const cairo_glyph_t *cur_glyph;
+
+ if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+ cur_glyph = glyphs + num_glyphs - 1;
+ else
+ cur_glyph = glyphs;
+
+ for (i = 0; i < num_clusters; i++) {
+ cairo_bool_t cluster_visible = FALSE;
+
+ for (k = 0; k < clusters[i].num_glyphs; k++) {
+ transformed_glyphs[j+k].index = cur_glyph->index;
+ transformed_glyphs[j+k].x = cur_glyph->x;
+ transformed_glyphs[j+k].y = cur_glyph->y;
+ if (KEEP_GLYPH (transformed_glyphs[j+k]))
+ cluster_visible = TRUE;
+
+ if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+ cur_glyph--;
+ else
+ cur_glyph++;
+ }
+
+ transformed_clusters[i] = clusters[i];
+ if (cluster_visible)
+ j += k;
+ else
+ transformed_clusters[i].num_glyphs = 0;
+ }
+ }
+ }
+ else if (_cairo_matrix_is_translation (ctm) &&
+ _cairo_matrix_is_translation (device_transform))
+ {
+ double tx = font_matrix->x0 + ctm->x0 + device_transform->x0;
+ double ty = font_matrix->y0 + ctm->y0 + device_transform->y0;
+
+ if (! drop || num_clusters == 0) {
+ for (i = 0; i < num_glyphs; i++) {
+ transformed_glyphs[j].index = glyphs[i].index;
+ transformed_glyphs[j].x = glyphs[i].x + tx;
+ transformed_glyphs[j].y = glyphs[i].y + ty;
+ if (!drop || KEEP_GLYPH (transformed_glyphs[j]))
+ j++;
+ }
+ memcpy (transformed_clusters, clusters,
+ num_clusters * sizeof (cairo_text_cluster_t));
+ } else {
+ const cairo_glyph_t *cur_glyph;
+
+ if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+ cur_glyph = glyphs + num_glyphs - 1;
+ else
+ cur_glyph = glyphs;
+
+ for (i = 0; i < num_clusters; i++) {
+ cairo_bool_t cluster_visible = FALSE;
+
+ for (k = 0; k < clusters[i].num_glyphs; k++) {
+ transformed_glyphs[j+k].index = cur_glyph->index;
+ transformed_glyphs[j+k].x = cur_glyph->x + tx;
+ transformed_glyphs[j+k].y = cur_glyph->y + ty;
+ if (KEEP_GLYPH (transformed_glyphs[j+k]))
+ cluster_visible = TRUE;
+
+ if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+ cur_glyph--;
+ else
+ cur_glyph++;
+ }
+
+ transformed_clusters[i] = clusters[i];
+ if (cluster_visible)
+ j += k;
+ else
+ transformed_clusters[i].num_glyphs = 0;
+ }
+ }
+ }
+ else
+ {
+ cairo_matrix_t aggregate_transform;
+
+ cairo_matrix_init_translate (&aggregate_transform,
+ gstate->font_matrix.x0,
+ gstate->font_matrix.y0);
+ cairo_matrix_multiply (&aggregate_transform,
+ &aggregate_transform, ctm);
+ cairo_matrix_multiply (&aggregate_transform,
+ &aggregate_transform, device_transform);
+
+ if (! drop || num_clusters == 0) {
+ for (i = 0; i < num_glyphs; i++) {
+ transformed_glyphs[j] = glyphs[i];
+ cairo_matrix_transform_point (&aggregate_transform,
+ &transformed_glyphs[j].x,
+ &transformed_glyphs[j].y);
+ if (! drop || KEEP_GLYPH (transformed_glyphs[j]))
+ j++;
+ }
+ memcpy (transformed_clusters, clusters,
+ num_clusters * sizeof (cairo_text_cluster_t));
+ } else {
+ const cairo_glyph_t *cur_glyph;
+
+ if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+ cur_glyph = glyphs + num_glyphs - 1;
+ else
+ cur_glyph = glyphs;
+
+ for (i = 0; i < num_clusters; i++) {
+ cairo_bool_t cluster_visible = FALSE;
+ for (k = 0; k < clusters[i].num_glyphs; k++) {
+ transformed_glyphs[j+k] = *cur_glyph;
+ cairo_matrix_transform_point (&aggregate_transform,
+ &transformed_glyphs[j+k].x,
+ &transformed_glyphs[j+k].y);
+ if (KEEP_GLYPH (transformed_glyphs[j+k]))
+ cluster_visible = TRUE;
+
+ if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+ cur_glyph--;
+ else
+ cur_glyph++;
+ }
+
+ transformed_clusters[i] = clusters[i];
+ if (cluster_visible)
+ j += k;
+ else
+ transformed_clusters[i].num_glyphs = 0;
+ }
+ }
+ }
+ *num_transformed_glyphs = j;
+
+ if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) {
+ for (i = 0; i < --j; i++) {
+ cairo_glyph_t tmp;
+
+ tmp = transformed_glyphs[i];
+ transformed_glyphs[i] = transformed_glyphs[j];
+ transformed_glyphs[j] = tmp;
+ }
+ }
+}
+
+cairo_status_t
+_cairo_gstate_set_shadow (cairo_gstate_t *gstate, cairo_shadow_type_t shadow)
+{
+ gstate->shadow.type = shadow;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_set_shadow_offset (cairo_gstate_t *gstate, double x_offset,
+ double y_offset)
+{
+ double x = x_offset;
+ double y = y_offset;
+
+ gstate->shadow.x_offset = x;
+ gstate->shadow.y_offset = y;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_set_shadow_rgba (cairo_gstate_t *gstate, double r, double g,
+ double b, double a)
+{
+ r = _cairo_restrict_value (r, 0.0, 1.0);
+ g = _cairo_restrict_value (g, 0.0, 1.0);
+ b = _cairo_restrict_value (b, 0.0, 1.0);
+ a = _cairo_restrict_value (a, 0.0, 1.0);
+
+ _cairo_color_init_rgba (&gstate->shadow.color, r, g, b, a);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_set_shadow_blur (cairo_gstate_t *gstate, double x_blur,
+ double y_blur)
+{
+ double x = x_blur;
+ double y = y_blur;
+
+ if (x < 0.0)
+ x = 0.0;
+ if (y < 0.0)
+ y = 0.0;
+
+ gstate->shadow.x_blur = MIN (CAIRO_MAX_BLUR, x);
+ gstate->shadow.y_blur = MIN (CAIRO_MAX_BLUR, y);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_set_draw_shadow_only (cairo_gstate_t *gstate,
+ cairo_bool_t draw_shadow_only)
+{
+ gstate->shadow.draw_shadow_only = draw_shadow_only;
+}
+
+void
+_cairo_gstate_shadow_enable_cache (cairo_gstate_t *gstate, cairo_bool_t enable)
+{
+ gstate->shadow.enable_cache = enable;
+}
+
+void
+_cairo_gstate_set_path_is_inset_shadow_with_spread (cairo_gstate_t *gstate,
+ cairo_bool_t is_spread_path)
+{
+ gstate->shadow.path_is_fill_with_spread = is_spread_path;
+}
diff --git a/src/cairo-hash-private.h b/src/cairo-hash-private.h
new file mode 100755
index 000000000..30e51ffe6
--- /dev/null
+++ b/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/src/cairo-hash.c b/src/cairo-hash.c
new file mode 100755
index 000000000..928c74b8b
--- /dev/null
+++ b/src/cairo-hash.c
@@ -0,0 +1,578 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Keith Packard <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)
+
+/*
+ * This table is open-addressed with double hashing. Each table size
+ * is a prime and it makes for the "first" hash modulus; a second
+ * prime (2 less than the first prime) serves as the "second" hash
+ * modulus, which is smaller and thus guarantees a complete
+ * permutation of table indices.
+ *
+ * Hash tables are rehashed in order to keep between 12.5% and 50%
+ * entries in the hash table alive and at least 25% free. When table
+ * size is changed, the new table has about 25% live elements.
+ *
+ * The free entries guarantee an expected constant-time lookup.
+ * Doubling/halving the table in the described fashion guarantees
+ * amortized O(1) insertion/removal.
+ *
+ * This structure, and accompanying table, is borrowed/modified from the
+ * file xserver/render/glyph.c in the freedesktop.org x server, with
+ * permission (and suggested modification of doubling sizes) by Keith
+ * Packard.
+ */
+
+static const unsigned long hash_table_sizes[] = {
+ 43,
+ 73,
+ 151,
+ 283,
+ 571,
+ 1153,
+ 2269,
+ 4519,
+ 9013,
+ 18043,
+ 36109,
+ 72091,
+ 144409,
+ 288361,
+ 576883,
+ 1153459,
+ 2307163,
+ 4613893,
+ 9227641,
+ 18455029,
+ 36911011,
+ 73819861,
+ 147639589,
+ 295279081,
+ 590559793
+};
+
+struct _cairo_hash_table {
+ cairo_hash_keys_equal_func_t keys_equal;
+
+ cairo_hash_entry_t *cache[32];
+
+ const unsigned long *table_size;
+ cairo_hash_entry_t **entries;
+
+ unsigned long live_entries;
+ unsigned long free_entries;
+ unsigned long iterating; /* Iterating, no insert, no resize */
+};
+
+/**
+ * _cairo_hash_table_uid_keys_equal:
+ * @key_a: the first key to be compared
+ * @key_b: the second key to be compared
+ *
+ * Provides a #cairo_hash_keys_equal_func_t which always returns
+ * %TRUE. This is useful to create hash tables using keys whose hash
+ * completely describes the key, because in this special case
+ * comparing the hashes is sufficient to guarantee that the keys are
+ * equal.
+ *
+ * Return value: %TRUE.
+ **/
+static cairo_bool_t
+_cairo_hash_table_uid_keys_equal (const void *key_a, const void *key_b)
+{
+ return TRUE;
+}
+
+/**
+ * _cairo_hash_table_create:
+ * @keys_equal: a function to return %TRUE if two keys are equal
+ *
+ * Creates a new hash table which will use the keys_equal() function
+ * to compare hash keys. Data is provided to the hash table in the
+ * form of user-derived versions of #cairo_hash_entry_t. A hash entry
+ * must be able to hold both a key (including a hash code) and a
+ * value. Sometimes only the key will be necessary, (as in
+ * _cairo_hash_table_remove), and other times both a key and a value
+ * will be necessary, (as in _cairo_hash_table_insert).
+ *
+ * If @keys_equal is %NULL, two keys will be considered equal if and
+ * only if their hashes are equal.
+ *
+ * See #cairo_hash_entry_t for more details.
+ *
+ * Return value: the new hash table or %NULL if out of memory.
+ **/
+cairo_hash_table_t *
+_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal)
+{
+ cairo_hash_table_t *hash_table;
+
+ hash_table = malloc (sizeof (cairo_hash_table_t));
+ if (unlikely (hash_table == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ if (keys_equal == NULL)
+ hash_table->keys_equal = _cairo_hash_table_uid_keys_equal;
+ else
+ hash_table->keys_equal = keys_equal;
+
+ memset (&hash_table->cache, 0, sizeof (hash_table->cache));
+ hash_table->table_size = &hash_table_sizes[0];
+
+ hash_table->entries = calloc (*hash_table->table_size,
+ sizeof (cairo_hash_entry_t *));
+ if (unlikely (hash_table->entries == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ free (hash_table);
+ return NULL;
+ }
+
+ hash_table->live_entries = 0;
+ hash_table->free_entries = *hash_table->table_size;
+ hash_table->iterating = 0;
+
+ return hash_table;
+}
+
+/**
+ * _cairo_hash_table_destroy:
+ * @hash_table: an empty hash table to destroy
+ *
+ * Immediately destroys the given hash table, freeing all resources
+ * associated with it.
+ *
+ * WARNING: The hash_table must have no live entries in it before
+ * _cairo_hash_table_destroy is called. It is a fatal error otherwise,
+ * and this function will halt. The rationale for this behavior is to
+ * avoid memory leaks and to avoid needless complication of the API
+ * with destroy 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);
+ free (hash_table);
+}
+
+static cairo_hash_entry_t **
+_cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table,
+ cairo_hash_entry_t *key)
+{
+ unsigned long table_size, i, idx, step;
+ cairo_hash_entry_t **entry;
+
+ table_size = *hash_table->table_size;
+ idx = key->hash % table_size;
+
+ entry = &hash_table->entries[idx];
+ if (! ENTRY_IS_LIVE (*entry))
+ return entry;
+
+ i = 1;
+ step = 1 + key->hash % (table_size - 2);
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = &hash_table->entries[idx];
+ if (! ENTRY_IS_LIVE (*entry))
+ return entry;
+ } while (++i < table_size);
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+/**
+ * _cairo_hash_table_manage:
+ * @hash_table: a hash table
+ *
+ * Resize the hash table if the number of entries has gotten much
+ * bigger or smaller than the ideal number of entries for the current
+ * size and guarantee some free entries to be used as lookup
+ * termination points.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
+static cairo_status_t
+_cairo_hash_table_manage (cairo_hash_table_t *hash_table)
+{
+ cairo_hash_table_t tmp;
+ unsigned long new_size, i;
+
+ /* Keep between 12.5% and 50% entries in the hash table alive and
+ * at least 25% free. */
+ unsigned long live_high = *hash_table->table_size >> 1;
+ unsigned long live_low = live_high >> 2;
+ unsigned long free_low = live_high >> 1;
+
+ tmp = *hash_table;
+
+ if (hash_table->live_entries > live_high)
+ {
+ tmp.table_size = hash_table->table_size + 1;
+ /* This code is being abused if we can't make a table big enough. */
+ assert (tmp.table_size - hash_table_sizes <
+ ARRAY_LENGTH (hash_table_sizes));
+ }
+ else if (hash_table->live_entries < live_low)
+ {
+ /* Can't shrink if we're at the smallest size */
+ if (hash_table->table_size == &hash_table_sizes[0])
+ tmp.table_size = hash_table->table_size;
+ else
+ tmp.table_size = hash_table->table_size - 1;
+ }
+
+ if (tmp.table_size == hash_table->table_size &&
+ hash_table->free_entries > free_low)
+ {
+ /* The number of live entries is within the desired bounds
+ * (we're not going to resize the table) and we have enough
+ * free entries. Do nothing. */
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ new_size = *tmp.table_size;
+ tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*));
+ if (unlikely (tmp.entries == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (i = 0; i < *hash_table->table_size; ++i) {
+ if (ENTRY_IS_LIVE (hash_table->entries[i])) {
+ *_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i])
+ = hash_table->entries[i];
+ }
+ }
+
+ free (hash_table->entries);
+ hash_table->entries = tmp.entries;
+ hash_table->table_size = tmp.table_size;
+ hash_table->free_entries = new_size - hash_table->live_entries;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_hash_table_lookup:
+ * @hash_table: a hash table
+ * @key: the key of interest
+ *
+ * Performs a lookup in @hash_table looking for an entry which has a
+ * key that matches @key, (as determined by the keys_equal() function
+ * passed to _cairo_hash_table_create).
+ *
+ * Return value: the matching entry, of %NULL if no match was found.
+ **/
+void *
+_cairo_hash_table_lookup (cairo_hash_table_t *hash_table,
+ cairo_hash_entry_t *key)
+{
+ cairo_hash_entry_t *entry;
+ unsigned long table_size, i, idx, step;
+ unsigned long hash = key->hash;
+
+ entry = hash_table->cache[hash & 31];
+ if (entry && entry->hash == hash && hash_table->keys_equal (key, entry))
+ return entry;
+
+ table_size = *hash_table->table_size;
+ idx = hash % table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry)) {
+ if (entry->hash == hash && hash_table->keys_equal (key, entry))
+ goto insert_cache;
+ } else if (ENTRY_IS_FREE (entry))
+ return NULL;
+
+ i = 1;
+ step = 1 + hash % (table_size - 2);
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry)) {
+ if (entry->hash == hash && hash_table->keys_equal (key, entry))
+ goto insert_cache;
+ } else if (ENTRY_IS_FREE (entry))
+ return NULL;
+ } while (++i < table_size);
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+
+insert_cache:
+ hash_table->cache[hash & 31] = entry;
+ return entry;
+}
+
+/**
+ * _cairo_hash_table_random_entry:
+ * @hash_table: a hash table
+ * @predicate: a predicate function.
+ *
+ * Find a random entry in the hash table satisfying the given
+ * @predicate.
+ *
+ * We use the same algorithm as the lookup algorithm to walk over the
+ * entries in the hash table in a pseudo-random order. Walking
+ * linearly would favor entries following gaps in the hash table. We
+ * could also call rand() repeatedly, which works well for almost-full
+ * tables, but degrades when the table is almost empty, or predicate
+ * returns %TRUE for most entries.
+ *
+ * Return value: a random live entry or %NULL if there are no entries
+ * that match the given predicate. In particular, if predicate is
+ * %NULL, a %NULL return value indicates that the table is empty.
+ **/
+void *
+_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table,
+ cairo_hash_predicate_func_t predicate)
+{
+ cairo_hash_entry_t *entry;
+ unsigned long hash;
+ unsigned long table_size, i, idx, step;
+
+ assert (predicate != NULL);
+
+ table_size = *hash_table->table_size;
+ hash = rand ();
+ idx = hash % table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry) && predicate (entry))
+ return entry;
+
+ i = 1;
+ step = 1 + hash % (table_size - 2);
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry) && predicate (entry))
+ return entry;
+ } while (++i < table_size);
+
+ return NULL;
+}
+
+/**
+ * _cairo_hash_table_insert:
+ * @hash_table: a hash table
+ * @key_and_value: an entry to be inserted
+ *
+ * Insert the entry #key_and_value into the hash table.
+ *
+ * WARNING: There must not be an existing entry in the hash table
+ * with a matching key.
+ *
+ * WARNING: It is a fatal error to insert an element while
+ * an iterator is running
+ *
+ * Instead of using insert to replace an entry, consider just editing
+ * the entry obtained with _cairo_hash_table_lookup. Or if absolutely
+ * necessary, use _cairo_hash_table_remove first.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available.
+ **/
+cairo_status_t
+_cairo_hash_table_insert (cairo_hash_table_t *hash_table,
+ cairo_hash_entry_t *key_and_value)
+{
+ cairo_hash_entry_t **entry;
+ cairo_status_t status;
+
+ /* Insert is illegal while an iterator is running. */
+ assert (hash_table->iterating == 0);
+
+ status = _cairo_hash_table_manage (hash_table);
+ if (unlikely (status))
+ return status;
+
+ entry = _cairo_hash_table_lookup_unique_key (hash_table, key_and_value);
+
+ if (ENTRY_IS_FREE (*entry))
+ hash_table->free_entries--;
+
+ *entry = key_and_value;
+ hash_table->cache[key_and_value->hash & 31] = key_and_value;
+ hash_table->live_entries++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_hash_entry_t **
+_cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table,
+ cairo_hash_entry_t *key)
+{
+ unsigned long table_size, i, idx, step;
+ cairo_hash_entry_t **entry;
+
+ table_size = *hash_table->table_size;
+ idx = key->hash % table_size;
+
+ entry = &hash_table->entries[idx];
+ if (*entry == key)
+ return entry;
+
+ i = 1;
+ step = 1 + key->hash % (table_size - 2);
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = &hash_table->entries[idx];
+ if (*entry == key)
+ return entry;
+ } while (++i < table_size);
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+/**
+ * _cairo_hash_table_remove:
+ * @hash_table: a hash table
+ * @key: key of entry to be removed
+ *
+ * Remove an entry from the hash table which points to @key.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
+void
+_cairo_hash_table_remove (cairo_hash_table_t *hash_table,
+ cairo_hash_entry_t *key)
+{
+ *_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY;
+ hash_table->live_entries--;
+ hash_table->cache[key->hash & 31] = NULL;
+
+ /* Check for table resize. Don't do this when iterating as this will
+ * reorder elements of the table and cause the iteration to potentially
+ * skip some elements. */
+ if (hash_table->iterating == 0) {
+ /* This call _can_ fail, but only in failing to allocate new
+ * memory to shrink the hash table. It does leave the table in a
+ * consistent state, and we've already succeeded in removing the
+ * entry, so we don't examine the failure status of this call. */
+ _cairo_hash_table_manage (hash_table);
+ }
+}
+
+/**
+ * _cairo_hash_table_foreach:
+ * @hash_table: a hash table
+ * @hash_callback: function to be called for each live entry
+ * @closure: additional argument to be passed to @hash_callback
+ *
+ * Call @hash_callback for each live entry in the hash table, in a
+ * non-specified order.
+ *
+ * Entries in @hash_table may be removed by code executed from @hash_callback.
+ *
+ * Entries may not be inserted to @hash_table, nor may @hash_table
+ * be destroyed by code executed from @hash_callback. The relevant
+ * functions will halt in these cases.
+ **/
+void
+_cairo_hash_table_foreach (cairo_hash_table_t *hash_table,
+ cairo_hash_callback_func_t hash_callback,
+ void *closure)
+{
+ unsigned long i;
+ cairo_hash_entry_t *entry;
+
+ /* Mark the table for iteration */
+ ++hash_table->iterating;
+ for (i = 0; i < *hash_table->table_size; i++) {
+ entry = hash_table->entries[i];
+ if (ENTRY_IS_LIVE(entry))
+ hash_callback (entry, closure);
+ }
+ /* If some elements were deleted during the iteration,
+ * the table may need resizing. Just do this every time
+ * as the check is inexpensive.
+ */
+ if (--hash_table->iterating == 0) {
+ /* Should we fail to shrink the hash table, it is left unaltered,
+ * and we don't need to propagate the error status. */
+ _cairo_hash_table_manage (hash_table);
+ }
+}
diff --git a/src/cairo-hull.c b/src/cairo-hull.c
new file mode 100755
index 000000000..c65593327
--- /dev/null
+++ b/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/src/cairo-image-compositor.c b/src/cairo-image-compositor.c
new file mode 100755
index 000000000..a732f2a3a
--- /dev/null
+++ b/src/cairo-image-compositor.c
@@ -0,0 +1,3277 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* The primarily reason for keeping a traps-compositor around is
+ * for validating cairo-xlib (which currently also uses traps).
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-spans-compositor-private.h"
+
+#include "cairo-region-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-tristrip-private.h"
+
+#if CAIRO_HAS_TG_SURFACE
+#include "cairo-thread-local-private.h"
+#endif
+
+static pixman_image_t *
+to_pixman_image (cairo_surface_t *s)
+{
+ return ((cairo_image_surface_t *)s)->pixman_image;
+}
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+ cairo_region_t *region)
+{
+ cairo_image_surface_t *surface = _surface;
+ pixman_region32_t *rgn = region ? &region->rgn : NULL;
+
+ if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy)
+{
+ cairo_image_surface_t *dst = _dst;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+ int x = _cairo_fixed_integer_part (b->p1.x);
+ int y = _cairo_fixed_integer_part (b->p1.y);
+ int w = _cairo_fixed_integer_part (b->p2.x) - x;
+ int h = _cairo_fixed_integer_part (b->p2.y) - y;
+ if (dst->pixman_format != image->pixman_format ||
+ ! pixman_blt ((uint32_t *)image->data, (uint32_t *)dst->data,
+ image->stride / sizeof (uint32_t),
+ dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (image->pixman_format),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x + dx, y + dy,
+ x, y,
+ w, h))
+ {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, dst->pixman_image,
+ x + dx, y + dy,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+ }
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+ return
+ (color->alpha_short >> 8 << 24) |
+ (color->red_short >> 8 << 16) |
+ (color->green_short & 0xff00) |
+ (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t *color,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ uint32_t c;
+
+ if (!(format == PIXMAN_a8r8g8b8 ||
+ format == PIXMAN_x8r8g8b8 ||
+ format == PIXMAN_a8b8g8r8 ||
+ format == PIXMAN_x8b8g8r8 ||
+ format == PIXMAN_b8g8r8a8 ||
+ format == PIXMAN_b8g8r8x8 ||
+ format == PIXMAN_r5g6b5 ||
+ format == PIXMAN_b5g6r5 ||
+ format == PIXMAN_a8))
+ {
+ return FALSE;
+ }
+
+ c = color_to_uint32 (color);
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+ c = ((c & 0xff000000) >> 0) |
+ ((c & 0x00ff0000) >> 16) |
+ ((c & 0x0000ff00) >> 0) |
+ ((c & 0x000000ff) << 16);
+ }
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+ c = ((c & 0xff000000) >> 24) |
+ ((c & 0x00ff0000) >> 8) |
+ ((c & 0x0000ff00) << 8) |
+ ((c & 0x000000ff) << 24);
+ }
+
+ if (format == PIXMAN_a8) {
+ c = c >> 24;
+ } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+ c = ((((c) >> 3) & 0x001f) |
+ (((c) >> 5) & 0x07e0) |
+ (((c) >> 8) & 0xf800));
+ }
+
+ *pixel = c;
+ return TRUE;
+}
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+ switch ((int) op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return PIXMAN_OP_CLEAR;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return PIXMAN_OP_SRC;
+ case CAIRO_OPERATOR_OVER:
+ return PIXMAN_OP_OVER;
+ case CAIRO_OPERATOR_IN:
+ return PIXMAN_OP_IN;
+ case CAIRO_OPERATOR_OUT:
+ return PIXMAN_OP_OUT;
+ case CAIRO_OPERATOR_ATOP:
+ return PIXMAN_OP_ATOP;
+
+ case CAIRO_OPERATOR_DEST:
+ return PIXMAN_OP_DST;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return PIXMAN_OP_OVER_REVERSE;
+ case CAIRO_OPERATOR_DEST_IN:
+ return PIXMAN_OP_IN_REVERSE;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return PIXMAN_OP_OUT_REVERSE;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return PIXMAN_OP_ATOP_REVERSE;
+
+ case CAIRO_OPERATOR_XOR:
+ return PIXMAN_OP_XOR;
+ case CAIRO_OPERATOR_ADD:
+ return PIXMAN_OP_ADD;
+ case CAIRO_OPERATOR_SATURATE:
+ return PIXMAN_OP_SATURATE;
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ return PIXMAN_OP_MULTIPLY;
+ case CAIRO_OPERATOR_SCREEN:
+ return PIXMAN_OP_SCREEN;
+ case CAIRO_OPERATOR_OVERLAY:
+ return PIXMAN_OP_OVERLAY;
+ case CAIRO_OPERATOR_DARKEN:
+ return PIXMAN_OP_DARKEN;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return PIXMAN_OP_LIGHTEN;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return PIXMAN_OP_COLOR_DODGE;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return PIXMAN_OP_COLOR_BURN;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return PIXMAN_OP_HARD_LIGHT;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return PIXMAN_OP_SOFT_LIGHT;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return PIXMAN_OP_DIFFERENCE;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return PIXMAN_OP_EXCLUSION;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return PIXMAN_OP_HSL_HUE;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return PIXMAN_OP_HSL_SATURATION;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return PIXMAN_OP_HSL_COLOR;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return PIXMAN_OP_HSL_LUMINOSITY;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return PIXMAN_OP_OVER;
+ }
+}
+
+static cairo_bool_t
+fill_reduces_to_source (cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_image_surface_t *dst)
+{
+ if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR)
+ return TRUE;
+ if (op == CAIRO_OPERATOR_OVER && CAIRO_COLOR_IS_OPAQUE (color))
+ return TRUE;
+ if (dst->base.is_clear)
+ return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD;
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+fill_rectangles (void *_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_image_surface_t *dst = _dst;
+ uint32_t pixel;
+ int i;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (fill_reduces_to_source (op, color, dst) &&
+ color_to_pixel (color, dst->pixman_format, &pixel))
+ {
+ for (i = 0; i < num_rects; i++) {
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height,
+ pixel);
+ }
+ }
+ else
+ {
+ pixman_image_t *src = _pixman_image_for_color (color);
+ if (src == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ op = _pixman_operator (op);
+ for (i = 0; i < num_rects; i++) {
+ pixman_image_composite32 (op,
+ src, NULL, dst->pixman_image,
+ 0, 0,
+ 0, 0,
+ rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height);
+ }
+
+ pixman_image_unref (src);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fill_boxes (void *_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ cairo_image_surface_t *dst = _dst;
+ struct _cairo_boxes_chunk *chunk;
+ uint32_t pixel;
+ int i;
+
+ TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));
+
+ if (fill_reduces_to_source (op, color, dst) &&
+ color_to_pixel (color, dst->pixman_format, &pixel))
+ {
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int w = _cairo_fixed_integer_part (chunk->base[i].p2.x) - x;
+ int h = _cairo_fixed_integer_part (chunk->base[i].p2.y) - y;
+ pixman_fill ((uint32_t *) dst->data,
+ dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x, y, w, h, pixel);
+ }
+ }
+ }
+ else
+ {
+ pixman_image_t *src = _pixman_image_for_color (color);
+ if (src == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ op = _pixman_operator (op);
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+ pixman_image_composite32 (op,
+ src, NULL, dst->pixman_image,
+ 0, 0,
+ 0, 0,
+ x1, y1,
+ x2-x1, y2-y1);
+ }
+ }
+
+ pixman_image_unref (src);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
+ cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (mask) {
+ pixman_image_composite32 (_pixman_operator (op),
+ src->pixman_image, mask->pixman_image, to_pixman_image (_dst),
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ } else {
+ pixman_image_composite32 (_pixman_operator (op),
+ src->pixman_image, NULL, to_pixman_image (_dst),
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp (void *_dst,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_image_surface_t *dst = _dst;
+ cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
+ cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
+ src->pixman_image, mask->pixman_image, dst->pixman_image,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+#else
+ /* Punch the clip out of the destination */
+ TRACE ((stderr, "%s - OUT_REVERSE (mask=%d/%p, dst=%d/%p)\n",
+ __FUNCTION__,
+ mask->base.unique_id, mask->pixman_image,
+ dst->base.unique_id, dst->pixman_image));
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask->pixman_image, NULL, dst->pixman_image,
+ mask_x, mask_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+
+ /* Now add the two results together */
+ TRACE ((stderr, "%s - ADD (src=%d/%p, mask=%d/%p, dst=%d/%p)\n",
+ __FUNCTION__,
+ src->base.unique_id, src->pixman_image,
+ mask->base.unique_id, mask->pixman_image,
+ dst->base.unique_id, dst->pixman_image));
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ src->pixman_image, mask->pixman_image, dst->pixman_image,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+#endif
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp_color_glyph (void *_dst,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_image_surface_t *dst = _dst;
+ cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
+ cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ /* Punch the clip out of the destination */
+ TRACE ((stderr, "%s - OUT_REVERSE (mask=%d/%p, dst=%d/%p)\n",
+ __FUNCTION__,
+ mask->base.unique_id, mask->pixman_image,
+ dst->base.unique_id, dst->pixman_image));
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask->pixman_image, src->pixman_image, dst->pixman_image,
+ mask_x, mask_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+
+ /* Now add the two results together */
+ TRACE ((stderr, "%s - ADD (src=%d/%p, mask=%d/%p, dst=%d/%p)\n",
+ __FUNCTION__,
+ src->base.unique_id, src->pixman_image,
+ mask->base.unique_id, mask->pixman_image,
+ dst->base.unique_id, dst->pixman_image));
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ src->pixman_image, mask->pixman_image, dst->pixman_image,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents)
+{
+ pixman_image_t *dst = to_pixman_image (_dst);
+ pixman_image_t *src = ((cairo_image_source_t *)abstract_src)->pixman_image;
+ pixman_image_t *mask = abstract_mask ? ((cairo_image_source_t *)abstract_mask)->pixman_image : NULL;
+ pixman_image_t *free_src = NULL;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ /* XXX consider using a region? saves multiple prepare-composite */
+ TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));
+
+ if (((cairo_surface_t *)_dst)->is_clear &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ op == CAIRO_OPERATOR_OVER ||
+ op == CAIRO_OPERATOR_ADD)) {
+ op = PIXMAN_OP_SRC;
+ } else if (mask) {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+#if PIXMAN_HAS_OP_LERP
+ op = PIXMAN_OP_LERP_CLEAR;
+#else
+ free_src = src = _pixman_image_for_color (CAIRO_COLOR_WHITE);
+ if (src == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ op = PIXMAN_OP_OUT_REVERSE;
+#endif
+ } else if (op == CAIRO_OPERATOR_SOURCE) {
+#if PIXMAN_HAS_OP_LERP
+ op = PIXMAN_OP_LERP_SRC;
+#else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+ } else {
+ op = _pixman_operator (op);
+ }
+ } else {
+ op = _pixman_operator (op);
+ }
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ pixman_image_composite32 (op, src, mask, dst,
+ x1 + src_x, y1 + src_y,
+ x1 + mask_x, y1 + mask_y,
+ x1 + dst_x, y1 + dst_y,
+ x2 - x1, y2 - y1);
+ }
+ }
+
+ if (free_src)
+ pixman_image_unref (free_src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
+#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
+
+static cairo_bool_t
+line_exceeds_16_16 (const cairo_line_t *line)
+{
+ return
+ line->p1.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p1.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.y >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.y >= CAIRO_FIXED_16_16_MAX;
+}
+
+static void
+project_line_x_onto_16_16 (const cairo_line_t *line,
+ cairo_fixed_t top,
+ cairo_fixed_t bottom,
+ pixman_line_fixed_t *out)
+{
+ /* XXX use fixed-point arithmetic? */
+ cairo_point_double_t p1, p2;
+ double m;
+
+ p1.x = _cairo_fixed_to_double (line->p1.x);
+ p1.y = _cairo_fixed_to_double (line->p1.y);
+
+ p2.x = _cairo_fixed_to_double (line->p2.x);
+ p2.y = _cairo_fixed_to_double (line->p2.y);
+
+ m = (p2.x - p1.x) / (p2.y - p1.y);
+ out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+ out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+
+void
+_pixman_image_add_traps (pixman_image_t *image,
+ int dst_x, int dst_y,
+ cairo_traps_t *traps)
+{
+ cairo_trapezoid_t *t = traps->traps;
+ int num_traps = traps->num_traps;
+ while (num_traps--) {
+ pixman_trapezoid_t trap;
+
+ /* top/bottom will be clamped to surface bounds */
+ trap.top = _cairo_fixed_to_16_16 (t->top);
+ trap.bottom = _cairo_fixed_to_16_16 (t->bottom);
+
+ /* However, all the other coordinates will have been left untouched so
+ * as not to introduce numerical error. Recompute them if they
+ * exceed the 16.16 limits.
+ */
+ if (unlikely (line_exceeds_16_16 (&t->left))) {
+ project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left);
+ trap.left.p1.y = trap.top;
+ trap.left.p2.y = trap.bottom;
+ } else {
+ trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x);
+ trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y);
+ trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x);
+ trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y);
+ }
+
+ if (unlikely (line_exceeds_16_16 (&t->right))) {
+ project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right);
+ trap.right.p1.y = trap.top;
+ trap.right.p2.y = trap.bottom;
+ } else {
+ trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x);
+ trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y);
+ trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x);
+ trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y);
+ }
+
+ pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+ t++;
+ }
+}
+
+static cairo_int_status_t
+composite_traps (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
+ cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
+ pixman_image_t *mask;
+ pixman_format_code_t format;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ /* Special case adding trapezoids onto a mask surface; we want to avoid
+ * creating an intermediate temporary mask unnecessarily.
+ *
+ * We make the assumption here that the portion of the trapezoids
+ * contained within the surface is bounded by [dst_x,dst_y,width,height];
+ * the Cairo core code passes bounds based on the trapezoid extents.
+ */
+ format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
+ if (dst->pixman_format == format &&
+ (abstract_src == NULL ||
+ (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
+ {
+ _pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, traps);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ mask = pixman_image_create_bits (format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _pixman_image_add_traps (mask, extents->x, extents->y, traps);
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src->pixman_image, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+set_point (pixman_point_fixed_t *p, cairo_point_t *c)
+{
+ p->x = _cairo_fixed_to_16_16 (c->x);
+ p->y = _cairo_fixed_to_16_16 (c->y);
+}
+
+void
+_pixman_image_add_tristrip (pixman_image_t *image,
+ int dst_x, int dst_y,
+ cairo_tristrip_t *strip)
+{
+ pixman_triangle_t tri;
+ pixman_point_fixed_t *p[3] = {&tri.p1, &tri.p2, &tri.p3 };
+ int n;
+
+ set_point (p[0], &strip->points[0]);
+ set_point (p[1], &strip->points[1]);
+ set_point (p[2], &strip->points[2]);
+ pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
+ for (n = 3; n < strip->num_points; n++) {
+ set_point (p[n%3], &strip->points[n]);
+ pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
+ }
+}
+
+static cairo_int_status_t
+composite_tristrip (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_tristrip_t *strip)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
+ cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
+ pixman_image_t *mask;
+ pixman_format_code_t format;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (strip->num_points < 3)
+ return CAIRO_STATUS_SUCCESS;
+
+ format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
+ if (dst->pixman_format == format &&
+ (abstract_src == NULL ||
+ (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
+ {
+ _pixman_image_add_tristrip (dst->pixman_image, dst_x, dst_y, strip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ mask = pixman_image_create_bits (format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _pixman_image_add_tristrip (mask, extents->x, extents->y, strip);
+ pixman_image_composite32 (_pixman_operator (op),
+ src->pixman_image, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0 && HAS_PIXMAN_GLYPHS
+#if CAIRO_HAS_TG_SURFACE
+CAIRO_DEFINE_THREAD_LOCAL (pixman_glyph_cache_t *, per_thread_glyph_cache);
+#else
+static pixman_glyph_cache_t *global_glyph_cache;
+#endif
+
+static inline pixman_glyph_cache_t *
+get_glyph_cache (void)
+{
+ pixman_glyph_cache_t **glyph_cache = NULL;
+
+#if CAIRO_HAS_TG_SURFACE
+ glyph_cache = CAIRO_GET_THREAD_LOCAL (per_thread_glyph_cache);
+#else
+ glyph_cache = &global_glyph_cache;
+#endif
+
+ if (! (*glyph_cache))
+ *glyph_cache = pixman_glyph_cache_create ();
+
+ return *glyph_cache;
+}
+
+void
+_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ pixman_glyph_cache_t *glyph_cache = NULL;
+
+#if CAIRO_HAS_TG_SURFACE
+ glyph_cache = *CAIRO_GET_THREAD_LOCAL (per_thread_glyph_cache);
+#else
+ glyph_cache = global_glyph_cache;
+ CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
+#endif
+
+ if (glyph_cache) {
+ pixman_glyph_cache_remove (
+ glyph_cache, scaled_font,
+ (void *)_cairo_scaled_glyph_index (scaled_glyph));
+ }
+
+#if ! CAIRO_HAS_TG_SURFACE
+ CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
+#endif
+}
+
+static cairo_int_status_t
+composite_glyphs (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+ pixman_glyph_cache_t *glyph_cache;
+ pixman_glyph_t pglyphs_stack[CAIRO_STACK_ARRAY_LENGTH (pixman_glyph_t)];
+ pixman_glyph_t *pglyphs = pglyphs_stack;
+ pixman_glyph_t *pg;
+ int i;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+#if ! CAIRO_HAS_TG_SURFACE
+ CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
+#endif
+
+ glyph_cache = get_glyph_cache();
+ if (unlikely (glyph_cache == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto out_unlock;
+ }
+
+ pixman_glyph_cache_freeze (glyph_cache);
+
+ if (info->num_glyphs > ARRAY_LENGTH (pglyphs_stack)) {
+ pglyphs = _cairo_malloc_ab (info->num_glyphs, sizeof (pixman_glyph_t));
+ if (unlikely (pglyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto out_thaw;
+ }
+ }
+
+ pg = pglyphs;
+ for (i = 0; i < info->num_glyphs; i++) {
+ unsigned long index = info->glyphs[i].index;
+ const void *glyph;
+
+ glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)index);
+ if (!glyph) {
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_image_surface_t *glyph_surface;
+
+#if ! CAIRO_HAS_TG_SURFACE
+ /* This call can actually end up recursing, so we have to
+ * drop the mutex around it.
+ */
+ CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
+#else
+ _cairo_scaled_font_freeze_cache (info->font);
+ CAIRO_MUTEX_LOCK (_cairo_tg_scaled_glyph_mutex);
+#endif
+
+ status = _cairo_scaled_glyph_lookup (info->font, index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+#if ! CAIRO_HAS_TG_SURFACE
+ CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
+#endif
+
+ if (unlikely (status)) {
+#if CAIRO_HAS_TG_SURFACE
+ CAIRO_MUTEX_UNLOCK (_cairo_tg_scaled_glyph_mutex);
+ _cairo_scaled_font_thaw_cache (info->font);
+#endif
+ goto out_thaw;
+ }
+
+ glyph_surface = scaled_glyph->surface;
+ glyph = pixman_glyph_cache_insert (glyph_cache, info->font, (void *)index,
+ glyph_surface->base.device_transform.x0,
+ glyph_surface->base.device_transform.y0,
+ glyph_surface->pixman_image);
+
+#if CAIRO_HAS_TG_SURFACE
+ CAIRO_MUTEX_UNLOCK (_cairo_tg_scaled_glyph_mutex);
+ _cairo_scaled_font_thaw_cache (info->font);
+#endif
+
+ if (unlikely (!glyph)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto out_thaw;
+ }
+ }
+
+ pg->x = _cairo_lround (info->glyphs[i].x);
+ pg->y = _cairo_lround (info->glyphs[i].y);
+ pg->glyph = glyph;
+ pg++;
+ }
+
+ if (info->use_mask) {
+ pixman_format_code_t mask_format;
+
+ mask_format = pixman_glyph_get_mask_format (glyph_cache, pg - pglyphs, pglyphs);
+
+ pixman_composite_glyphs (_pixman_operator (op),
+ ((cairo_image_source_t *)_src)->pixman_image,
+ to_pixman_image (_dst),
+ mask_format,
+ info->extents.x + src_x, info->extents.y + src_y,
+ info->extents.x, info->extents.y,
+ info->extents.x - dst_x, info->extents.y - dst_y,
+ info->extents.width, info->extents.height,
+ glyph_cache, pg - pglyphs, pglyphs);
+ } else {
+ pixman_composite_glyphs_no_mask (_pixman_operator (op),
+ ((cairo_image_source_t *)_src)->pixman_image,
+ to_pixman_image (_dst),
+ src_x, src_y,
+ - dst_x, - dst_y,
+ glyph_cache, pg - pglyphs, pglyphs);
+ }
+
+out_thaw:
+ pixman_glyph_cache_thaw (glyph_cache);
+
+ if (pglyphs != pglyphs_stack)
+ free(pglyphs);
+
+out_unlock:
+#if ! CAIRO_HAS_TG_SURFACE
+ CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
+#endif
+
+ return status;
+}
+#else
+void
+_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+}
+
+static cairo_int_status_t
+composite_one_glyph (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info)
+{
+ cairo_image_surface_t *dst_surface = (cairo_image_surface_t *)_dst;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_status_t status;
+ int x, y;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = _cairo_scaled_glyph_lookup (info->font,
+ info->glyphs[0].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ return status;
+
+ glyph_surface = scaled_glyph->surface;
+ if (glyph_surface->width == 0 || glyph_surface->height == 0)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (glyph_surface->format == CAIRO_FORMAT_ARGB32 &&
+ dst_surface->format != CAIRO_FORMAT_ARGB32) {
+ /* FIXME: color glyph */
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+ }
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (info->glyphs[0].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (info->glyphs[0].y -
+ glyph_surface->base.device_transform.y0);
+
+
+ if (glyph_surface->format != CAIRO_FORMAT_ARGB32 ||
+ pixman_image_get_component_alpha (glyph_surface->pixman_image))
+ pixman_image_composite32 (_pixman_operator (op),
+ ((cairo_image_source_t *)_src)->pixman_image,
+ glyph_surface->pixman_image,
+ to_pixman_image (_dst),
+ x + src_x, y + src_y,
+ 0, 0,
+ x - dst_x, y - dst_y,
+ glyph_surface->width,
+ glyph_surface->height);
+ else /* color glyph */
+ pixman_image_composite32 (_pixman_operator (op),
+ glyph_surface->pixman_image,
+ NULL,
+ to_pixman_image (_dst),
+ 0, 0,
+ x + src_x, y + src_y,
+ x - dst_x, y - dst_y,
+ glyph_surface->width,
+ glyph_surface->height);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_glyphs_via_mask (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info)
+{
+ cairo_scaled_glyph_t *glyph_cache[64];
+ pixman_image_t *white = _pixman_image_for_color (CAIRO_COLOR_WHITE);
+ cairo_scaled_glyph_t *scaled_glyph;
+ uint8_t buf[2048];
+ pixman_image_t *mask;
+ pixman_format_code_t format;
+ cairo_status_t status;
+ int i;
+ cairo_bool_t component_alpha = FALSE;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (unlikely (white == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit
+ * optimised paths through pixman. Should we increase the bit
+ * depth of the target surface, we should reconsider the appropriate
+ * mask formats.
+ */
+
+ status = _cairo_scaled_glyph_lookup (info->font,
+ info->glyphs[0].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (unlikely (status)) {
+ pixman_image_unref (white);
+ return status;
+ }
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+ glyph_cache[info->glyphs[0].index % ARRAY_LENGTH (glyph_cache)] = scaled_glyph;
+
+ format = PIXMAN_a8;
+ i = (info->extents.width + 3) & ~3;
+ if (scaled_glyph->surface->base.content & CAIRO_CONTENT_COLOR) {
+ format = PIXMAN_a8r8g8b8;
+ i = info->extents.width * 4;
+ }
+
+ if (i * info->extents.height > (int) sizeof (buf)) {
+ mask = pixman_image_create_bits (format,
+ info->extents.width,
+ info->extents.height,
+ NULL, 0);
+ } else {
+ memset (buf, 0, i * info->extents.height);
+ mask = pixman_image_create_bits (format,
+ info->extents.width,
+ info->extents.height,
+ (uint32_t *)buf, i);
+ }
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (white);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ for (i = 0; i < info->num_glyphs; i++) {
+ unsigned long glyph_index = info->glyphs[i].index;
+ int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+ cairo_image_surface_t *glyph_surface;
+ int x, y;
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+ {
+ status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status)) {
+ pixman_image_unref (mask);
+ pixman_image_unref (white);
+ return status;
+ }
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ glyph_surface = scaled_glyph->surface;
+ if (! component_alpha)
+ component_alpha = pixman_image_get_component_alpha (glyph_surface->pixman_image);
+ if (glyph_surface->width && glyph_surface->height) {
+ if (glyph_surface->base.content & CAIRO_CONTENT_COLOR &&
+ format == PIXMAN_a8) {
+ pixman_image_t *ca_mask;
+
+ format = PIXMAN_a8r8g8b8;
+ ca_mask = pixman_image_create_bits (format,
+ info->extents.width,
+ info->extents.height,
+ NULL, 0);
+ if (unlikely (ca_mask == NULL)) {
+ pixman_image_unref (mask);
+ pixman_image_unref (white);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ white, mask, ca_mask,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ info->extents.width,
+ info->extents.height);
+ pixman_image_unref (mask);
+ mask = ca_mask;
+ }
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (info->glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (info->glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+
+ if (glyph_surface->pixman_format == format) {
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ glyph_surface->pixman_image, NULL, mask,
+ 0, 0,
+ 0, 0,
+ x - info->extents.x, y - info->extents.y,
+ glyph_surface->width,
+ glyph_surface->height);
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ white, glyph_surface->pixman_image, mask,
+ 0, 0,
+ 0, 0,
+ x - info->extents.x, y - info->extents.y,
+ glyph_surface->width,
+ glyph_surface->height);
+ }
+ }
+ }
+
+ if (format == PIXMAN_a8r8g8b8 && component_alpha)
+ pixman_image_set_component_alpha (mask, TRUE);
+
+ if (format != PIXMAN_a8r8g8b8 || component_alpha)
+ pixman_image_composite32 (_pixman_operator (op),
+ ((cairo_image_source_t *)_src)->pixman_image,
+ mask,
+ to_pixman_image (_dst),
+ info->extents.x + src_x, info->extents.y + src_y,
+ 0, 0,
+ info->extents.x - dst_x, info->extents.y - dst_y,
+ info->extents.width, info->extents.height);
+ else /* color glyph */
+ pixman_image_composite32 (_pixman_operator (op), mask, NULL,
+ to_pixman_image (_dst),
+ 0, 0,
+ info->extents.x + src_x, info->extents.y + src_y,
+ info->extents.x - dst_x, info->extents.y - dst_y,
+ info->extents.width, info->extents.height);
+ pixman_image_unref (mask);
+ pixman_image_unref (white);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_glyphs (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info)
+{
+ cairo_scaled_glyph_t *glyph_cache[64];
+ pixman_image_t *dst, *src;
+ cairo_status_t status;
+ int i;
+ cairo_image_surface_t *dst_surface = (cairo_image_surface_t *)_dst;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+#if CAIRO_HAS_TG_SURFACE
+ _cairo_scaled_font_freeze_cache (info->font);
+ CAIRO_MUTEX_LOCK (_cairo_tg_scaled_glyph_mutex);
+#endif
+
+ if (0 && info->num_glyphs == 1) {
+ status = composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
+ goto out_thaw;
+ }
+
+ if (0 && info->use_mask) {
+ status = composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
+ goto out_thaw;
+ }
+
+ op = _pixman_operator (op);
+ dst = to_pixman_image (_dst);
+ src = ((cairo_image_source_t *)_src)->pixman_image;
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+ status = CAIRO_STATUS_SUCCESS;
+
+ for (i = 0; i < info->num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+ unsigned long glyph_index = info->glyphs[i].index;
+ int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+ {
+ status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ break;
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ glyph_surface = scaled_glyph->surface;
+ if (glyph_surface->format == CAIRO_FORMAT_ARGB32 &&
+ dst_surface->format != CAIRO_FORMAT_ARGB32) {
+ /* FIXME: color glyph */
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+ }
+
+ if (glyph_surface->width && glyph_surface->height) {
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (info->glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (info->glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+
+ if (glyph_surface->format != CAIRO_FORMAT_ARGB32 ||
+ pixman_image_get_component_alpha (glyph_surface->pixman_image))
+ pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst,
+ x + src_x, y + src_y,
+ 0, 0,
+ x - dst_x, y - dst_y,
+ glyph_surface->width,
+ glyph_surface->height);
+ else /* Color glyph. */
+ pixman_image_composite32 (op, glyph_surface->pixman_image, NULL, dst,
+ 0, 0,
+ x + src_x, y + src_y,
+ x - dst_x, y - dst_y,
+ glyph_surface->width,
+ glyph_surface->height);
+ }
+ }
+
+out_thaw:
+#if CAIRO_HAS_TG_SURFACE
+ _cairo_scaled_font_thaw_cache (info->font);
+ CAIRO_MUTEX_UNLOCK (_cairo_tg_scaled_glyph_mutex);
+#endif
+
+ return status;
+}
+#endif
+
+static cairo_int_status_t
+check_composite (const cairo_composite_rectangles_t *extents)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_compositor_t *
+_cairo_image_traps_compositor_get (void)
+{
+ static cairo_traps_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_traps_compositor_init (&compositor,
+ &__cairo_no_compositor);
+ compositor.acquire = acquire;
+ compositor.release = release;
+ compositor.set_clip_region = set_clip_region;
+ compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
+ compositor.draw_image_boxes = draw_image_boxes;
+ //compositor.copy_boxes = copy_boxes;
+ compositor.fill_boxes = fill_boxes;
+ compositor.check_composite = check_composite;
+ compositor.composite = composite;
+ compositor.lerp = lerp;
+ compositor.lerp_color_glyph = lerp_color_glyph;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ //compositor.check_composite_traps = check_composite_traps;
+ compositor.composite_traps = composite_traps;
+ //compositor.check_composite_tristrip = check_composite_traps;
+ compositor.composite_tristrip = composite_tristrip;
+ compositor.check_composite_glyphs = check_composite_glyphs;
+ compositor.composite_glyphs = composite_glyphs;
+ }
+
+ return &compositor.base;
+}
+
+const cairo_compositor_t *
+_cairo_image_mask_compositor_get (void)
+{
+ static cairo_mask_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_mask_compositor_init (&compositor,
+ _cairo_image_traps_compositor_get ());
+ compositor.acquire = acquire;
+ compositor.release = release;
+ compositor.set_clip_region = set_clip_region;
+ compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
+ compositor.draw_image_boxes = draw_image_boxes;
+ compositor.fill_rectangles = fill_rectangles;
+ compositor.fill_boxes = fill_boxes;
+ //compositor.check_composite = check_composite;
+ compositor.composite = composite;
+ //compositor.lerp = lerp;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ compositor.check_composite_glyphs = check_composite_glyphs;
+ compositor.composite_glyphs = composite_glyphs;
+ }
+
+ return &compositor.base;
+}
+
+#if PIXMAN_HAS_COMPOSITOR
+typedef struct _cairo_image_span_renderer {
+ cairo_span_renderer_t base;
+
+ pixman_image_compositor_t *compositor;
+ pixman_image_t *src, *mask;
+ float opacity;
+ cairo_rectangle_int_t extents;
+} cairo_image_span_renderer_t;
+COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t));
+
+static cairo_status_t
+_cairo_image_bounded_opaque_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage)
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ assert (y + height <= r->extents.height);
+ if (y > r->extents.y) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, r->extents.y,
+ r->extents.width, y - r->extents.y,
+ 0);
+ }
+
+ if (num_spans == 0) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, y,
+ r->extents.width, height,
+ 0);
+ } else {
+ if (spans[0].x != r->extents.x) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, y,
+ spans[0].x - r->extents.x,
+ height,
+ 0);
+ }
+
+ do {
+ assert (spans[0].x < r->extents.x + r->extents.width);
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != r->extents.x + r->extents.width) {
+ assert (spans[0].x < r->extents.x + r->extents.width);
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ r->extents.x + r->extents.width - spans[0].x, height,
+ 0);
+ }
+ }
+
+ r->extents.y = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_clipped_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ assert (num_spans);
+
+ do {
+ if (! spans[0].inverse)
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ r->extents.y = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_finish_unbounded_spans (void *abstract_renderer)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (r->extents.y < r->extents.height) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, r->extents.y,
+ r->extents.width,
+ r->extents.height - r->extents.y,
+ 0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+span_renderer_init (cairo_abstract_span_renderer_t *_r,
+ const cairo_composite_rectangles_t *composite,
+ cairo_bool_t needs_clip)
+{
+ cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
+ const cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_operator_t op = composite->op;
+ int src_x, src_y;
+ int mask_x, mask_y;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ op = PIXMAN_OP_LERP_CLEAR;
+ } else if (dst->base.is_clear &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ op == CAIRO_OPERATOR_OVER ||
+ op == CAIRO_OPERATOR_ADD)) {
+ op = PIXMAN_OP_SRC;
+ } else if (op == CAIRO_OPERATOR_SOURCE) {
+ op = PIXMAN_OP_LERP_SRC;
+ } else {
+ op = _pixman_operator (op);
+ }
+
+ r->compositor = NULL;
+ r->mask = NULL;
+ r->src = _pixman_image_for_pattern (dst, source, FALSE,
+ &composite->unbounded,
+ &composite->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (r->src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ r->opacity = 1.0;
+ if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+ r->opacity = composite->mask_pattern.solid.color.alpha;
+ } else {
+ r->mask = _pixman_image_for_pattern (dst,
+ &composite->mask_pattern.base,
+ TRUE,
+ &composite->unbounded,
+ &composite->mask_sample_area,
+ &mask_x, &mask_y);
+ if (unlikely (r->mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* XXX Component-alpha? */
+ if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 &&
+ _cairo_pattern_is_opaque (source, &composite->source_sample_area))
+ {
+ pixman_image_unref (r->src);
+ r->src = r->mask;
+ src_x = mask_x;
+ src_y = mask_y;
+ r->mask = NULL;
+ }
+ }
+
+ if (composite->is_bounded) {
+ if (r->opacity == 1.)
+ r->base.render_rows = _cairo_image_bounded_opaque_spans;
+ else
+ r->base.render_rows = _cairo_image_bounded_spans;
+ r->base.finish = NULL;
+ } else {
+ if (needs_clip)
+ r->base.render_rows = _cairo_image_clipped_spans;
+ else
+ r->base.render_rows = _cairo_image_unbounded_spans;
+ r->base.finish = _cairo_image_finish_unbounded_spans;
+ r->extents = composite->unbounded;
+ r->extents.height += r->extents.y;
+ }
+
+ r->compositor =
+ pixman_image_create_compositor (op, r->src, r->mask, dst->pixman_image,
+ composite->unbounded.x + src_x,
+ composite->unbounded.y + src_y,
+ composite->unbounded.x + mask_x,
+ composite->unbounded.y + mask_y,
+ composite->unbounded.x,
+ composite->unbounded.y,
+ composite->unbounded.width,
+ composite->unbounded.height);
+ if (unlikely (r->compositor == NULL))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+ cairo_int_status_t status)
+{
+ cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && r->base.finish)
+ r->base.finish (r);
+
+ if (r->compositor)
+ pixman_image_compositor_destroy (r->compositor);
+
+ if (r->src)
+ pixman_image_unref (r->src);
+ if (r->mask)
+ pixman_image_unref (r->mask);
+}
+#else
+typedef struct _cairo_image_span_renderer {
+ cairo_span_renderer_t base;
+
+ const cairo_composite_rectangles_t *composite;
+
+ float opacity;
+ uint8_t op;
+ int bpp;
+
+ pixman_image_t *src, *mask;
+ union {
+ struct fill {
+ int stride;
+ uint8_t *data;
+ uint32_t pixel;
+ } fill;
+ struct blit {
+ int stride;
+ uint8_t *data;
+ int src_stride;
+ uint8_t *src_data;
+ } blit;
+ struct composite {
+ pixman_image_t *dst;
+ int src_x, src_y;
+ int mask_x, mask_y;
+ int run_length;
+ } composite;
+ struct finish {
+ cairo_rectangle_int_t extents;
+ int src_x, src_y;
+ int stride;
+ uint8_t *data;
+ } mask;
+ } u;
+ uint8_t _buf[0];
+#define SZ_BUF (sizeof (cairo_abstract_span_renderer_t) - sizeof (cairo_image_span_renderer_t))
+} cairo_image_span_renderer_t;
+COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t));
+
+static cairo_status_t
+_cairo_image_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+ uint8_t *mask, *row;
+ int len;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ mask = r->u.mask.data + (y - r->u.mask.extents.y) * r->u.mask.stride;
+ mask += spans[0].x - r->u.mask.extents.x;
+ row = mask;
+
+ do {
+ len = spans[1].x - spans[0].x;
+ if (spans[0].coverage) {
+ *row++ = r->opacity * spans[0].coverage;
+ if (--len)
+ memset (row, row[-1], len);
+ }
+ row += len;
+ spans++;
+ } while (--num_spans > 1);
+
+ len = row - mask;
+ row = mask;
+ while (--height) {
+ mask += r->u.mask.stride;
+ memcpy (mask, row, len);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_spans_and_zero (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+ uint8_t *mask;
+ int len;
+
+ mask = r->u.mask.data;
+ if (y > r->u.mask.extents.y) {
+ len = (y - r->u.mask.extents.y) * r->u.mask.stride;
+ memset (mask, 0, len);
+ mask += len;
+ }
+
+ r->u.mask.extents.y = y + height;
+ r->u.mask.data = mask + height * r->u.mask.stride;
+ if (num_spans == 0) {
+ memset (mask, 0, height * r->u.mask.stride);
+ } else {
+ uint8_t *row = mask;
+
+ if (spans[0].x != r->u.mask.extents.x) {
+ len = spans[0].x - r->u.mask.extents.x;
+ memset (row, 0, len);
+ row += len;
+ }
+
+ do {
+ len = spans[1].x - spans[0].x;
+ *row++ = r->opacity * spans[0].coverage;
+ if (len > 1) {
+ memset (row, row[-1], --len);
+ row += len;
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != r->u.mask.extents.x + r->u.mask.extents.width) {
+ len = r->u.mask.extents.x + r->u.mask.extents.width - spans[0].x;
+ memset (row, 0, len);
+ }
+
+ row = mask;
+ while (--height) {
+ mask += r->u.mask.stride;
+ memcpy (mask, row, r->u.mask.extents.width);
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_finish_spans_and_zero (void *abstract_renderer)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (r->u.mask.extents.y < r->u.mask.extents.height)
+ memset (r->u.mask.data, 0, (r->u.mask.extents.height - r->u.mask.extents.y) * r->u.mask.stride);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_fill8_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ do {
+ if (spans[0].coverage) {
+ int len = spans[1].x - spans[0].x;
+ uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x;
+ if (len == 1)
+ *d = r->u.fill.pixel;
+ else
+ memset(d, r->u.fill.pixel, len);
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ if (spans[0].coverage) {
+ int yy = y, hh = h;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
+ if (len == 1)
+ *d = r->u.fill.pixel;
+ else
+ memset(d, r->u.fill.pixel, len);
+ yy++;
+ } while (--hh);
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_fill16_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ do {
+ if (spans[0].coverage) {
+ int len = spans[1].x - spans[0].x;
+ uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*2);
+ while (len--)
+ *d++ = r->u.fill.pixel;
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ if (spans[0].coverage) {
+ int yy = y, hh = h;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*2);
+ while (len--)
+ *d++ = r->u.fill.pixel;
+ yy++;
+ } while (--hh);
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_fill32_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ do {
+ if (spans[0].coverage) {
+ int len = spans[1].x - spans[0].x;
+ if (len > 32) {
+ pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp,
+ spans[0].x, y, len, 1, r->u.fill.pixel);
+ } else {
+ uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
+ while (len--)
+ *d++ = r->u.fill.pixel;
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ if (spans[0].coverage) {
+ if (spans[1].x - spans[0].x > 16) {
+ pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp,
+ spans[0].x, y, spans[1].x - spans[0].x, h,
+ r->u.fill.pixel);
+ } else {
+ int yy = y, hh = h;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
+ while (len--)
+ *d++ = r->u.fill.pixel;
+ yy++;
+ } while (--hh);
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0
+static cairo_status_t
+_fill_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ pixman_fill ((uint32_t *) r->data, r->stride, r->bpp,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, h,
+ r->pixel);
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+static cairo_status_t
+_blit_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+ int cpp;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ cpp = r->bpp/8;
+ if (likely (h == 1)) {
+ uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride;
+ uint8_t *dst = r->u.blit.data + y*r->u.blit.stride;
+ do {
+ if (spans[0].coverage) {
+ void *s = src + spans[0].x*cpp;
+ void *d = dst + spans[0].x*cpp;
+ int len = (spans[1].x - spans[0].x) * cpp;
+ switch (len) {
+ case 1:
+ *(uint8_t *)d = *(uint8_t *)s;
+ break;
+ case 2:
+ *(uint16_t *)d = *(uint16_t *)s;
+ break;
+ case 4:
+ *(uint32_t *)d = *(uint32_t *)s;
+ break;
+#if HAVE_UINT64_T
+ case 8:
+ *(uint64_t *)d = *(uint64_t *)s;
+ break;
+#endif
+ default:
+ memcpy(d, s, len);
+ break;
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ if (spans[0].coverage) {
+ int yy = y, hh = h;
+ do {
+ void *src = r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x*cpp;
+ void *dst = r->u.blit.data + yy*r->u.blit.stride + spans[0].x*cpp;
+ int len = (spans[1].x - spans[0].x) * cpp;
+ switch (len) {
+ case 1:
+ *(uint8_t *)dst = *(uint8_t *)src;
+ break;
+ case 2:
+ *(uint16_t *)dst = *(uint16_t *)src;
+ break;
+ case 4:
+ *(uint32_t *)dst = *(uint32_t *)src;
+ break;
+#if HAVE_UINT64_T
+ case 8:
+ *(uint64_t *)dst = *(uint64_t *)src;
+ break;
+#endif
+ default:
+ memcpy(dst, src, len);
+ break;
+ }
+ yy++;
+ } while (--hh);
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_mono_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ pixman_image_composite32 (r->op,
+ r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
+ 0, 0,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, h);
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_mono_unbounded_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0) {
+ pixman_image_composite32 (PIXMAN_OP_CLEAR,
+ r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
+ 0, 0,
+ r->composite->unbounded.x, y,
+ r->composite->unbounded.width, h);
+ r->u.composite.mask_y = y + h;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (y != r->u.composite.mask_y) {
+ pixman_image_composite32 (PIXMAN_OP_CLEAR,
+ r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
+ 0, 0,
+ r->composite->unbounded.x, r->u.composite.mask_y,
+ r->composite->unbounded.width, y - r->u.composite.mask_y);
+ }
+
+ if (spans[0].x != r->composite->unbounded.x) {
+ pixman_image_composite32 (PIXMAN_OP_CLEAR,
+ r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
+ 0, 0,
+ r->composite->unbounded.x, y,
+ spans[0].x - r->composite->unbounded.x, h);
+ }
+
+ do {
+ int op = spans[0].coverage ? r->op : PIXMAN_OP_CLEAR;
+ pixman_image_composite32 (op,
+ r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
+ 0, 0,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, h);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != r->composite->unbounded.x + r->composite->unbounded.width) {
+ pixman_image_composite32 (PIXMAN_OP_CLEAR,
+ r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
+ 0, 0,
+ spans[0].x, y,
+ r->composite->unbounded.x + r->composite->unbounded.width - spans[0].x, h);
+ }
+
+ r->u.composite.mask_y = y + h;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_mono_finish_unbounded_spans (void *abstract_renderer)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (r->u.composite.mask_y < r->composite->unbounded.y + r->composite->unbounded.height) {
+ pixman_image_composite32 (PIXMAN_OP_CLEAR,
+ r->src, NULL, r->u.composite.dst,
+ r->composite->unbounded.x + r->u.composite.src_x, r->u.composite.mask_y + r->u.composite.src_y,
+ 0, 0,
+ r->composite->unbounded.x, r->u.composite.mask_y,
+ r->composite->unbounded.width,
+ r->composite->unbounded.y + r->composite->unbounded.height - r->u.composite.mask_y);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+mono_renderer_init (cairo_image_span_renderer_t *r,
+ const cairo_composite_rectangles_t *composite,
+ cairo_antialias_t antialias,
+ cairo_bool_t needs_clip)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ composite->source_pattern.base.filter == CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ composite->mask_pattern.base.filter == CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (!_cairo_pattern_is_opaque_solid (&composite->mask_pattern.base))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ r->base.render_rows = NULL;
+ if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_color_t *color;
+
+ color = &composite->source_pattern.solid.color;
+ if (composite->op == CAIRO_OPERATOR_CLEAR)
+ color = CAIRO_COLOR_TRANSPARENT;
+
+ if (fill_reduces_to_source (composite->op, color, dst) &&
+ color_to_pixel (color, dst->pixman_format, &r->u.fill.pixel)) {
+ /* Use plain C for the fill operations as the span length is
+ * typically small, too small to payback the startup overheads of
+ * using SSE2 etc.
+ */
+ switch (PIXMAN_FORMAT_BPP(dst->pixman_format)) {
+ case 8: r->base.render_rows = _fill8_spans; break;
+ case 16: r->base.render_rows = _fill16_spans; break;
+ case 32: r->base.render_rows = _fill32_spans; break;
+ default: break;
+ }
+ r->u.fill.data = dst->data;
+ r->u.fill.stride = dst->stride;
+ }
+ } else if ((composite->op == CAIRO_OPERATOR_SOURCE ||
+ (composite->op == CAIRO_OPERATOR_OVER &&
+ (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) &&
+ composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE &&
+ to_image_surface(composite->source_pattern.surface.surface)->format == dst->format)
+ {
+ cairo_image_surface_t *src =
+ to_image_surface(composite->source_pattern.surface.surface);
+ int tx, ty;
+
+ if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix,
+ &tx, &ty) &&
+ composite->bounded.x + tx >= 0 &&
+ composite->bounded.y + ty >= 0 &&
+ composite->bounded.x + composite->bounded.width + tx <= src->width &&
+ composite->bounded.y + composite->bounded.height + ty <= src->height) {
+
+ r->u.blit.stride = dst->stride;
+ r->u.blit.data = dst->data;
+ r->u.blit.src_stride = src->stride;
+ r->u.blit.src_data = src->data + src->stride * ty + tx * 4;
+ r->base.render_rows = _blit_spans;
+ }
+ }
+
+ if (r->base.render_rows == NULL) {
+ r->src = _pixman_image_for_pattern (dst, &composite->source_pattern.base, FALSE,
+ &composite->unbounded,
+ &composite->source_sample_area,
+ &r->u.composite.src_x, &r->u.composite.src_y);
+ if (unlikely (r->src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ r->u.composite.dst = to_pixman_image (composite->surface);
+ r->op = _pixman_operator (composite->op);
+ if (composite->is_bounded == 0) {
+ r->base.render_rows = _mono_unbounded_spans;
+ r->base.finish = _mono_finish_unbounded_spans;
+ r->u.composite.mask_y = composite->unbounded.y;
+ } else
+ r->base.render_rows = _mono_spans;
+ }
+ r->bpp = PIXMAN_FORMAT_BPP(dst->pixman_format);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+#define ONE_HALF 0x7f
+#define RB_MASK 0x00ff00ff
+#define RB_ONE_HALF 0x007f007f
+#define RB_MASK_PLUS_ONE 0x01000100
+#define G_SHIFT 8
+static inline uint32_t
+mul8x2_8 (uint32_t a, uint8_t b)
+{
+ uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF;
+ return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK;
+}
+
+static inline uint32_t
+add8x2_8x2 (uint32_t a, uint32_t b)
+{
+ uint32_t t = a + b;
+ t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK);
+ return t & RB_MASK;
+}
+
+static inline uint8_t
+mul8_8 (uint8_t a, uint8_t b)
+{
+ uint16_t t = a * (uint16_t)b + ONE_HALF;
+ return ((t >> G_SHIFT) + t) >> G_SHIFT;
+}
+
+static inline uint32_t
+lerp8x4 (uint32_t src, uint8_t a, uint32_t dst)
+{
+ return (add8x2_8x2 (mul8x2_8 (src, a),
+ mul8x2_8 (dst, ~a)) |
+ add8x2_8x2 (mul8x2_8 (src >> G_SHIFT, a),
+ mul8x2_8 (dst >> G_SHIFT, ~a)) << G_SHIFT);
+}
+
+static cairo_status_t
+_fill_a8_lerp_opaque_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ uint8_t *d = r->u.fill.data + r->u.fill.stride*y;
+ do {
+ uint8_t a = spans[0].coverage;
+ if (a) {
+ int len = spans[1].x - spans[0].x;
+ if (a == 0xff) {
+ memset(d + spans[0].x, r->u.fill.pixel, len);
+ } else {
+ uint8_t s = mul8_8(a, r->u.fill.pixel);
+ uint8_t *dst = d + spans[0].x;
+ a = ~a;
+ while (len--) {
+ uint8_t t = mul8_8(*dst, a);
+ *dst++ = t + s;
+ }
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ uint8_t a = spans[0].coverage;
+ if (a) {
+ int yy = y, hh = h;
+ if (a == 0xff) {
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
+ memset(d, r->u.fill.pixel, len);
+ yy++;
+ } while (--hh);
+ } else {
+ uint8_t s = mul8_8(a, r->u.fill.pixel);
+ a = ~a;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
+ while (len--) {
+ uint8_t t = mul8_8(*d, a);
+ *d++ = t + s;
+ }
+ yy++;
+ } while (--hh);
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_fill_xrgb32_lerp_opaque_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ do {
+ uint8_t a = spans[0].coverage;
+ if (a) {
+ int len = spans[1].x - spans[0].x;
+ uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
+ if (a == 0xff) {
+ if (len > 31) {
+ pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32,
+ spans[0].x, y, len, 1, r->u.fill.pixel);
+ } else {
+ uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
+ while (len--)
+ *d++ = r->u.fill.pixel;
+ }
+ } else while (len--) {
+ *d = lerp8x4 (r->u.fill.pixel, a, *d);
+ d++;
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ uint8_t a = spans[0].coverage;
+ if (a) {
+ if (a == 0xff) {
+ if (spans[1].x - spans[0].x > 16) {
+ pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32,
+ spans[0].x, y, spans[1].x - spans[0].x, h,
+ r->u.fill.pixel);
+ } else {
+ int yy = y, hh = h;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
+ while (len--)
+ *d++ = r->u.fill.pixel;
+ yy++;
+ } while (--hh);
+ }
+ } else {
+ int yy = y, hh = h;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
+ while (len--) {
+ *d = lerp8x4 (r->u.fill.pixel, a, *d);
+ d++;
+ }
+ yy++;
+ } while (--hh);
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_fill_a8_lerp_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ do {
+ uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
+ if (a) {
+ int len = spans[1].x - spans[0].x;
+ uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x;
+ uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f;
+ uint16_t ia = ~a;
+ while (len--) {
+ uint16_t t = *d*ia + p;
+ *d++ = (t + (t>>8)) >> 8;
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
+ if (a) {
+ int yy = y, hh = h;
+ uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f;
+ uint16_t ia = ~a;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
+ while (len--) {
+ uint16_t t = *d*ia + p;
+ *d++ = (t + (t>>8)) >> 8;
+ }
+ yy++;
+ } while (--hh);
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ do {
+ uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
+ if (a) {
+ int len = spans[1].x - spans[0].x;
+ uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
+ while (len--) {
+ *d = lerp8x4 (r->u.fill.pixel, a, *d);
+ d++;
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
+ if (a) {
+ int yy = y, hh = h;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
+ while (len--) {
+ *d = lerp8x4 (r->u.fill.pixel, a, *d);
+ d++;
+ }
+ yy++;
+ } while (--hh);
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_blit_xrgb32_lerp_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely(h == 1)) {
+ uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride;
+ uint8_t *dst = r->u.blit.data + y*r->u.blit.stride;
+ do {
+ uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
+ if (a) {
+ uint32_t *s = (uint32_t*)src + spans[0].x;
+ uint32_t *d = (uint32_t*)dst + spans[0].x;
+ int len = spans[1].x - spans[0].x;
+ if (a == 0xff) {
+ if (len == 1)
+ *d = *s;
+ else
+ memcpy(d, s, len*4);
+ } else {
+ while (len--) {
+ *d = lerp8x4 (*s, a, *d);
+ s++, d++;
+ }
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+ } else {
+ do {
+ uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
+ if (a) {
+ int yy = y, hh = h;
+ do {
+ uint32_t *s = (uint32_t *)(r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x * 4);
+ uint32_t *d = (uint32_t *)(r->u.blit.data + yy*r->u.blit.stride + spans[0].x * 4);
+ int len = spans[1].x - spans[0].x;
+ if (a == 0xff) {
+ if (len == 1)
+ *d = *s;
+ else
+ memcpy(d, s, len * 4);
+ } else {
+ while (len--) {
+ *d = lerp8x4 (*s, a, *d);
+ s++, d++;
+ }
+ }
+ yy++;
+ } while (--hh);
+ }
+ spans++;
+ } while (--num_spans > 1);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_inplace_spans (void *abstract_renderer,
+ int y, int h,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+ uint8_t *mask;
+ int x0, x1;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (num_spans == 2 && spans[0].coverage == 0xff) {
+ pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, h);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ mask = (uint8_t *)pixman_image_get_data (r->mask);
+ x1 = x0 = spans[0].x;
+ do {
+ int len = spans[1].x - spans[0].x;
+ if (mask == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ *mask++ = spans[0].coverage;
+ if (len > 1) {
+ if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) {
+ if (x1 != x0) {
+ pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ x1 - x0, h);
+ }
+ pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ spans[0].x, y,
+ len, h);
+ mask = (uint8_t *)pixman_image_get_data (r->mask);
+ x0 = spans[1].x;
+ } else if (spans[0].coverage == 0x0 &&
+ x1 - x0 > r->u.composite.run_length) {
+ pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ x1 - x0, h);
+ mask = (uint8_t *)pixman_image_get_data (r->mask);
+ x0 = spans[1].x;
+ }else {
+ memset (mask, spans[0].coverage, --len);
+ mask += len;
+ }
+ }
+ x1 = spans[1].x;
+ spans++;
+ } while (--num_spans > 1);
+
+ if (x1 != x0) {
+ pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ x1 - x0, h);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_inplace_opacity_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+ uint8_t *mask;
+ int x0, x1;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ mask = (uint8_t *)pixman_image_get_data (r->mask);
+ x1 = x0 = spans[0].x;
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint8_t m = mul8_8(spans[0].coverage, r->bpp);
+ if (mask == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ *mask++ = m;
+ if (len > 1) {
+ if (m == 0 &&
+ x1 - x0 > r->u.composite.run_length) {
+ pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ x1 - x0, h);
+ mask = (uint8_t *)pixman_image_get_data (r->mask);
+ x0 = spans[1].x;
+ }else {
+ memset (mask, m, --len);
+ mask += len;
+ }
+ }
+ x1 = spans[1].x;
+ spans++;
+ } while (--num_spans > 1);
+
+ if (x1 != x0) {
+ pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ x1 - x0, h);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_inplace_src_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+ uint8_t *m;
+ int x0;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ x0 = spans[0].x;
+ m = r->_buf;
+ do {
+ int len = spans[1].x - spans[0].x;
+ if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) {
+ if (spans[0].x != x0) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#else
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ r->mask, NULL, r->u.composite.dst,
+ 0, 0,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#endif
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ r->src, NULL, r->u.composite.dst,
+ spans[0].x + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, h);
+
+ m = r->_buf;
+ x0 = spans[1].x;
+ } else if (spans[0].coverage == 0x0) {
+ if (spans[0].x != x0) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#else
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ r->mask, NULL, r->u.composite.dst,
+ 0, 0,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#endif
+ }
+
+ m = r->_buf;
+ x0 = spans[1].x;
+ } else {
+ *m++ = spans[0].coverage;
+ if (len > 1) {
+ memset (m, spans[0].coverage, --len);
+ m += len;
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != x0) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#else
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ r->mask, NULL, r->u.composite.dst,
+ 0, 0,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#endif
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_inplace_src_opacity_spans (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
+ uint8_t *mask;
+ int x0;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ x0 = spans[0].x;
+ mask = (uint8_t *)pixman_image_get_data (r->mask);
+ do {
+ int len = spans[1].x - spans[0].x;
+ uint8_t m = mul8_8(spans[0].coverage, r->bpp);
+ if (m == 0) {
+ if (spans[0].x != x0) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#else
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ r->mask, NULL, r->u.composite.dst,
+ 0, 0,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#endif
+ }
+
+ mask = (uint8_t *)pixman_image_get_data (r->mask);
+ x0 = spans[1].x;
+ } else {
+ if (mask == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ *mask++ = m;
+ if (len > 1) {
+ memset (mask, m, --len);
+ mask += len;
+ }
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != x0) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#else
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ r->mask, NULL, r->u.composite.dst,
+ 0, 0,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ r->src, r->mask, r->u.composite.dst,
+ x0 + r->u.composite.src_x,
+ y + r->u.composite.src_y,
+ 0, 0,
+ x0, y,
+ spans[0].x - x0, h);
+#endif
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void free_pixels (pixman_image_t *image, void *data)
+{
+ free (data);
+}
+
+static cairo_int_status_t
+inplace_renderer_init (cairo_image_span_renderer_t *r,
+ const cairo_composite_rectangles_t *composite,
+ cairo_antialias_t antialias,
+ cairo_bool_t needs_clip)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
+ uint8_t *buf;
+
+ if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ composite->source_pattern.base.filter == CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ composite->mask_pattern.base.filter == CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ r->base.render_rows = NULL;
+ r->bpp = composite->mask_pattern.solid.color.alpha_short >> 8;
+
+ if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_color_t *color;
+
+ color = &composite->source_pattern.solid.color;
+ if (composite->op == CAIRO_OPERATOR_CLEAR)
+ color = CAIRO_COLOR_TRANSPARENT;
+
+ if (fill_reduces_to_source (composite->op, color, dst) &&
+ color_to_pixel (color, dst->pixman_format, &r->u.fill.pixel)) {
+ /* Use plain C for the fill operations as the span length is
+ * typically small, too small to payback the startup overheads of
+ * using SSE2 etc.
+ */
+ if (r->bpp == 0xff) {
+ switch (dst->format) {
+ case CAIRO_FORMAT_A8:
+ r->base.render_rows = _fill_a8_lerp_opaque_spans;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_ARGB32:
+ r->base.render_rows = _fill_xrgb32_lerp_opaque_spans;
+ break;
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_INVALID:
+ default: break;
+ }
+ } else {
+ switch (dst->format) {
+ case CAIRO_FORMAT_A8:
+ r->base.render_rows = _fill_a8_lerp_spans;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_ARGB32:
+ r->base.render_rows = _fill_xrgb32_lerp_spans;
+ break;
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_INVALID:
+ default: break;
+ }
+ }
+ r->u.fill.data = dst->data;
+ r->u.fill.stride = dst->stride;
+ }
+ } else if ((dst->format == CAIRO_FORMAT_ARGB32 || dst->format == CAIRO_FORMAT_RGB24) &&
+ (composite->op == CAIRO_OPERATOR_SOURCE ||
+ (composite->op == CAIRO_OPERATOR_OVER &&
+ (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) &&
+ composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE &&
+ to_image_surface(composite->source_pattern.surface.surface)->format == dst->format)
+ {
+ cairo_image_surface_t *src =
+ to_image_surface(composite->source_pattern.surface.surface);
+ int tx, ty;
+
+ if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix,
+ &tx, &ty) &&
+ composite->bounded.x + tx >= 0 &&
+ composite->bounded.y + ty >= 0 &&
+ composite->bounded.x + composite->bounded.width + tx <= src->width &&
+ composite->bounded.y + composite->bounded.height + ty <= src->height) {
+
+ assert(PIXMAN_FORMAT_BPP(dst->pixman_format) == 32);
+ r->u.blit.stride = dst->stride;
+ r->u.blit.data = dst->data;
+ r->u.blit.src_stride = src->stride;
+ r->u.blit.src_data = src->data + src->stride * ty + tx * 4;
+ r->base.render_rows = _blit_xrgb32_lerp_spans;
+ }
+ }
+ if (r->base.render_rows == NULL) {
+ const cairo_pattern_t *src = &composite->source_pattern.base;
+ unsigned int width;
+
+ if (composite->is_bounded == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ r->base.render_rows = r->bpp == 0xff ? _inplace_spans : _inplace_opacity_spans;
+ width = (composite->bounded.width + 3) & ~3;
+
+ r->u.composite.run_length = 8;
+ if (src->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ src->type == CAIRO_PATTERN_TYPE_RADIAL)
+ r->u.composite.run_length = 256;
+ if (dst->base.is_clear &&
+ (composite->op == CAIRO_OPERATOR_SOURCE ||
+ composite->op == CAIRO_OPERATOR_OVER ||
+ composite->op == CAIRO_OPERATOR_ADD)) {
+ r->op = PIXMAN_OP_SRC;
+ } else if (composite->op == CAIRO_OPERATOR_SOURCE) {
+ r->base.render_rows = r->bpp == 0xff ? _inplace_src_spans : _inplace_src_opacity_spans;
+ r->u.composite.mask_y = r->composite->unbounded.y;
+ width = (composite->unbounded.width + 3) & ~3;
+ } else if (composite->op == CAIRO_OPERATOR_CLEAR) {
+ r->op = PIXMAN_OP_OUT_REVERSE;
+ src = NULL;
+ } else {
+ r->op = _pixman_operator (composite->op);
+ }
+
+ r->src = _pixman_image_for_pattern (dst, src, FALSE,
+ &composite->bounded,
+ &composite->source_sample_area,
+ &r->u.composite.src_x, &r->u.composite.src_y);
+ if (unlikely (r->src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* Create an effectively unbounded mask by repeating the single line */
+ buf = r->_buf;
+ if (width > SZ_BUF) {
+ buf = malloc (width);
+ if (unlikely (buf == NULL)) {
+ pixman_image_unref (r->src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+ r->mask = pixman_image_create_bits (PIXMAN_a8,
+ width, composite->unbounded.height,
+ (uint32_t *)buf, 0);
+ if (unlikely (r->mask == NULL)) {
+ pixman_image_unref (r->src);
+ if (buf != r->_buf)
+ free (buf);
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (buf != r->_buf)
+ pixman_image_set_destroy_function (r->mask, free_pixels, buf);
+
+ r->u.composite.dst = dst->pixman_image;
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+span_renderer_init (cairo_abstract_span_renderer_t *_r,
+ const cairo_composite_rectangles_t *composite,
+ cairo_antialias_t antialias,
+ cairo_bool_t needs_clip)
+{
+ cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
+ const cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_operator_t op = composite->op;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s: antialias=%d, needs_clip=%d\n", __FUNCTION__,
+ antialias, needs_clip));
+
+ if (needs_clip)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ r->composite = composite;
+ r->mask = NULL;
+ r->src = NULL;
+ r->base.finish = NULL;
+
+ status = mono_renderer_init (r, composite, antialias, needs_clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = inplace_renderer_init (r, composite, antialias, needs_clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ r->bpp = 0;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+#if PIXMAN_HAS_OP_LERP
+ op = PIXMAN_OP_LERP_CLEAR;
+#else
+ source = &_cairo_pattern_white.base;
+ op = PIXMAN_OP_OUT_REVERSE;
+#endif
+ } else if (dst->base.is_clear &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ op == CAIRO_OPERATOR_OVER ||
+ op == CAIRO_OPERATOR_ADD)) {
+ op = PIXMAN_OP_SRC;
+ } else if (op == CAIRO_OPERATOR_SOURCE) {
+ if (_cairo_pattern_is_opaque (&composite->source_pattern.base,
+ &composite->source_sample_area))
+ {
+ op = PIXMAN_OP_OVER;
+ }
+ else
+ {
+#if PIXMAN_HAS_OP_LERP
+ op = PIXMAN_OP_LERP_SRC;
+#else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+ }
+ } else {
+ op = _pixman_operator (op);
+ }
+ r->op = op;
+
+ r->src = _pixman_image_for_pattern (dst, source, FALSE,
+ &composite->unbounded,
+ &composite->source_sample_area,
+ &r->u.mask.src_x, &r->u.mask.src_y);
+ if (unlikely (r->src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ r->opacity = 1.0;
+ if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+ r->opacity = composite->mask_pattern.solid.color.alpha;
+ } else {
+ pixman_image_t *mask;
+ int mask_x, mask_y;
+
+ mask = _pixman_image_for_pattern (dst,
+ &composite->mask_pattern.base,
+ TRUE,
+ &composite->unbounded,
+ &composite->mask_sample_area,
+ &mask_x, &mask_y);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* XXX Component-alpha? */
+ if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 &&
+ _cairo_pattern_is_opaque (source, &composite->source_sample_area))
+ {
+ pixman_image_unref (r->src);
+ r->src = mask;
+ r->u.mask.src_x = mask_x;
+ r->u.mask.src_y = mask_y;
+ mask = NULL;
+ }
+
+ if (mask) {
+ pixman_image_unref (mask);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ r->u.mask.extents = composite->unbounded;
+ r->u.mask.stride = (r->u.mask.extents.width + 3) & ~3;
+ if (r->u.mask.extents.height * r->u.mask.stride > (int)sizeof (r->_buf)) {
+ r->mask = pixman_image_create_bits (PIXMAN_a8,
+ r->u.mask.extents.width,
+ r->u.mask.extents.height,
+ NULL, 0);
+
+ r->base.render_rows = _cairo_image_spans;
+ r->base.finish = NULL;
+ } else {
+ r->mask = pixman_image_create_bits (PIXMAN_a8,
+ r->u.mask.extents.width,
+ r->u.mask.extents.height,
+ (uint32_t *)r->_buf, r->u.mask.stride);
+
+ r->base.render_rows = _cairo_image_spans_and_zero;
+ r->base.finish = _cairo_image_finish_spans_and_zero;
+ }
+ if (unlikely (r->mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ r->u.mask.data = (uint8_t *) pixman_image_get_data (r->mask);
+ r->u.mask.stride = pixman_image_get_stride (r->mask);
+
+ r->u.mask.extents.height += r->u.mask.extents.y;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+ cairo_int_status_t status)
+{
+ cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ if (r->base.finish)
+ r->base.finish (r);
+ }
+
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS && r->bpp == 0)) {
+ const cairo_composite_rectangles_t *composite = r->composite;
+
+ pixman_image_composite32 (r->op, r->src, r->mask,
+ to_pixman_image (composite->surface),
+ composite->unbounded.x + r->u.mask.src_x,
+ composite->unbounded.y + r->u.mask.src_y,
+ 0, 0,
+ composite->unbounded.x,
+ composite->unbounded.y,
+ composite->unbounded.width,
+ composite->unbounded.height);
+ }
+
+ if (r->src)
+ pixman_image_unref (r->src);
+ if (r->mask)
+ pixman_image_unref (r->mask);
+}
+#endif
+
+const cairo_compositor_t *
+_cairo_image_spans_compositor_get (void)
+{
+ static cairo_spans_compositor_t spans;
+ static cairo_compositor_t shape;
+
+ if (spans.base.delegate == NULL) {
+ _cairo_shape_mask_compositor_init (&shape,
+ _cairo_image_traps_compositor_get());
+ shape.glyphs = NULL;
+
+ _cairo_spans_compositor_init (&spans, &shape);
+
+ spans.flags = 0;
+#if PIXMAN_HAS_OP_LERP
+ spans.flags |= CAIRO_SPANS_COMPOSITOR_HAS_LERP;
+#endif
+
+ //spans.acquire = acquire;
+ //spans.release = release;
+ spans.fill_boxes = fill_boxes;
+ spans.draw_image_boxes = draw_image_boxes;
+ //spans.copy_boxes = copy_boxes;
+ spans.pattern_to_surface = _cairo_image_source_create_for_pattern;
+ //spans.check_composite_boxes = check_composite_boxes;
+ spans.composite_boxes = composite_boxes;
+ //spans.check_span_renderer = check_span_renderer;
+ spans.renderer_init = span_renderer_init;
+ spans.renderer_fini = span_renderer_fini;
+ }
+
+ return &spans.base;
+}
diff --git a/src/cairo-image-filters-private.h b/src/cairo-image-filters-private.h
new file mode 100755
index 000000000..a9e47f0b3
--- /dev/null
+++ b/src/cairo-image-filters-private.h
@@ -0,0 +1,56 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ * Copyright © 2013 Samsung Research America, Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Henry Song <henry.song@samsung.com>
+ */
+
+/* The purpose of this file/surface is to simply translate a pattern
+ * to a pixman_image_t and thence to feed it back to the general
+ * compositor interface.
+ */
+
+#ifndef CAIRO_IMAGE_FILTERS_PRIVATE_H
+#define CAIRO_IMAGE_FILTERS_PRIVATE_H
+
+#include "cairoint.h"
+
+#define MAX_BLUR_SIZE 256
+
+cairo_private cairo_surface_t *
+_cairo_image_gaussian_filter (cairo_surface_t *src,
+ const cairo_pattern_t *pattern);
+
+#endif /* CAIRO_IMAGE_FILTERS_PRIVATE_H */
diff --git a/src/cairo-image-filters.c b/src/cairo-image-filters.c
new file mode 100755
index 000000000..1f0531ef2
--- /dev/null
+++ b/src/cairo-image-filters.c
@@ -0,0 +1,315 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ * Copyright © 2013 Samsung Research America, Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Henry Song <henry.song@samsung.com>
+ */
+
+/* The purpose of this file/surface is to simply translate a pattern
+ * to a pixman_image_t and thence to feed it back to the general
+ * compositor interface.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-filters-private.h"
+#include "cairo-image-filters-private.h"
+
+static pixman_fixed_t *
+_pixman_image_create_convolution_params (double *params,
+ int col, int row,
+ cairo_bool_t x_pass)
+{
+ int i;
+ pixman_fixed_t *pixman_params;
+ double *coef;
+ int length;
+
+ if ( params == NULL)
+ return NULL;
+
+ if (x_pass) {
+ pixman_params = _cairo_malloc_ab (col + 2, sizeof (double));
+ if (pixman_params == NULL)
+ return NULL;
+
+ pixman_params[0] = pixman_int_to_fixed (col);
+ pixman_params[1] = pixman_int_to_fixed (1);
+ coef = _cairo_malloc_ab (col, sizeof (double));
+ if (coef == NULL) {
+ free (pixman_params);
+ return NULL;
+ }
+
+ memset (coef, 0, sizeof (double) * col);
+ compute_x_coef_to_double (params, row, col, coef);
+ length = col;
+ }
+ else {
+ pixman_params = _cairo_malloc_ab (row + 2, sizeof (double));
+ if (pixman_params == NULL)
+ return NULL;
+ pixman_params[0] = pixman_int_to_fixed (1);
+ pixman_params[1] = pixman_int_to_fixed (row);
+ coef = _cairo_malloc_ab (row, sizeof (double));
+ if (coef == NULL) {
+ free (pixman_params);
+ return NULL;
+ }
+
+ memset (coef, 0, sizeof (double) * row);
+ compute_y_coef_to_double (params, row, col, coef);
+ length = row;
+ }
+
+ for (i = 0; i < length; i++)
+ pixman_params[i + 2] = pixman_double_to_fixed (coef[i]);
+
+ free (coef);
+
+ return pixman_params;
+}
+
+cairo_surface_t *
+_cairo_image_gaussian_filter (cairo_surface_t *src, const cairo_pattern_t *pattern)
+{
+ int row, col;
+ int width, height;
+ int stride;
+ pixman_fixed_t *pixman_params;
+ pixman_transform_t pixman_transform;
+ cairo_int_status_t status;
+ cairo_matrix_t matrix;
+ int ix = 0;
+ int iy = 0;
+
+ pixman_image_t *scratch_images[2];
+ cairo_image_surface_t *src_image = (cairo_image_surface_t *)src;
+ cairo_image_surface_t *clone_image;
+ pixman_image_t *temp_image = NULL;
+
+ int src_width = cairo_image_surface_get_width (src);
+ int src_height = cairo_image_surface_get_height (src);
+ int i;
+
+ /* clone image, because we don't want to mess with original image
+ * transformation
+ */
+ /* XXX: we need to first scale the image down */
+ if (pattern->filter == CAIRO_FILTER_GAUSSIAN &&
+ pattern->convolution_matrix) {
+ for (i = 0; i < 2; i++)
+ scratch_images[i] = NULL;
+
+ row = pattern->y_radius * 2 + 1;
+ col = pattern->x_radius * 2 + 1;
+ width = src_width / pattern->shrink_factor_x;
+ height = src_height / pattern->shrink_factor_y;
+ stride = width * (src_image->stride / src_width);
+
+ clone_image = (cairo_image_surface_t *)
+ cairo_image_surface_create (src_image->format,
+ src_width, src_height);
+
+ if (unlikely (clone_image->base.status)) {
+ cairo_surface_destroy (&clone_image->base);
+ clone_image = (cairo_image_surface_t *)cairo_surface_reference (src);
+ goto DONE;
+ }
+
+ /* XXX: we must always create a clone because we need to modify
+ * it transformation, no copy data */
+ temp_image = pixman_image_create_bits (src_image->pixman_format,
+ src_image->width,
+ src_image->height,
+ (uint32_t *)src_image->data,
+ src_image->stride);
+ if (unlikely (temp_image == NULL)) {
+ cairo_surface_destroy (&clone_image->base);
+ clone_image = (cairo_image_surface_t *)cairo_surface_reference (src);
+ goto DONE;
+ }
+
+ /* create scratch images */
+ for (i = 0; i < 2; i++) {
+ scratch_images[i] = pixman_image_create_bits (src_image->pixman_format,
+ width, height,
+ NULL, stride);
+ if (unlikely (scratch_images[i] == NULL)) {
+ cairo_surface_destroy (&clone_image->base);
+ clone_image = (cairo_image_surface_t *)cairo_surface_reference (src);
+ goto DONE;
+ }
+ }
+
+ /* if scale, we need to shrink it to scratch 0 */
+ /* paint temp to temp_surface */
+ if (width != src_width || height != src_height) {
+ pixman_image_set_filter (temp_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+ /* set up transform matrix */
+ cairo_matrix_init_scale (&matrix,
+ (double) src_width / (double) width,
+ (double) src_height / (double) height);
+ status = _cairo_matrix_to_pixman_matrix_offset (&matrix,
+ pattern->filter,
+ src_width/2,
+ src_height/2,
+ &pixman_transform,
+ &ix, &iy);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ }
+ else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
+ ! pixman_image_set_transform (temp_image,
+ &pixman_transform))) {
+ cairo_surface_destroy (&clone_image->base);
+ clone_image = (cairo_image_surface_t *)cairo_surface_reference (src);
+ goto DONE;
+ }
+ /* set repeat to none */
+ pixman_image_set_repeat (temp_image, PIXMAN_REPEAT_NONE);
+
+ if (pattern->has_component_alpha)
+ pixman_image_set_component_alpha (temp_image, TRUE);
+ pixman_image_set_filter (temp_image, PIXMAN_FILTER_BILINEAR, NULL, 0);
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ temp_image,
+ NULL,
+ scratch_images[0],
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height);
+ pixman_image_unref (temp_image);
+ temp_image = pixman_image_ref (scratch_images[0]);
+ }
+
+ /* XXX: begin blur pass */
+ /* set up convolution params for x-pass */
+ pixman_params =
+ _pixman_image_create_convolution_params (pattern->convolution_matrix, col, row, TRUE);
+ pixman_image_set_filter (temp_image, PIXMAN_FILTER_CONVOLUTION,
+ (const pixman_fixed_t *)pixman_params, col + 2);
+ free (pixman_params);
+
+ pixman_image_set_repeat (temp_image, PIXMAN_REPEAT_NONE);
+
+ if (pattern->has_component_alpha)
+ pixman_image_set_component_alpha (temp_image, TRUE);
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ temp_image,
+ NULL,
+ scratch_images[1],
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ /* y-pass */
+ pixman_params =
+ _pixman_image_create_convolution_params (pattern->convolution_matrix, col, row, FALSE);
+ pixman_image_set_filter (scratch_images[1], PIXMAN_FILTER_CONVOLUTION,
+ (const pixman_fixed_t *)pixman_params, row + 2);
+ free (pixman_params);
+
+ pixman_image_set_repeat (scratch_images[1], PIXMAN_REPEAT_NONE);
+
+ if (pattern->has_component_alpha)
+ pixman_image_set_component_alpha (scratch_images[1], TRUE);
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ scratch_images[1],
+ NULL,
+ scratch_images[0],
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ /* paint scratch_surfaces[0] to clone */
+ /* set up transform matrix */
+ cairo_matrix_init_scale (&matrix,
+ (double) width / (double) src_width,
+ (double) height / (double) src_height);
+ status = _cairo_matrix_to_pixman_matrix_offset (&matrix,
+ pattern->filter,
+ width/2,
+ height/2,
+ &pixman_transform,
+ &ix, &iy);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ /* If the transform is an identity, we don't need to set it */
+ }
+ else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
+ ! pixman_image_set_transform (scratch_images[0],
+ &pixman_transform))) {
+ cairo_surface_destroy (&clone_image->base);
+ clone_image = (cairo_image_surface_t *)cairo_surface_reference (src);
+ goto DONE;
+ }
+ /* set repeat to none */
+ pixman_image_set_repeat (scratch_images[0], PIXMAN_REPEAT_NONE);
+
+ if (pattern->has_component_alpha)
+ pixman_image_set_component_alpha (scratch_images[0], TRUE);
+ pixman_image_set_filter (scratch_images[0], PIXMAN_FILTER_BILINEAR, NULL, 0);
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ scratch_images[0],
+ NULL,
+ clone_image->pixman_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ src_width, src_height);
+DONE:
+ /* free temp surfaces */
+ if (temp_image)
+ pixman_image_unref (temp_image);
+ for (i = 0; i < 2; i++) {
+ if (scratch_images[i])
+ pixman_image_unref (scratch_images[i]);
+ }
+ }
+ else
+ clone_image = (cairo_image_surface_t *) cairo_surface_reference (src);
+
+ return &clone_image->base;
+
+}
diff --git a/src/cairo-image-info-private.h b/src/cairo-image-info-private.h
new file mode 100755
index 000000000..0d9ef8498
--- /dev/null
+++ b/src/cairo-image-info-private.h
@@ -0,0 +1,63 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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/src/cairo-image-info.c b/src/cairo-image-info.c
new file mode 100755
index 000000000..4489698e8
--- /dev/null
+++ b/src/cairo-image-info.c
@@ -0,0 +1,292 @@
+/* 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-error-private.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/src/cairo-image-mask-compositor.c b/src/cairo-image-mask-compositor.c
new file mode 100755
index 000000000..33fd6dd87
--- /dev/null
+++ b/src/cairo-image-mask-compositor.c
@@ -0,0 +1,408 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This compositor is slightly pointless. Just exists for testing
+ * and as skeleton code.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-region-private.h"
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+ cairo_region_t *region)
+{
+ cairo_image_surface_t *surface = _surface;
+ pixman_region32_t *rgn = region ? &region->rgn : NULL;
+
+ if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+has_snapshot (void *_dst,
+ const cairo_pattern_t *pattern)
+{
+ return FALSE;
+}
+
+static cairo_int_status_t
+draw_image (void *_dst,
+ cairo_image_surface_t *image,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)_dst;
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, dst->pixman_image,
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+ return
+ (color->alpha_short >> 8 << 24) |
+ (color->red_short >> 8 << 16) |
+ (color->green_short & 0xff00) |
+ (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t *color,
+ double opacity,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ cairo_color_t opacity_color;
+ uint32_t c;
+
+ if (!(format == PIXMAN_a8r8g8b8 ||
+ format == PIXMAN_x8r8g8b8 ||
+ format == PIXMAN_a8b8g8r8 ||
+ format == PIXMAN_x8b8g8r8 ||
+ format == PIXMAN_b8g8r8a8 ||
+ format == PIXMAN_b8g8r8x8 ||
+ format == PIXMAN_r5g6b5 ||
+ format == PIXMAN_b5g6r5 ||
+ format == PIXMAN_a8))
+ {
+ return FALSE;
+ }
+
+ if (opacity != 1.0) {
+ _cairo_color_init_rgba (&opacity_color,
+ color->red,
+ color->green,
+ color->blue,
+ color->alpha * opacity);
+ color = &opacity_color;
+ }
+ c = color_to_uint32 (color);
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+ c = ((c & 0xff000000) >> 0) |
+ ((c & 0x00ff0000) >> 16) |
+ ((c & 0x0000ff00) >> 0) |
+ ((c & 0x000000ff) << 16);
+ }
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+ c = ((c & 0xff000000) >> 24) |
+ ((c & 0x00ff0000) >> 8) |
+ ((c & 0x0000ff00) << 8) |
+ ((c & 0x000000ff) << 24);
+ }
+
+ if (format == PIXMAN_a8) {
+ c = c >> 24;
+ } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+ c = ((((c) >> 3) & 0x001f) |
+ (((c) >> 5) & 0x07e0) |
+ (((c) >> 8) & 0xf800));
+ }
+
+ *pixel = c;
+ return TRUE;
+}
+
+static cairo_int_status_t
+fill_rectangles (void *_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_image_surface_t *dst = _dst;
+ uint32_t pixel;
+ int i;
+
+ if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ for (i = 0; i < num_rects; i++) {
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height,
+ pixel);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fill_boxes (void *_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ cairo_image_surface_t *dst = _dst;
+ struct _cairo_boxes_chunk *chunk;
+ uint32_t pixel;
+ int i;
+
+ assert (boxes->is_pixel_aligned);
+
+ if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+ switch ((int) op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return PIXMAN_OP_CLEAR;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return PIXMAN_OP_SRC;
+ case CAIRO_OPERATOR_OVER:
+ return PIXMAN_OP_OVER;
+ case CAIRO_OPERATOR_IN:
+ return PIXMAN_OP_IN;
+ case CAIRO_OPERATOR_OUT:
+ return PIXMAN_OP_OUT;
+ case CAIRO_OPERATOR_ATOP:
+ return PIXMAN_OP_ATOP;
+
+ case CAIRO_OPERATOR_DEST:
+ return PIXMAN_OP_DST;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return PIXMAN_OP_OVER_REVERSE;
+ case CAIRO_OPERATOR_DEST_IN:
+ return PIXMAN_OP_IN_REVERSE;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return PIXMAN_OP_OUT_REVERSE;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return PIXMAN_OP_ATOP_REVERSE;
+
+ case CAIRO_OPERATOR_XOR:
+ return PIXMAN_OP_XOR;
+ case CAIRO_OPERATOR_ADD:
+ return PIXMAN_OP_ADD;
+ case CAIRO_OPERATOR_SATURATE:
+ return PIXMAN_OP_SATURATE;
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ return PIXMAN_OP_MULTIPLY;
+ case CAIRO_OPERATOR_SCREEN:
+ return PIXMAN_OP_SCREEN;
+ case CAIRO_OPERATOR_OVERLAY:
+ return PIXMAN_OP_OVERLAY;
+ case CAIRO_OPERATOR_DARKEN:
+ return PIXMAN_OP_DARKEN;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return PIXMAN_OP_LIGHTEN;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return PIXMAN_OP_COLOR_DODGE;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return PIXMAN_OP_COLOR_BURN;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return PIXMAN_OP_HARD_LIGHT;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return PIXMAN_OP_SOFT_LIGHT;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return PIXMAN_OP_DIFFERENCE;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return PIXMAN_OP_EXCLUSION;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return PIXMAN_OP_HSL_HUE;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return PIXMAN_OP_HSL_SATURATION;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return PIXMAN_OP_HSL_COLOR;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return PIXMAN_OP_HSL_LUMINOSITY;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return PIXMAN_OP_OVER;
+ }
+}
+
+static cairo_int_status_t
+composite (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_image_surface_t *dst = _dst;
+ cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src;
+ cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask;
+ if (mask) {
+ pixman_image_composite32 (_pixman_operator (op),
+ src->pixman_image, mask->pixman_image, dst->pixman_image,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ } else {
+ pixman_image_composite32 (_pixman_operator (op),
+ src->pixman_image, NULL, dst->pixman_image,
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes)
+{
+ cairo_image_surface_t *dst = _dst;
+ cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src;
+ cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ assert (boxes->is_pixel_aligned);
+
+ op = _pixman_operator (op);
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ if (mask) {
+ pixman_image_composite32 (op,
+ src->pixman_image, mask->pixman_image, dst->pixman_image,
+ x1 + src_x, y1 + src_y,
+ x1 + mask_x, y1 + mask_y,
+ x1 + dst_x, y1 + dst_y,
+ x2 - x1, y2 - y1);
+ } else {
+ pixman_image_composite32 (op,
+ src->pixman_image, NULL, dst->pixman_image,
+ x1 + src_x, y1 + src_y,
+ 0, 0,
+ x1 + dst_x, y1 + dst_y,
+ x2 - x1, y2 - y1);
+ }
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_compositor_t *
+_cairo_image_mask_compositor_get (void)
+{
+ static cairo_mask_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_mask_compositor_init (&compositor,
+ _cairo_image_traps_compositor_get ());
+ compositor.acquire = acquire;
+ compositor.release = release;
+ compositor.set_clip_region = set_clip_region;
+ compositor.pattern_to_surface = _cairo_pixman_source_create_for_pattern;
+ compositor.has_snapshot = has_snapshot;
+ compositor.draw_image = draw_image;
+ compositor.fill_rectangles = fill_rectangles;
+ compositor.fill_boxes = fill_boxes;
+ //compositor.check_composite = check_composite;
+ compositor.composite = composite;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ }
+
+ return &compositor.base;
+}
diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c
new file mode 100755
index 000000000..e02ebe5e0
--- /dev/null
+++ b/src/cairo-image-source.c
@@ -0,0 +1,1331 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* The purpose of this file/surface is to simply translate a pattern
+ * to a pixman_image_t and thence to feed it back to the general
+ * compositor interface.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-image-filters-private.h"
+
+#if CAIRO_HAS_TG_SURFACE
+#include "cairo-tg-private.h"
+#endif
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+#if CAIRO_NO_MUTEX
+#define PIXMAN_HAS_ATOMIC_OPS 1
+#endif
+
+#if PIXMAN_HAS_ATOMIC_OPS
+static pixman_image_t *__pixman_transparent_image;
+static pixman_image_t *__pixman_black_image;
+static pixman_image_t *__pixman_white_image;
+
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ pixman_image_t *image;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = __pixman_transparent_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0x00;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ pixman_image_t *image;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = __pixman_black_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ pixman_image_t *image;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = __pixman_white_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0xffff;
+ color.green = 0xffff;
+ color.blue = 0xffff;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static struct {
+ cairo_color_t color;
+ pixman_image_t *image;
+} cache[16];
+static int n_cached;
+
+#else /* !PIXMAN_HAS_ATOMIC_OPS */
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT);
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return _pixman_image_for_color (CAIRO_COLOR_BLACK);
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return _pixman_image_for_color (CAIRO_COLOR_WHITE);
+}
+#endif /* !PIXMAN_HAS_ATOMIC_OPS */
+
+
+pixman_image_t *
+_pixman_image_for_color (const cairo_color_t *cairo_color)
+{
+ pixman_color_t color;
+ pixman_image_t *image;
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ int i;
+
+ if (CAIRO_COLOR_IS_CLEAR (cairo_color))
+ return _pixman_transparent_image ();
+
+ if (CAIRO_COLOR_IS_OPAQUE (cairo_color)) {
+ if (cairo_color->red_short <= 0x00ff &&
+ cairo_color->green_short <= 0x00ff &&
+ cairo_color->blue_short <= 0x00ff)
+ {
+ return _pixman_black_image ();
+ }
+
+ if (cairo_color->red_short >= 0xff00 &&
+ cairo_color->green_short >= 0xff00 &&
+ cairo_color->blue_short >= 0xff00)
+ {
+ return _pixman_white_image ();
+ }
+ }
+
+ CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+ for (i = 0; i < n_cached; i++) {
+ if (_cairo_color_equal (&cache[i].color, cairo_color)) {
+ image = pixman_image_ref (cache[i].image);
+ goto UNLOCK;
+ }
+ }
+#endif
+
+ color.red = cairo_color->red_short;
+ color.green = cairo_color->green_short;
+ color.blue = cairo_color->blue_short;
+ color.alpha = cairo_color->alpha_short;
+
+ image = pixman_image_create_solid_fill (&color);
+#if PIXMAN_HAS_ATOMIC_OPS
+ if (image == NULL)
+ goto UNLOCK;
+
+ if (n_cached < ARRAY_LENGTH (cache)) {
+ i = n_cached++;
+ } else {
+ i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
+ pixman_image_unref (cache[i].image);
+ }
+ cache[i].image = pixman_image_ref (image);
+ cache[i].color = *cairo_color;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
+#endif
+ return image;
+}
+
+
+void
+_cairo_image_reset_static_data (void)
+{
+#if PIXMAN_HAS_ATOMIC_OPS
+ while (n_cached)
+ pixman_image_unref (cache[--n_cached].image);
+
+ if (__pixman_transparent_image) {
+ pixman_image_unref (__pixman_transparent_image);
+ __pixman_transparent_image = NULL;
+ }
+
+ if (__pixman_black_image) {
+ pixman_image_unref (__pixman_black_image);
+ __pixman_black_image = NULL;
+ }
+
+ if (__pixman_white_image) {
+ pixman_image_unref (__pixman_white_image);
+ __pixman_white_image = NULL;
+ }
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ pixman_gradient_stop_t pixman_stops_static[2];
+ pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
+ pixman_transform_t pixman_transform;
+ cairo_matrix_t matrix;
+ cairo_circle_double_t extremes[2];
+ pixman_point_fixed_t p1, p2;
+ unsigned int i;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
+ pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+ sizeof(pixman_gradient_stop_t));
+ if (unlikely (pixman_stops == NULL))
+ return NULL;
+ }
+
+ for (i = 0; i < pattern->n_stops; i++) {
+ pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+ pixman_stops[i].color.red = pattern->stops[i].color.red_short;
+ pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+ pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
+ pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+ }
+
+ _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+ p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ pattern->n_stops);
+ } else {
+ pixman_fixed_t r1, r2;
+
+ r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
+ r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+ pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2,
+ pixman_stops,
+ pattern->n_stops);
+ }
+
+ if (pixman_stops != pixman_stops_static)
+ free (pixman_stops);
+
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+
+ *ix = *iy = 0;
+ status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.,
+ &pixman_transform, ix, iy);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) ||
+ ! pixman_image_set_transform (pixman_image, &pixman_transform))
+ {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ }
+
+ {
+ pixman_repeat_t pixman_repeat;
+
+ switch (pattern->base.extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *tx, int *ty)
+{
+ pixman_image_t *image;
+ int width, height;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *tx = -extents->x;
+ *ty = -extents->y;
+ width = extents->width;
+ height = extents->height;
+
+ image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ _cairo_mesh_pattern_rasterize (pattern,
+ pixman_image_get_data (image),
+ width, height,
+ pixman_image_get_stride (image),
+ *tx, *ty);
+ return image;
+}
+
+struct acquire_source_cleanup {
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+_acquire_source_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ struct acquire_source_cleanup *data = closure;
+
+ _cairo_surface_release_source_image (data->surface,
+ data->image,
+ data->image_extra);
+ free (data);
+}
+
+static void
+_defer_free_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ cairo_surface_destroy (closure);
+}
+
+typedef struct _cairo_image_buffer
+{
+ cairo_format_t format;
+ unsigned char *data;
+ int width;
+ int height;
+ int stride;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+} cairo_image_buffer_t;
+
+static inline void
+_get_image_buffer (cairo_surface_t *surface, cairo_image_buffer_t *image_buffer)
+{
+ if (surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE)
+ {
+ cairo_image_surface_t *image = (cairo_image_surface_t *)surface;
+
+ image_buffer->format = image->format;
+ image_buffer->data = image->data;
+ image_buffer->width = image->width;
+ image_buffer->height = image->height;
+ image_buffer->stride = image->stride;
+ image_buffer->pixman_image = image->pixman_image;
+ image_buffer->pixman_format = image->pixman_format;
+ }
+#if CAIRO_HAS_TG_SURFACE
+ else if (surface->backend->type == CAIRO_SURFACE_TYPE_TG)
+ {
+ cairo_tg_surface_t *tg = (cairo_tg_surface_t *)surface;
+
+ image_buffer->format = tg->format;
+ image_buffer->data = tg->data;
+ image_buffer->width = tg->width;
+ image_buffer->height = tg->height;
+ image_buffer->stride = tg->stride;
+ image_buffer->pixman_image = ((cairo_image_surface_t *)(tg->image_surface))->pixman_image;
+ image_buffer->pixman_format = ((cairo_image_surface_t *)(tg->image_surface))->pixman_format;
+
+ /* flush the journal to make the memory image_buffer up-to-date. */
+ cairo_surface_flush (surface);
+ }
+#endif
+}
+
+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 (const cairo_image_buffer_t *image_buffer, int x, int y)
+{
+ uint32_t pixel;
+ pixman_color_t color;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ switch (image_buffer->format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return NULL;
+
+ case CAIRO_FORMAT_A1:
+ pixel = *(uint8_t *) (image_buffer->data + y * image_buffer->stride + x/8);
+ return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image ();
+
+ case CAIRO_FORMAT_A8:
+ color.alpha = *(uint8_t *) (image_buffer->data + y * image_buffer->stride + x);
+ color.alpha |= color.alpha << 8;
+ if (color.alpha == 0)
+ return _pixman_transparent_image ();
+ if (color.alpha == 0xffff)
+ return _pixman_black_image ();
+
+ color.red = color.green = color.blue = 0;
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_RGB16_565:
+ pixel = *(uint16_t *) (image_buffer->data + y * image_buffer->stride + 2 * x);
+ if (pixel == 0)
+ return _pixman_black_image ();
+ if (pixel == 0xffff)
+ return _pixman_white_image ();
+
+ color.alpha = 0xffff;
+ color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
+ color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
+ color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_RGB30:
+ pixel = *(uint32_t *) (image_buffer->data + y * image_buffer->stride + 4 * x);
+ pixel &= 0x3fffffff; /* ignore alpha bits */
+ if (pixel == 0)
+ return _pixman_black_image ();
+ if (pixel == 0x3fffffff)
+ return _pixman_white_image ();
+
+ /* convert 10bpc to 16bpc */
+ color.alpha = 0xffff;
+ color.red = expand_channel((pixel >> 20) & 0x3fff, 10);
+ color.green = expand_channel((pixel >> 10) & 0x3fff, 10);
+ color.blue = expand_channel(pixel & 0x3fff, 10);
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ pixel = *(uint32_t *) (image_buffer->data + y * image_buffer->stride + 4 * x);
+ color.alpha = image_buffer->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 cairo_bool_t
+_pixman_image_set_properties (pixman_image_t *pixman_image,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *ix,int *iy)
+{
+ pixman_transform_t pixman_transform;
+ cairo_int_status_t status;
+
+ status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix,
+ pattern->filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.,
+ &pixman_transform, ix, iy);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ /* If the transform is an identity, we don't need to set it
+ * and we can use any filtering, so choose the fastest one. */
+ pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+ }
+ else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
+ ! pixman_image_set_transform (pixman_image,
+ &pixman_transform)))
+ {
+ return FALSE;
+ }
+ else
+ {
+ pixman_filter_t pixman_filter;
+
+ switch (pattern->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 (pattern->extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ if (pattern->has_component_alpha)
+ pixman_image_set_component_alpha (pixman_image, TRUE);
+
+ return TRUE;
+}
+
+struct proxy {
+ cairo_surface_t base;
+ cairo_surface_t *image;
+};
+
+static cairo_status_t
+proxy_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ struct proxy *proxy = abstract_surface;
+ return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
+}
+
+static void
+proxy_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ struct proxy *proxy = abstract_surface;
+ _cairo_surface_release_source_image (proxy->image, image, image_extra);
+}
+
+static cairo_status_t
+proxy_finish (void *abstract_surface)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t proxy_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+ proxy_finish,
+ NULL,
+
+ NULL, /* create similar */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ proxy_acquire_source_image,
+ proxy_release_source_image,
+};
+
+static cairo_surface_t *
+attach_proxy (cairo_surface_t *source,
+ cairo_surface_t *image)
+{
+ struct proxy *proxy;
+
+ proxy = malloc (sizeof (*proxy));
+ if (unlikely (proxy == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content);
+
+ proxy->image = image;
+ _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
+
+ return &proxy->base;
+}
+
+static void
+detach_proxy (cairo_surface_t *source,
+ cairo_surface_t *proxy)
+{
+ cairo_surface_finish (proxy);
+ cairo_surface_destroy (proxy);
+}
+
+static cairo_surface_t *
+get_proxy (cairo_surface_t *proxy)
+{
+ return ((struct proxy *)proxy)->image;
+}
+
+static pixman_image_t *
+_pixman_image_for_recording (cairo_image_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *ix, int *iy)
+{
+ cairo_surface_t *source, *clone, *proxy;
+ cairo_rectangle_int_t limit;
+ pixman_image_t *pixman_image;
+ cairo_status_t status;
+ cairo_extend_t extend;
+ cairo_matrix_t *m, matrix;
+ int tx = 0, ty = 0;
+ cairo_surface_t *blurred_surface;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *ix = *iy = 0;
+
+ source = _cairo_pattern_get_source (pattern, &limit);
+
+ extend = pattern->base.extend;
+ if (_cairo_rectangle_contains_rectangle (&limit, sample))
+ extend = CAIRO_EXTEND_NONE;
+ if (extend == CAIRO_EXTEND_NONE) {
+ if (! _cairo_rectangle_intersect (&limit, sample))
+ return _pixman_transparent_image ();
+
+ if (! _cairo_matrix_is_identity (&pattern->base.matrix)) {
+ double x1, y1, x2, y2;
+
+ matrix = pattern->base.matrix;
+ status = cairo_matrix_invert (&matrix);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x1 = limit.x;
+ y1 = limit.y;
+ x2 = limit.x + limit.width;
+ y2 = limit.y + limit.height;
+
+ _cairo_matrix_transform_bounding_box (&matrix,
+ &x1, &y1, &x2, &y2, NULL);
+
+ limit.x = floor (x1);
+ limit.y = floor (y1);
+ limit.width = ceil (x2) - limit.x;
+ limit.height = ceil (y2) - limit.y;
+ }
+ }
+ tx = limit.x;
+ ty = limit.y;
+
+ /* XXX transformations! */
+ proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
+ if (proxy != NULL) {
+ clone = cairo_surface_reference (get_proxy (proxy));
+ goto done;
+ }
+
+ if (is_mask) {
+ clone = cairo_image_surface_create (CAIRO_FORMAT_A8,
+ limit.width, limit.height);
+ } else {
+ if (dst->base.content == source->content)
+ clone = cairo_image_surface_create (dst->format,
+ limit.width, limit.height);
+ else
+ clone = _cairo_image_surface_create_with_content (source->content,
+ limit.width,
+ limit.height);
+ }
+
+ m = NULL;
+ if (extend == CAIRO_EXTEND_NONE) {
+ matrix = pattern->base.matrix;
+ if (tx | ty)
+ cairo_matrix_translate (&matrix, tx, ty);
+ m = &matrix;
+ } else {
+ /* XXX extract scale factor for repeating patterns */
+ }
+
+ /* Handle recursion by returning future reads from the current image */
+ proxy = attach_proxy (source, clone);
+ status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL);
+ detach_proxy (source, proxy);
+ if (unlikely (status)) {
+ cairo_surface_destroy (clone);
+ return NULL;
+ }
+
+done:
+ /* filter with gaussian */
+ blurred_surface = _cairo_image_gaussian_filter (clone, &pattern->base);
+
+ pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ cairo_surface_destroy (clone);
+
+ *ix = -limit.x;
+ *iy = -limit.y;
+ if (extend != CAIRO_EXTEND_NONE) {
+ if (! _pixman_image_set_properties (pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (pixman_image);
+ pixman_image= NULL;
+ }
+ }
+
+ return pixman_image;
+}
+
+static inline cairo_bool_t
+_surface_type_is_image_buffer (cairo_surface_type_t type)
+{
+#if CAIRO_HAS_TG_SURFACE
+ return type == CAIRO_SURFACE_TYPE_IMAGE || type == CAIRO_SURFACE_TYPE_TG;
+#else
+ return type == CAIRO_SURFACE_TYPE_IMAGE;
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_surface (cairo_image_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *ix, int *iy)
+{
+ cairo_extend_t extend = pattern->base.extend;
+ pixman_image_t *pixman_image = NULL;
+ pixman_image_t *blurred_pixman_image = NULL;
+ cairo_surface_t *blurred_surface = NULL;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *ix = *iy = 0;
+ pixman_image = NULL;
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+ return _pixman_image_for_recording(dst, pattern,
+ is_mask, extents, sample,
+ ix, iy);
+
+ if (_surface_type_is_image_buffer (pattern->surface->type) &&
+ (! is_mask || ! pattern->base.has_component_alpha ||
+ (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+ {
+ cairo_surface_t *defer_free = NULL;
+ cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+ cairo_image_buffer_t image_buffer;
+
+ if (_cairo_surface_is_snapshot (&source->base)) {
+ defer_free = _cairo_surface_snapshot_get_target (&source->base);
+ source = (cairo_image_surface_t *) defer_free;
+ }
+
+ if (_surface_type_is_image_buffer (source->base.backend->type)) {
+ _get_image_buffer (source, &image_buffer);
+
+ if (extend != CAIRO_EXTEND_NONE &&
+ sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= image_buffer.width &&
+ sample->y + sample->height <= image_buffer.height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (sample->x < 0 ||
+ sample->y < 0 ||
+ sample->x >= image_buffer.width ||
+ sample->y >= image_buffer.height)
+ {
+ if (extend == CAIRO_EXTEND_NONE) {
+ cairo_surface_destroy (defer_free);
+ return _pixman_transparent_image ();
+ }
+ }
+ else
+ {
+ pixman_image = _pixel_to_solid (&image_buffer, sample->x, sample->y);
+ if (pixman_image) {
+ cairo_surface_destroy (defer_free);
+ return pixman_image;
+ }
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ /* avoid allocating a 'pattern' image if we can reuse the original */
+ if (extend == CAIRO_EXTEND_NONE &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ pattern->base.filter,
+ ix, iy))
+ {
+ cairo_surface_destroy (defer_free);
+ /* filter with gaussian */
+ if (pattern->filter == CAIRO_FILTER_GAUSSIAN) {
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ blurred_pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ return blurred_pixman_image;
+ }
+ else
+ return pixman_image_ref (image_buffer.pixman_image);
+ }
+#endif
+
+ if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN) {
+ pixman_image = pixman_image_create_bits (image_buffer.pixman_format,
+ image_buffer.width,
+ image_buffer.height,
+ (uint32_t *) image_buffer.data,
+ image_buffer.stride);
+ if (unlikely (pixman_image == NULL)) {
+ cairo_surface_destroy (defer_free);
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+
+ if (defer_free) {
+ pixman_image_set_destroy_function (pixman_image,
+ _defer_free_cleanup,
+ defer_free);
+ }
+ }
+ else
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ } else if (source->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub;
+ cairo_bool_t is_contained = FALSE;
+
+ sub = (cairo_surface_subsurface_t *) source;
+ source = sub->target;
+
+ _get_image_buffer (source, &image_buffer);
+
+ if (sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= sub->extents.width &&
+ sample->y + sample->height <= sub->extents.height)
+ {
+ is_contained = TRUE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (is_contained) {
+ pixman_image = _pixel_to_solid (&image_buffer,
+ sub->extents.x + sample->x,
+ sub->extents.y + sample->y);
+ if (pixman_image)
+ return pixman_image;
+ } else {
+ if (extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ *ix = sub->extents.x;
+ *iy = sub->extents.y;
+ if (is_contained &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ pattern->base.filter,
+ ix, iy))
+ {
+ /* filter with gaussian */
+ if (pattern->filter == CAIRO_FILTER_GAUSSIAN) {
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ blurred_pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ return blurred_pixman_image;
+ }
+ }
+#endif
+
+ /* Avoid sub-byte offsets, force a copy in that case. */
+ if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN) {
+ if (PIXMAN_FORMAT_BPP (image_buffer.pixman_format) >= 8) {
+ if (is_contained) {
+ void *data = image_buffer.data
+ + sub->extents.x * PIXMAN_FORMAT_BPP(image_buffer.pixman_format)/8
+ + sub->extents.y * image_buffer.stride;
+ pixman_image = pixman_image_create_bits (image_buffer.pixman_format,
+ sub->extents.width,
+ sub->extents.height,
+ data,
+ image_buffer.stride);
+ if (unlikely (pixman_image == NULL)) {
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+ } else {
+ /* XXX for a simple translation and EXTEND_NONE we can
+ * fix up the pattern matrix instead.
+ */
+ }
+ }
+ }
+ else
+ /* filter */
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ }
+ }
+
+ if (pixman_image == NULL && blurred_surface == 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)) {
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+
+ if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN) {
+ 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);
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+ }
+ else
+ /* filter with gaussian */
+ blurred_surface = _cairo_image_gaussian_filter (&image->base, &pattern->base);
+
+ cleanup = malloc (sizeof (*cleanup));
+ if (unlikely (cleanup == NULL)) {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ if (pixman_image)
+ pixman_image_unref (pixman_image);
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+
+ if (pixman_image) {
+ cleanup->surface = pattern->surface;
+ cleanup->image = image;
+ cleanup->image_extra = extra;
+ pixman_image_set_destroy_function (pixman_image,
+ _acquire_source_cleanup, cleanup);
+ }
+ }
+
+ if (blurred_surface) {
+ blurred_pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ }
+
+ if (blurred_pixman_image) {
+ if (! _pixman_image_set_properties (blurred_pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (blurred_pixman_image);
+ blurred_pixman_image= NULL;
+ }
+ }
+ if (pixman_image) {
+ if (! _pixman_image_set_properties (pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (pixman_image);
+ pixman_image= NULL;
+ }
+ }
+
+ if (blurred_pixman_image) {
+ if (pixman_image)
+ pixman_image_unref (pixman_image);
+ }
+ else
+ blurred_pixman_image = pixman_image;
+
+ return blurred_pixman_image;
+}
+
+struct raster_source_cleanup {
+ const cairo_pattern_t *pattern;
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+_raster_source_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ struct raster_source_cleanup *data = closure;
+
+ _cairo_surface_release_source_image (data->surface,
+ data->image,
+ data->image_extra);
+
+ _cairo_raster_source_pattern_release (data->pattern,
+ data->surface);
+
+ free (data);
+}
+
+static pixman_image_t *
+_pixman_image_for_raster (cairo_image_surface_t *dst,
+ const cairo_raster_source_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ struct raster_source_cleanup *cleanup;
+ cairo_image_surface_t *image;
+ void *extra;
+ cairo_status_t status;
+ cairo_surface_t *surface;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *ix = *iy = 0;
+
+ surface = _cairo_raster_source_pattern_acquire (&pattern->base,
+ &dst->base, NULL);
+ if (unlikely (surface == NULL || surface->status))
+ return NULL;
+
+ status = _cairo_surface_acquire_source_image (surface, &image, &extra);
+ if (unlikely (status)) {
+ _cairo_raster_source_pattern_release (&pattern->base, surface);
+ return NULL;
+ }
+
+ assert (image->width == pattern->extents.width);
+ assert (image->height == pattern->extents.height);
+
+ pixman_image = pixman_image_create_bits (image->pixman_format,
+ image->width,
+ image->height,
+ (uint32_t *) image->data,
+ image->stride);
+ if (unlikely (pixman_image == NULL)) {
+ _cairo_surface_release_source_image (surface, image, extra);
+ _cairo_raster_source_pattern_release (&pattern->base, surface);
+ return NULL;
+ }
+
+ cleanup = malloc (sizeof (*cleanup));
+ if (unlikely (cleanup == NULL)) {
+ pixman_image_unref (pixman_image);
+ _cairo_surface_release_source_image (surface, image, extra);
+ _cairo_raster_source_pattern_release (&pattern->base, surface);
+ return NULL;
+ }
+
+ cleanup->pattern = &pattern->base;
+ cleanup->surface = surface;
+ cleanup->image = image;
+ cleanup->image_extra = extra;
+ pixman_image_set_destroy_function (pixman_image,
+ _raster_source_cleanup, cleanup);
+
+ if (! _pixman_image_set_properties (pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (pixman_image);
+ pixman_image= NULL;
+ }
+
+ return pixman_image;
+}
+
+pixman_image_t *
+_pixman_image_for_pattern (cairo_image_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *tx, int *ty)
+{
+ *tx = *ty = 0;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (pattern == NULL)
+ return _pixman_white_image ();
+
+ switch (pattern->type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _pixman_image_for_color (&((const cairo_solid_pattern_t *) pattern)->color);
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
+ extents, tx, ty);
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _pixman_image_for_mesh ((const cairo_mesh_pattern_t *) pattern,
+ extents, tx, ty);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _pixman_image_for_surface (dst,
+ (const cairo_surface_pattern_t *) pattern,
+ is_mask, extents, sample,
+ tx, ty);
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _pixman_image_for_raster (dst,
+ (const cairo_raster_source_pattern_t *) pattern,
+ is_mask, extents, sample,
+ tx, ty);
+ }
+}
+
+static cairo_status_t
+_cairo_image_source_finish (void *abstract_surface)
+{
+ cairo_image_source_t *source = abstract_surface;
+
+ pixman_image_unref (source->pixman_image);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_surface_backend_t _cairo_image_source_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_image_source_finish,
+ NULL, /* read-only wrapper */
+};
+
+cairo_surface_t *
+_cairo_image_source_create_for_pattern (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_image_source_t *source;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ source = malloc (sizeof (cairo_image_source_t));
+ if (unlikely (source == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ source->pixman_image =
+ _pixman_image_for_pattern ((cairo_image_surface_t *)dst,
+ pattern, is_mask,
+ extents, sample,
+ src_x, src_y);
+ if (unlikely (source->pixman_image == NULL)) {
+ free (source);
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_surface_init (&source->base,
+ &_cairo_image_source_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ source->is_opaque_solid =
+ pattern == NULL || _cairo_pattern_is_opaque_solid (pattern);
+
+ return &source->base;
+}
diff --git a/src/cairo-image-surface-inline.h b/src/cairo-image-surface-inline.h
new file mode 100755
index 000000000..743d5fd3e
--- /dev/null
+++ b/src/cairo-image-surface-inline.h
@@ -0,0 +1,96 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_IMAGE_SURFACE_INLINE_H
+#define CAIRO_IMAGE_SURFACE_INLINE_H
+
+#include "cairo-surface-private.h"
+#include "cairo-image-surface-private.h"
+
+CAIRO_BEGIN_DECLS
+
+static inline cairo_image_surface_t *
+_cairo_image_surface_create_in_error (cairo_status_t status)
+{
+ return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
+}
+
+static inline void
+_cairo_image_surface_set_parent (cairo_image_surface_t *image,
+ cairo_surface_t *parent)
+{
+ image->parent = parent;
+}
+
+static inline cairo_bool_t
+_cairo_image_surface_is_clone (cairo_image_surface_t *image)
+{
+ return image->parent != NULL;
+}
+
+/**
+ * _cairo_surface_is_image:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is an #cairo_image_surface_t
+ *
+ * Return value: %TRUE if the surface is an image surface
+ **/
+static inline cairo_bool_t
+_cairo_surface_is_image (const cairo_surface_t *surface)
+{
+ /* _cairo_surface_nil sets a NULL backend so be safe */
+ return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE;
+}
+
+/**
+ * _cairo_surface_is_image_source:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is an #cairo_image_source_t
+ *
+ * Return value: %TRUE if the surface is an image source
+ **/
+static inline cairo_bool_t
+_cairo_surface_is_image_source (const cairo_surface_t *surface)
+{
+ return surface->backend == &_cairo_image_source_backend;
+}
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_IMAGE_SURFACE_INLINE_H */
diff --git a/src/cairo-image-surface-private.h b/src/cairo-image-surface-private.h
new file mode 100755
index 000000000..8ca694c5e
--- /dev/null
+++ b/src/cairo-image-surface-private.h
@@ -0,0 +1,238 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_IMAGE_SURFACE_PRIVATE_H
+#define CAIRO_IMAGE_SURFACE_PRIVATE_H
+
+#include "cairo-surface-private.h"
+
+#include <pixman.h>
+
+CAIRO_BEGIN_DECLS
+
+/* The canonical image backend */
+struct _cairo_image_surface {
+ cairo_surface_t base;
+
+ pixman_image_t *pixman_image;
+ const cairo_compositor_t *compositor;
+
+ /* Parenting is tricky wrt lifetime tracking...
+ *
+ * One use for tracking the parent of an image surface is for
+ * create_similar_image() where we wish to create a device specific
+ * surface but return an image surface to the user. In such a case,
+ * the image may be owned by the device specific surface, its parent,
+ * but the user lifetime tracking is then performed on the image. So
+ * when the image is then finalized we call cairo_surface_destroy()
+ * on the parent. However, for normal usage where the lifetime tracking
+ * is done on the parent surface, we need to be careful to unhook
+ * the image->parent pointer before finalizing the image.
+ */
+ cairo_surface_t *parent;
+
+ pixman_format_code_t pixman_format;
+ cairo_format_t format;
+ unsigned char *data;
+
+ int width;
+ int height;
+ int stride;
+ int depth;
+
+ unsigned owns_data : 1;
+ unsigned transparency : 2;
+ unsigned color : 2;
+};
+#define to_image_surface(S) ((cairo_image_surface_t *)(S))
+
+/* A wrapper for holding pixman images returned by create_for_pattern */
+typedef struct _cairo_image_source {
+ cairo_surface_t base;
+
+ pixman_image_t *pixman_image;
+ unsigned is_opaque_solid : 1;
+} cairo_image_source_t;
+
+cairo_private extern const cairo_surface_backend_t _cairo_image_surface_backend;
+cairo_private extern const cairo_surface_backend_t _cairo_image_source_backend;
+
+cairo_private const cairo_compositor_t *
+_cairo_image_mask_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_image_traps_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_image_spans_compositor_get (void);
+
+#define _cairo_image_default_compositor_get _cairo_image_spans_compositor_get
+
+cairo_private cairo_int_status_t
+_cairo_image_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_image_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_image_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_image_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_image_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+ pixman_image_t *pixman_image,
+ pixman_format_code_t pixman_format);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_map_to_image (void *abstract_other,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_image_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_image_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+cairo_private void
+_cairo_image_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_snapshot (void *abstract_surface);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_image_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_image_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options);
+
+cairo_private cairo_surface_t *
+_cairo_image_source_create_for_pattern (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y);
+
+cairo_private cairo_status_t
+_cairo_image_surface_finish (void *abstract_surface);
+
+cairo_private pixman_image_t *
+_pixman_image_for_color (const cairo_color_t *cairo_color);
+
+cairo_private pixman_image_t *
+_pixman_image_for_pattern (cairo_image_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *tx, int *ty);
+
+cairo_private void
+_pixman_image_add_traps (pixman_image_t *image,
+ int dst_x, int dst_y,
+ cairo_traps_t *traps);
+
+cairo_private void
+_pixman_image_add_tristrip (pixman_image_t *image,
+ int dst_x, int dst_y,
+ cairo_tristrip_t *strip);
+
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_clone_subimage (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents);
+
+/* Similar to clone; but allow format conversion */
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_create_from_image (cairo_image_surface_t *other,
+ pixman_format_code_t format,
+ int x, int y, int width, int height,
+ int stride);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
new file mode 100755
index 000000000..b68f4a9e7
--- /dev/null
+++ b/src/cairo-image-surface.c
@@ -0,0 +1,1671 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-shadow-private.h"
+#include "cairo-list-inline.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 MAX_IMAGE_SHADOW_SIZE 256
+#define MIN_IMAGE_SHADOW_SIZE 32
+
+/**
+ * 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_list_t shadow_caches;
+static unsigned long shadow_caches_size = 0;
+static cairo_recursive_mutex_t shadow_caches_mutex;
+static unsigned shadow_caches_mutex_depth = 0;
+static cairo_atomic_int_t shadow_caches_ref_count = 0;
+
+static void
+_cairo_image_shadow_caches_init (void)
+{
+ _cairo_atomic_int_inc (&shadow_caches_ref_count);
+
+ if (shadow_caches_ref_count == 1)
+ cairo_list_init (&shadow_caches);
+
+ CAIRO_RECURSIVE_MUTEX_INIT (shadow_caches_mutex);
+}
+
+static void
+_cairo_image_shadow_caches_destroy (void)
+{
+ assert (shadow_caches_ref_count != 0);
+
+ if (! _cairo_atomic_int_dec_and_test (&shadow_caches_ref_count))
+ return;
+
+ if (shadow_caches_mutex_depth == 0) {
+ CAIRO_MUTEX_FINI (shadow_caches_mutex);
+
+ while (! cairo_list_is_empty (&shadow_caches)) {
+ cairo_shadow_cache_t *shadow;
+
+ shadow = cairo_list_first_entry (&shadow_caches,
+ cairo_shadow_cache_t,
+ link);
+ cairo_list_del (&shadow->link);
+ cairo_surface_destroy (shadow->surface);
+ free (shadow);
+ }
+ shadow_caches_size = 0;
+ }
+}
+
+static cairo_status_t
+_cairo_image_surface_shadow_cache_acquire (void *abstract_surface)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_IMAGE)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ if (unlikely (surface->base.status))
+ return surface->base.status;
+
+ CAIRO_MUTEX_LOCK (shadow_caches_mutex);
+ shadow_caches_mutex_depth++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_surface_shadow_cache_release (void *abstract_surface)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_IMAGE)
+ return;
+
+ if (unlikely (surface->base.status))
+ return;
+
+ assert (shadow_caches_mutex_depth > 0);
+ shadow_caches_mutex_depth--;
+
+ CAIRO_MUTEX_UNLOCK (shadow_caches_mutex);
+}
+
+static cairo_list_t *
+_cairo_image_surface_get_shadow_cache (void *abstract_surface)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_IMAGE)
+ return NULL;
+
+ if (unlikely (surface->base.status))
+ return NULL;
+
+ return &shadow_caches;
+}
+
+static unsigned long *
+_cairo_image_surface_get_shadow_cache_size (void *abstract_surface)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_IMAGE)
+ return NULL;
+
+ if (unlikely (surface->base.status))
+ return NULL;
+
+ return &shadow_caches_size;
+}
+
+static cairo_bool_t
+_cairo_image_surface_has_shadow_cache (void *abstract_surface)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_IMAGE)
+ return FALSE;
+ return TRUE;
+}
+
+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_x2r10g10b10:
+ return CAIRO_FORMAT_RGB30;
+ case PIXMAN_x8r8g8b8:
+ return CAIRO_FORMAT_RGB24;
+ case PIXMAN_a8:
+ return CAIRO_FORMAT_A8;
+ case PIXMAN_a1:
+ return CAIRO_FORMAT_A1;
+ case PIXMAN_r5g6b5:
+ return CAIRO_FORMAT_RGB16_565;
+ case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8:
+ case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_r8g8b8:
+ case PIXMAN_b8g8r8: case PIXMAN_b5g6r5:
+ case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5:
+ case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4:
+ case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2:
+ case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2:
+ case PIXMAN_c8: case PIXMAN_g8: case PIXMAN_x4a4:
+ case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_b1g2r1:
+ case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4:
+ case PIXMAN_g4: case PIXMAN_g1:
+ case PIXMAN_yuy2: case PIXMAN_yv12:
+ case PIXMAN_b8g8r8x8:
+ case PIXMAN_b8g8r8a8:
+ case PIXMAN_a2b10g10r10:
+ case PIXMAN_x2b10g10r10:
+ case PIXMAN_a2r10g10b10:
+ case PIXMAN_x14r6g6b6:
+ default:
+ return CAIRO_FORMAT_INVALID;
+ }
+
+ return CAIRO_FORMAT_INVALID;
+}
+
+cairo_content_t
+_cairo_content_from_pixman_format (pixman_format_code_t pixman_format)
+{
+ cairo_content_t content;
+
+ content = 0;
+ if (PIXMAN_FORMAT_RGB (pixman_format))
+ content |= CAIRO_CONTENT_COLOR;
+ if (PIXMAN_FORMAT_A (pixman_format))
+ content |= CAIRO_CONTENT_ALPHA;
+
+ return content;
+}
+
+void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+ pixman_image_t *pixman_image,
+ pixman_format_code_t pixman_format)
+{
+ surface->parent = NULL;
+ surface->pixman_image = pixman_image;
+
+ surface->pixman_format = pixman_format;
+ surface->format = _cairo_format_from_pixman_format (pixman_format);
+ surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
+ surface->owns_data = FALSE;
+ surface->transparency = CAIRO_IMAGE_UNKNOWN;
+ surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
+
+ surface->width = pixman_image_get_width (pixman_image);
+ surface->height = pixman_image_get_height (pixman_image);
+ surface->stride = pixman_image_get_stride (pixman_image);
+ surface->depth = pixman_image_get_depth (pixman_image);
+
+ surface->base.is_clear = surface->width == 0 || surface->height == 0;
+
+ surface->compositor = _cairo_image_spans_compositor_get ();
+
+ _cairo_image_shadow_caches_init ();
+}
+
+cairo_surface_t *
+_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
+ pixman_format_code_t pixman_format)
+{
+ cairo_image_surface_t *surface;
+
+ surface = 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));
+
+ _cairo_image_surface_init (surface, pixman_image, pixman_format);
+
+ return &surface->base;
+}
+
+cairo_bool_t
+_pixman_format_from_masks (cairo_format_masks_t *masks,
+ pixman_format_code_t *format_ret)
+{
+ pixman_format_code_t format;
+ int format_type;
+ int a, r, g, b;
+ cairo_format_masks_t format_masks;
+
+ a = _cairo_popcount (masks->alpha_mask);
+ r = _cairo_popcount (masks->red_mask);
+ g = _cairo_popcount (masks->green_mask);
+ b = _cairo_popcount (masks->blue_mask);
+
+ if (masks->red_mask) {
+ if (masks->red_mask > masks->blue_mask)
+ format_type = PIXMAN_TYPE_ARGB;
+ else
+ format_type = PIXMAN_TYPE_ABGR;
+ } else if (masks->alpha_mask) {
+ format_type = PIXMAN_TYPE_A;
+ } else {
+ return FALSE;
+ }
+
+ format = PIXMAN_FORMAT (masks->bpp, format_type, a, r, g, b);
+
+ if (! pixman_format_supported_destination (format))
+ return FALSE;
+
+ /* Sanity check that we got out of PIXMAN_FORMAT exactly what we
+ * expected. This avoid any problems from something bizarre like
+ * alpha in the least-significant bits, or insane channel order,
+ * or whatever. */
+ if (!_pixman_format_to_masks (format, &format_masks) ||
+ masks->bpp != format_masks.bpp ||
+ masks->red_mask != format_masks.red_mask ||
+ masks->green_mask != format_masks.green_mask ||
+ masks->blue_mask != format_masks.blue_mask)
+ {
+ return FALSE;
+ }
+
+ *format_ret = format;
+ return TRUE;
+}
+
+/* A mask consisting of N bits set to 1. */
+#define MASK(N) ((1UL << (N))-1)
+
+cairo_bool_t
+_pixman_format_to_masks (pixman_format_code_t format,
+ cairo_format_masks_t *masks)
+{
+ int a, r, g, b;
+
+ masks->bpp = PIXMAN_FORMAT_BPP (format);
+
+ /* Number of bits in each channel */
+ a = PIXMAN_FORMAT_A (format);
+ r = PIXMAN_FORMAT_R (format);
+ g = PIXMAN_FORMAT_G (format);
+ b = PIXMAN_FORMAT_B (format);
+
+ switch (PIXMAN_FORMAT_TYPE (format)) {
+ case PIXMAN_TYPE_ARGB:
+ masks->alpha_mask = MASK (a) << (r + g + b);
+ masks->red_mask = MASK (r) << (g + b);
+ masks->green_mask = MASK (g) << (b);
+ masks->blue_mask = MASK (b);
+ return TRUE;
+ case PIXMAN_TYPE_ABGR:
+ masks->alpha_mask = MASK (a) << (b + g + r);
+ masks->blue_mask = MASK (b) << (g + r);
+ masks->green_mask = MASK (g) << (r);
+ masks->red_mask = MASK (r);
+ return TRUE;
+#ifdef PIXMAN_TYPE_BGRA
+ case PIXMAN_TYPE_BGRA:
+ masks->blue_mask = MASK (b) << (masks->bpp - b);
+ masks->green_mask = MASK (g) << (masks->bpp - b - g);
+ masks->red_mask = MASK (r) << (masks->bpp - b - g - r);
+ masks->alpha_mask = MASK (a);
+ return TRUE;
+#endif
+ case PIXMAN_TYPE_A:
+ masks->alpha_mask = MASK (a);
+ masks->red_mask = 0;
+ masks->green_mask = 0;
+ masks->blue_mask = 0;
+ return TRUE;
+ case PIXMAN_TYPE_OTHER:
+ case PIXMAN_TYPE_COLOR:
+ case PIXMAN_TYPE_GRAY:
+ case PIXMAN_TYPE_YUY2:
+ case PIXMAN_TYPE_YV12:
+ default:
+ masks->alpha_mask = 0;
+ masks->red_mask = 0;
+ masks->green_mask = 0;
+ masks->blue_mask = 0;
+ return FALSE;
+ }
+}
+
+pixman_format_code_t
+_cairo_format_to_pixman_format_code (cairo_format_t format)
+{
+ pixman_format_code_t ret;
+ switch (format) {
+ case CAIRO_FORMAT_A1:
+ ret = PIXMAN_a1;
+ break;
+ case CAIRO_FORMAT_A8:
+ ret = PIXMAN_a8;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ ret = PIXMAN_x8r8g8b8;
+ break;
+ case CAIRO_FORMAT_RGB30:
+ ret = PIXMAN_x2r10g10b10;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ ret = PIXMAN_r5g6b5;
+ break;
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ret = PIXMAN_a8r8g8b8;
+ break;
+ }
+ return ret;
+}
+
+cairo_surface_t *
+_cairo_image_surface_create_with_pixman_format (unsigned char *data,
+ pixman_format_code_t pixman_format,
+ int width,
+ int height,
+ int stride)
+{
+ cairo_surface_t *surface;
+ pixman_image_t *pixman_image;
+
+ if (! _cairo_image_surface_is_size_valid (width, height))
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ }
+
+ pixman_image = pixman_image_create_bits (pixman_format, width, height,
+ (uint32_t *) data, stride);
+
+ if (unlikely (pixman_image == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface = _cairo_image_surface_create_for_pixman_image (pixman_image,
+ pixman_format);
+ if (unlikely (surface->status)) {
+ pixman_image_unref (pixman_image);
+ return surface;
+ }
+
+ /* we can not make any assumptions about the initial state of user data */
+ surface->is_clear = data == NULL;
+ return surface;
+}
+
+/**
+ * cairo_image_surface_create:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates an image surface of the specified format and
+ * dimensions. Initially the surface contents are 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.
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_image_surface_create (cairo_format_t format,
+ int width,
+ int height)
+{
+ pixman_format_code_t pixman_format;
+
+ if (! CAIRO_FORMAT_VALID (format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ pixman_format = _cairo_format_to_pixman_format_code (format);
+
+ return _cairo_image_surface_create_with_pixman_format (NULL, pixman_format,
+ width, height, -1);
+}
+slim_hidden_def (cairo_image_surface_create);
+
+ cairo_surface_t *
+_cairo_image_surface_create_with_content (cairo_content_t content,
+ int width,
+ int height)
+{
+ return cairo_image_surface_create (_cairo_format_from_content (content),
+ width, height);
+}
+
+/**
+ * cairo_format_stride_for_width:
+ * @format: A #cairo_format_t value
+ * @width: The desired width of an image surface to be created.
+ *
+ * This function provides a stride value that will respect all
+ * alignment requirements of the accelerated image-rendering code
+ * within cairo. Typical usage will be of the form:
+ *
+ * <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.
+ *
+ * Since: 1.0
+ **/
+ cairo_surface_t *
+cairo_image_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride)
+{
+ pixman_format_code_t pixman_format;
+ int minstride;
+
+ if (! CAIRO_FORMAT_VALID (format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+ if (! _cairo_image_surface_is_size_valid (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ minstride = cairo_format_stride_for_width (format, width);
+ if (stride < 0) {
+ if (stride > -minstride) {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+ }
+ } else {
+ if (stride < minstride) {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+ }
+ }
+
+ pixman_format = _cairo_format_to_pixman_format_code (format);
+ return _cairo_image_surface_create_with_pixman_format (data,
+ pixman_format,
+ width, height,
+ stride);
+}
+slim_hidden_def (cairo_image_surface_create_for_data);
+
+/**
+ * cairo_image_surface_get_data:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get a pointer to the data of the image surface, for direct
+ * inspection or modification.
+ *
+ * A call to cairo_surface_flush() is required before accessing the
+ * pixel data to ensure that all pending drawing operations are
+ * finished. A call to cairo_surface_mark_dirty() is required after
+ * the data is modified.
+ *
+ * Return value: a pointer to the image data of this surface or %NULL
+ * if @surface is not an image surface, or if cairo_surface_finish()
+ * has been called.
+ *
+ * Since: 1.2
+ **/
+unsigned char *
+cairo_image_surface_get_data (cairo_surface_t *surface)
+{
+ cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+ if (! _cairo_surface_is_image (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return image_surface->data;
+}
+slim_hidden_def (cairo_image_surface_get_data);
+
+/**
+ * cairo_image_surface_get_format:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the format of the surface.
+ *
+ * Return value: the format of the surface
+ *
+ * Since: 1.2
+ **/
+cairo_format_t
+cairo_image_surface_get_format (cairo_surface_t *surface)
+{
+ cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+ if (! _cairo_surface_is_image (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return CAIRO_FORMAT_INVALID;
+ }
+
+ return image_surface->format;
+}
+slim_hidden_def (cairo_image_surface_get_format);
+
+/**
+ * cairo_image_surface_get_width:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the width of the image surface in pixels.
+ *
+ * Return value: the width of the surface in pixels.
+ *
+ * Since: 1.0
+ **/
+int
+cairo_image_surface_get_width (cairo_surface_t *surface)
+{
+ cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+ if (! _cairo_surface_is_image (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return image_surface->width;
+}
+slim_hidden_def (cairo_image_surface_get_width);
+
+/**
+ * cairo_image_surface_get_height:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the height of the image surface in pixels.
+ *
+ * Return value: the height of the surface in pixels.
+ *
+ * Since: 1.0
+ **/
+int
+cairo_image_surface_get_height (cairo_surface_t *surface)
+{
+ cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+ if (! _cairo_surface_is_image (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return image_surface->height;
+}
+slim_hidden_def (cairo_image_surface_get_height);
+
+/**
+ * cairo_image_surface_get_stride:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the stride of the image surface in bytes
+ *
+ * Return value: the stride of the image surface in bytes (or 0 if
+ * @surface is not an image surface). The stride is the distance in
+ * bytes from the beginning of one row of the image data to the
+ * beginning of the next row.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_image_surface_get_stride (cairo_surface_t *surface)
+{
+
+ cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+ if (! _cairo_surface_is_image (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return image_surface->stride;
+}
+slim_hidden_def (cairo_image_surface_get_stride);
+
+ cairo_format_t
+_cairo_format_from_content (cairo_content_t content)
+{
+ switch (content) {
+ case CAIRO_CONTENT_COLOR:
+ return CAIRO_FORMAT_RGB24;
+ case CAIRO_CONTENT_ALPHA:
+ return CAIRO_FORMAT_A8;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ return CAIRO_FORMAT_ARGB32;
+ }
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_FORMAT_INVALID;
+}
+
+ cairo_content_t
+_cairo_content_from_format (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ case CAIRO_FORMAT_RGB30:
+ return CAIRO_CONTENT_COLOR;
+ case CAIRO_FORMAT_RGB24:
+ return CAIRO_CONTENT_COLOR;
+ case CAIRO_FORMAT_RGB16_565:
+ return CAIRO_CONTENT_COLOR;
+ case CAIRO_FORMAT_A8:
+ case CAIRO_FORMAT_A1:
+ return CAIRO_CONTENT_ALPHA;
+ case CAIRO_FORMAT_INVALID:
+ break;
+ }
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_CONTENT_COLOR_ALPHA;
+}
+
+ int
+_cairo_format_bits_per_pixel (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_RGB24:
+ return 32;
+ case CAIRO_FORMAT_RGB16_565:
+ return 16;
+ case CAIRO_FORMAT_A8:
+ return 8;
+ case CAIRO_FORMAT_A1:
+ return 1;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ return 0;
+ }
+}
+
+cairo_surface_t *
+_cairo_image_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_image_surface_t *other = abstract_other;
+
+ TRACE ((stderr, "%s (other=%u)\n", __FUNCTION__, other->base.unique_id));
+
+ if (! _cairo_image_surface_is_size_valid (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ if (content == other->base.content) {
+ return _cairo_image_surface_create_with_pixman_format (NULL,
+ other->pixman_format,
+ width, height,
+ 0);
+ }
+
+ return _cairo_image_surface_create_with_content (content,
+ width, height);
+}
+
+cairo_surface_t *
+_cairo_image_surface_snapshot (void *abstract_surface)
+{
+ cairo_image_surface_t *image = abstract_surface;
+ cairo_image_surface_t *clone;
+
+ /* If we own the image, we can simply steal the memory for the snapshot */
+ if (image->owns_data && image->base._finishing) {
+ clone = (cairo_image_surface_t *)
+ _cairo_image_surface_create_for_pixman_image (image->pixman_image,
+ image->pixman_format);
+ if (unlikely (clone->base.status))
+ return &clone->base;
+
+ image->pixman_image = NULL;
+
+ clone->transparency = image->transparency;
+ clone->color = image->color;
+
+ clone->owns_data = image->owns_data;
+ image->owns_data = FALSE;
+ return &clone->base;
+ }
+
+ clone = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ image->pixman_format,
+ image->width,
+ image->height,
+ 0);
+ if (unlikely (clone->base.status))
+ return &clone->base;
+
+ if (clone->stride == image->stride) {
+ memcpy (clone->data, image->data, clone->stride * clone->height);
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, clone->pixman_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ image->width, image->height);
+ }
+ clone->base.is_clear = FALSE;
+ return &clone->base;
+}
+
+cairo_image_surface_t *
+_cairo_image_surface_map_to_image (void *abstract_other,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_image_surface_t *other = abstract_other;
+ cairo_surface_t *surface;
+ uint8_t *data;
+
+ data = other->data;
+ data += extents->y * other->stride;
+ data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8;
+
+ surface =
+ _cairo_image_surface_create_with_pixman_format (data,
+ other->pixman_format,
+ extents->width,
+ extents->height,
+ other->stride);
+
+ cairo_surface_set_device_offset (surface, -extents->x, -extents->y);
+ return (cairo_image_surface_t *) surface;
+}
+
+cairo_int_status_t
+_cairo_image_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_image_surface_finish (void *abstract_surface)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (surface->pixman_image) {
+ pixman_image_unref (surface->pixman_image);
+ surface->pixman_image = NULL;
+ }
+
+ if (surface->owns_data) {
+ free (surface->data);
+ surface->data = NULL;
+ }
+
+ if (surface->parent) {
+ cairo_surface_destroy (surface->parent);
+ surface->parent = NULL;
+ }
+
+ _cairo_image_shadow_caches_destroy ();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface)
+{
+ surface->owns_data = TRUE;
+}
+
+cairo_surface_t *
+_cairo_image_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (extents) {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+cairo_status_t
+_cairo_image_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ *image_out = abstract_surface;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_image_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+}
+
+/* high level image interface */
+cairo_bool_t
+_cairo_image_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+cairo_int_status_t
+_cairo_image_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->base.unique_id));
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_paint (abstract_surface, op, source,
+ clip, &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ status = _cairo_compositor_paint (surface->compositor,
+ &surface->base, op, source, clip);
+ cairo_device_release (surface->base.device);
+ return status;
+}
+
+cairo_int_status_t
+_cairo_image_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->base.unique_id));
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_mask (abstract_surface, op, source,
+ mask, clip, &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ status = _cairo_compositor_mask (surface->compositor,
+ &surface->base, op, source, mask, clip);
+ cairo_device_release (surface->base.device);
+ return status;
+}
+
+cairo_int_status_t
+_cairo_image_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->base.unique_id));
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (abstract_surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, clip,
+ &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_stroke (surface->compositor, &surface->base,
+ op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (abstract_surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, clip,
+ &source->shadow);
+
+ cairo_device_release (surface->base.device);
+ return status;
+}
+
+cairo_int_status_t
+_cairo_image_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->base.unique_id));
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (abstract_surface, op, source,
+ path, fill_rule,
+ tolerance, antialias, clip,
+ &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only) {
+ if (! source->shadow.path_is_fill_with_spread ||
+ source->shadow.type != CAIRO_SHADOW_INSET)
+ status = _cairo_compositor_fill (surface->compositor,
+ &surface->base,
+ op, source, path,
+ fill_rule, tolerance,
+ antialias,
+ clip);
+ else
+ status = _cairo_compositor_paint (surface->compositor,
+ &surface->base,
+ op, source, clip);
+ }
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (abstract_surface, op, source,
+ path, fill_rule,
+ tolerance, antialias, clip,
+ &source->shadow);
+
+ cairo_device_release (surface->base.device);
+ return status;
+}
+
+cairo_int_status_t
+_cairo_image_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->base.unique_id));
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (abstract_surface, op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_glyphs (surface->compositor, &surface->base,
+ op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (abstract_surface, op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &source->shadow);
+
+ cairo_device_release (surface->base.device);
+ return status;
+}
+
+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);
+}
+
+static cairo_surface_t *
+_cairo_image_surface_shadow_surface (void *surface,
+ cairo_bool_t has_blur,
+ int width, int height,
+ int *width_out, int *height_out)
+{
+ int shadow_width, shadow_height;
+ cairo_image_surface_t *shadow_surface = NULL;
+
+ if (width < MIN_IMAGE_SHADOW_SIZE)
+ shadow_width = width;
+ else if (has_blur) {
+ if (width < MIN_IMAGE_SHADOW_SIZE * 2)
+ shadow_width = MIN_IMAGE_SHADOW_SIZE;
+ else if (width > MAX_IMAGE_SHADOW_SIZE * 2)
+ shadow_width = MAX_IMAGE_SHADOW_SIZE;
+ else
+ shadow_width = width * 0.5;
+ }
+ else {
+ if (width > MAX_IMAGE_SHADOW_SIZE)
+ shadow_width = MAX_IMAGE_SHADOW_SIZE;
+ else
+ shadow_width = width;
+ }
+
+ if (height < MIN_IMAGE_SHADOW_SIZE)
+ shadow_height = height;
+ else if (has_blur) {
+ if (height < MIN_IMAGE_SHADOW_SIZE * 2)
+ shadow_height = MIN_IMAGE_SHADOW_SIZE;
+ else if (height > MAX_IMAGE_SHADOW_SIZE * 2)
+ shadow_height = MAX_IMAGE_SHADOW_SIZE;
+ else
+ shadow_height = height * 0.5;
+ }
+ else {
+ if (height > MAX_IMAGE_SHADOW_SIZE)
+ shadow_height = MAX_IMAGE_SHADOW_SIZE;
+ else
+ shadow_height = height;
+ }
+
+ shadow_surface = (cairo_image_surface_t *)
+ cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ shadow_width,
+ shadow_height);
+ if (unlikely (shadow_surface->base.status)) {
+ cairo_surface_destroy (&shadow_surface->base);
+ return NULL;
+ }
+
+ *width_out = shadow_width;
+ *height_out = shadow_height;
+
+ return &shadow_surface->base;
+}
+
+const cairo_surface_backend_t _cairo_image_surface_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_image_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_image_surface_create_similar,
+ NULL, /* create similar image */
+ _cairo_image_surface_map_to_image,
+ _cairo_image_surface_unmap_image,
+
+ _cairo_image_surface_source,
+ _cairo_image_surface_acquire_source_image,
+ _cairo_image_surface_release_source_image,
+ _cairo_image_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_image_surface_get_extents,
+ _cairo_image_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL,
+
+ _cairo_image_surface_paint,
+ _cairo_image_surface_mask,
+ _cairo_image_surface_stroke,
+ _cairo_image_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_image_surface_glyphs,
+ NULL, /* has_text_glyphs */
+ NULL, /* show_text_glyphs */
+ NULL, /* get_supported_mime_types */
+ _cairo_image_surface_shadow_surface,
+ NULL, /* get_glyph_shadow_surface */
+ NULL, /* get_shadow_mask_surface */
+ NULL, /* get_glyph_shadow_mask_surface */
+ _cairo_image_surface_shadow_cache_acquire,
+ _cairo_image_surface_shadow_cache_release,
+ _cairo_image_surface_get_shadow_cache,
+ _cairo_image_surface_get_shadow_cache_size,
+ _cairo_image_surface_has_shadow_cache,
+};
+
+/* A convenience function for when one needs to coerce an image
+ * surface to an alternate format. */
+cairo_image_surface_t *
+_cairo_image_surface_coerce (cairo_image_surface_t *surface)
+{
+ return _cairo_image_surface_coerce_to_format (surface,
+ _cairo_format_from_content (surface->base.content));
+}
+
+/* A convenience function for when one needs to coerce an image
+ * surface to an alternate format. */
+cairo_image_surface_t *
+_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface,
+ cairo_format_t format)
+{
+ cairo_image_surface_t *clone;
+ cairo_status_t status;
+
+ status = surface->base.status;
+ if (unlikely (status))
+ return (cairo_image_surface_t *)_cairo_surface_create_in_error (status);
+
+ if (surface->format == format)
+ return (cairo_image_surface_t *)cairo_surface_reference(&surface->base);
+
+ clone = (cairo_image_surface_t *)
+ cairo_image_surface_create (format, surface->width, surface->height);
+ if (unlikely (clone->base.status))
+ return clone;
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ surface->pixman_image, NULL, clone->pixman_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ surface->width, surface->height);
+ clone->base.is_clear = FALSE;
+
+ clone->base.device_transform =
+ surface->base.device_transform;
+ clone->base.device_transform_inverse =
+ surface->base.device_transform_inverse;
+
+ return clone;
+}
+
+cairo_image_surface_t *
+_cairo_image_surface_create_from_image (cairo_image_surface_t *other,
+ pixman_format_code_t format,
+ int x, int y,
+ int width, int height, int stride)
+{
+ cairo_image_surface_t *surface = NULL;
+ cairo_status_t status;
+ pixman_image_t *image;
+ void *mem = NULL;
+
+ status = other->base.status;
+ if (unlikely (status))
+ goto cleanup;
+
+ if (stride) {
+ mem = _cairo_malloc_ab (height, stride);
+ if (unlikely (mem == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+ }
+
+ image = pixman_image_create_bits (format, width, height, mem, stride);
+ if (unlikely (image == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup_mem;
+ }
+
+ surface = (cairo_image_surface_t *)
+ _cairo_image_surface_create_for_pixman_image (image, format);
+ if (unlikely (surface->base.status)) {
+ status = surface->base.status;
+ goto cleanup_image;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ other->pixman_image, NULL, image,
+ x, y,
+ 0, 0,
+ 0, 0,
+ width, height);
+ surface->base.is_clear = FALSE;
+ surface->owns_data = mem != NULL;
+
+ return surface;
+
+cleanup_image:
+ pixman_image_unref (image);
+cleanup_mem:
+ free (mem);
+cleanup:
+ cairo_surface_destroy (&surface->base);
+ return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
+}
+
+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.is_clear)
+ return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+
+ if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) {
+ if (image->format == CAIRO_FORMAT_A1) {
+ return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+ } else if (image->format == CAIRO_FORMAT_A8) {
+ for (y = 0; y < image->height; y++) {
+ uint8_t *alpha = (uint8_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < image->width; x++, alpha++) {
+ if (*alpha > 0 && *alpha < 255)
+ return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+ }
+ }
+ 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;
+}
+
+cairo_image_color_t
+_cairo_image_analyze_color (cairo_image_surface_t *image)
+{
+ int x, y;
+
+ if (image->color != CAIRO_IMAGE_UNKNOWN_COLOR)
+ return image->color;
+
+ if (image->format == CAIRO_FORMAT_A1)
+ return image->color = CAIRO_IMAGE_IS_MONOCHROME;
+
+ if (image->format == CAIRO_FORMAT_A8)
+ return image->color = CAIRO_IMAGE_IS_GRAYSCALE;
+
+ if (image->format == CAIRO_FORMAT_ARGB32) {
+ image->color = CAIRO_IMAGE_IS_MONOCHROME;
+ for (y = 0; y < image->height; y++) {
+ uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < image->width; x++, pixel++) {
+ int a = (*pixel & 0xff000000) >> 24;
+ int r = (*pixel & 0x00ff0000) >> 16;
+ int g = (*pixel & 0x0000ff00) >> 8;
+ int b = (*pixel & 0x000000ff);
+ if (a == 0) {
+ r = g = b = 0;
+ } else {
+ r = (r * 255 + a / 2) / a;
+ g = (g * 255 + a / 2) / a;
+ b = (b * 255 + a / 2) / a;
+ }
+ if (!(r == g && g == b))
+ return image->color = CAIRO_IMAGE_IS_COLOR;
+ else if (r > 0 && r < 255)
+ image->color = CAIRO_IMAGE_IS_GRAYSCALE;
+ }
+ }
+ return image->color;
+ }
+
+ if (image->format == CAIRO_FORMAT_RGB24) {
+ image->color = CAIRO_IMAGE_IS_MONOCHROME;
+ for (y = 0; y < image->height; y++) {
+ uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < image->width; x++, pixel++) {
+ int r = (*pixel & 0x00ff0000) >> 16;
+ int g = (*pixel & 0x0000ff00) >> 8;
+ int b = (*pixel & 0x000000ff);
+ if (!(r == g && g == b))
+ return image->color = CAIRO_IMAGE_IS_COLOR;
+ else if (r > 0 && r < 255)
+ image->color = CAIRO_IMAGE_IS_GRAYSCALE;
+ }
+ }
+ return image->color;
+ }
+
+ return image->color = CAIRO_IMAGE_IS_COLOR;
+}
+
+cairo_image_surface_t *
+_cairo_image_surface_clone_subimage (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *image;
+ cairo_surface_pattern_t pattern;
+ cairo_status_t status;
+
+ image = cairo_surface_create_similar_image (surface,
+ _cairo_format_from_content (surface->content),
+ extents->width,
+ extents->height);
+ if (image->status)
+ return to_image_surface (image);
+
+ /* TODO: check me with non-identity device_transform. Should we
+ * clone the scaling, too? */
+ cairo_surface_set_device_offset (image,
+ -extents->x,
+ -extents->y);
+
+ _cairo_pattern_init_for_surface (&pattern, surface);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+ status = _cairo_surface_paint (image,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base,
+ NULL);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status))
+ goto error;
+
+ /* We use the parent as a flag during map-to-image/umap-image that the
+ * resultant image came from a fallback rather than as direct call
+ * to the backend's map_to_image(). Whilst we use it as a simple flag,
+ * we need to make sure the parent surface obeys the reference counting
+ * semantics and is consistent for all callers.
+ */
+ _cairo_image_surface_set_parent (to_image_surface (image),
+ cairo_surface_reference (surface));
+
+ return to_image_surface (image);
+
+error:
+ cairo_surface_destroy (image);
+ return to_image_surface (_cairo_surface_create_in_error (status));
+}
diff --git a/src/cairo-list-inline.h b/src/cairo-list-inline.h
new file mode 100755
index 000000000..d00f40e98
--- /dev/null
+++ b/src/cairo-list-inline.h
@@ -0,0 +1,209 @@
+/* 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_INLINE_H
+#define CAIRO_LIST_INLINE_H
+
+#include "cairo-list-private.h"
+
+#define cairo_list_entry(ptr, type, member) \
+ cairo_container_of(ptr, type, member)
+
+#define cairo_list_first_entry(ptr, type, member) \
+ cairo_list_entry((ptr)->next, type, member)
+
+#define cairo_list_last_entry(ptr, type, member) \
+ cairo_list_entry((ptr)->prev, type, member)
+
+#define cairo_list_foreach(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define cairo_list_foreach_entry(pos, type, head, member) \
+ for (pos = cairo_list_entry((head)->next, type, member);\
+ &pos->member != (head); \
+ pos = cairo_list_entry(pos->member.next, type, member))
+
+#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \
+ for (pos = cairo_list_entry ((head)->next, type, member),\
+ n = cairo_list_entry (pos->member.next, type, member);\
+ &pos->member != (head); \
+ pos = n, n = cairo_list_entry (n->member.next, type, member))
+
+#define cairo_list_foreach_entry_reverse(pos, type, head, member) \
+ for (pos = cairo_list_entry((head)->prev, type, member);\
+ &pos->member != (head); \
+ pos = cairo_list_entry(pos->member.prev, type, member))
+
+#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \
+ for (pos = cairo_list_entry((head)->prev, type, member),\
+ n = cairo_list_entry (pos->member.prev, type, member);\
+ &pos->member != (head); \
+ pos = n, n = cairo_list_entry (n->member.prev, type, member))
+
+#ifdef CAIRO_LIST_DEBUG
+static inline void
+_cairo_list_validate (const cairo_list_t *link)
+{
+ assert (link->next->prev == link);
+ assert (link->prev->next == link);
+}
+static inline void
+cairo_list_validate (const cairo_list_t *head)
+{
+ cairo_list_t *link;
+
+ cairo_list_foreach (link, head)
+ _cairo_list_validate (link);
+}
+static inline cairo_bool_t
+cairo_list_is_empty (const cairo_list_t *head);
+static inline void
+cairo_list_validate_is_empty (const cairo_list_t *head)
+{
+ assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev));
+}
+#else
+#define _cairo_list_validate(link)
+#define cairo_list_validate(head)
+#define cairo_list_validate_is_empty(head)
+#endif
+
+static inline void
+cairo_list_init (cairo_list_t *entry)
+{
+ entry->next = entry;
+ entry->prev = entry;
+}
+
+static inline void
+__cairo_list_add (cairo_list_t *entry,
+ cairo_list_t *prev,
+ cairo_list_t *next)
+{
+ next->prev = entry;
+ entry->next = next;
+ entry->prev = prev;
+ prev->next = entry;
+}
+
+static inline void
+cairo_list_add (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ cairo_list_validate_is_empty (entry);
+ __cairo_list_add (entry, head, head->next);
+ cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ cairo_list_validate_is_empty (entry);
+ __cairo_list_add (entry, head->prev, head);
+ cairo_list_validate (head);
+}
+
+static inline void
+__cairo_list_del (cairo_list_t *prev, cairo_list_t *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void
+cairo_list_del (cairo_list_t *entry)
+{
+ __cairo_list_del (entry->prev, entry->next);
+ cairo_list_init (entry);
+}
+
+static inline void
+cairo_list_move (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ __cairo_list_del (entry->prev, entry->next);
+ __cairo_list_add (entry, head, head->next);
+ cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ __cairo_list_del (entry->prev, entry->next);
+ __cairo_list_add (entry, head->prev, head);
+ cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_swap (cairo_list_t *entry, cairo_list_t *other)
+{
+ __cairo_list_add (entry, other->prev, other->next);
+ cairo_list_init (other);
+}
+
+static inline cairo_bool_t
+cairo_list_is_first (const cairo_list_t *entry,
+ const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return entry->prev == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_last (const cairo_list_t *entry,
+ const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return entry->next == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_empty (const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return head->next == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_singular (const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return head->next == head || head->next == head->prev;
+}
+
+#endif /* CAIRO_LIST_INLINE_H */
diff --git a/src/cairo-list-private.h b/src/cairo-list-private.h
new file mode 100755
index 000000000..9f39b668f
--- /dev/null
+++ b/src/cairo-list-private.h
@@ -0,0 +1,48 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <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;
+
+#endif /* CAIRO_LIST_PRIVATE_H */
diff --git a/src/cairo-lzw.c b/src/cairo-lzw.c
new file mode 100755
index 000000000..de7f99983
--- /dev/null
+++ b/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/src/cairo-malloc-private.h b/src/cairo-malloc-private.h
new file mode 100755
index 000000000..1e2c67f8d
--- /dev/null
+++ b/src/cairo-malloc-private.h
@@ -0,0 +1,149 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#ifndef CAIRO_MALLOC_PRIVATE_H
+#define CAIRO_MALLOC_PRIVATE_H
+
+#include "cairo-wideint-private.h"
+#include <stdlib.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:
+ * @a: number of elements to allocate
+ * @size: size of each element
+ *
+ * Allocates @a*@size memory using _cairo_malloc(), taking care to not
+ * overflow when doing the multiplication. Behaves much like
+ * calloc(), except that the returned memory is not set to zero.
+ * The memory should be freed using free().
+ *
+ * @size should be a constant so that the compiler can optimize
+ * out a constant division.
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or overflow.
+ **/
+
+#define _cairo_malloc_ab(a, size) \
+ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
+ _cairo_malloc((unsigned) (a) * (unsigned) (size)))
+
+/**
+ * _cairo_realloc_ab:
+ * @ptr: original pointer to block of memory to be resized
+ * @a: number of elements to allocate
+ * @size: size of each element
+ *
+ * Reallocates @ptr a block of @a*@size memory using realloc(), taking
+ * care to not overflow when doing the multiplication. The memory
+ * should be freed using free().
+ *
+ * @size should be a constant so that the compiler can optimize
+ * out a constant division.
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of realloc() failure or overflow (whereupon the original block
+ * of memory * is left untouched).
+ **/
+
+#define _cairo_realloc_ab(ptr, a, size) \
+ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
+ realloc(ptr, (unsigned) (a) * (unsigned) (size)))
+
+/**
+ * _cairo_malloc_abc:
+ * @a: first factor of number of elements to allocate
+ * @b: second factor of number of elements to allocate
+ * @size: size of each element
+ *
+ * Allocates @a*@b*@size memory using _cairo_malloc(), taking care to not
+ * overflow when doing the multiplication. Behaves like
+ * _cairo_malloc_ab(). The memory should be freed using free().
+ *
+ * @size should be a constant so that the compiler can optimize
+ * out a constant division.
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or overflow.
+ **/
+
+#define _cairo_malloc_abc(a, b, size) \
+ ((b) && (unsigned) (a) >= INT32_MAX / (unsigned) (b) ? NULL : \
+ (size) && (unsigned) ((a)*(b)) >= INT32_MAX / (unsigned) (size) ? NULL : \
+ _cairo_malloc((unsigned) (a) * (unsigned) (b) * (unsigned) (size)))
+
+/**
+ * _cairo_malloc_ab_plus_c:
+ * @a: number of elements to allocate
+ * @size: size of each element
+ * @c: additional size to allocate
+ *
+ * Allocates @a*@size+@c memory using _cairo_malloc(), taking care to not
+ * overflow when doing the arithmetic. Behaves similar to
+ * _cairo_malloc_ab(). The memory should be freed using free().
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or overflow.
+ **/
+
+#define _cairo_malloc_ab_plus_c(a, size, c) \
+ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
+ (unsigned) (c) >= INT32_MAX - (unsigned) (a) * (unsigned) (size) ? NULL : \
+ _cairo_malloc((unsigned) (a) * (unsigned) (size) + (unsigned) (c)))
+
+#endif /* CAIRO_MALLOC_PRIVATE_H */
diff --git a/src/cairo-mask-compositor.c b/src/cairo-mask-compositor.c
new file mode 100755
index 000000000..31d116119
--- /dev/null
+++ b/src/cairo-mask-compositor.c
@@ -0,0 +1,1498 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This compositor renders the shape to a mask using an image surface
+ * then calls composite.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-inline.h"
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+typedef cairo_int_status_t
+(*draw_func_t) (const cairo_mask_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_rectangle_int_t *src_sample,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip);
+
+static void do_unaligned_row(void (*blt)(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage),
+ void *closure,
+ const cairo_box_t *b,
+ int tx, int y, int h,
+ uint16_t coverage)
+{
+ int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
+ int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
+ if (x2 > x1) {
+ if (! _cairo_fixed_is_integer (b->p1.x)) {
+ blt(closure, x1, y, 1, h,
+ coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
+ x1++;
+ }
+
+ if (x2 > x1)
+ blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
+
+ if (! _cairo_fixed_is_integer (b->p2.x))
+ blt(closure, x2, y, 1, h,
+ coverage * _cairo_fixed_fractional_part (b->p2.x));
+ } else
+ blt(closure, x1, y, 1, h,
+ coverage * (b->p2.x - b->p1.x));
+}
+
+static void do_unaligned_box(void (*blt)(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage),
+ void *closure,
+ const cairo_box_t *b, int tx, int ty)
+{
+ int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
+ int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
+ if (y2 > y1) {
+ if (! _cairo_fixed_is_integer (b->p1.y)) {
+ do_unaligned_row(blt, closure, b, tx, y1, 1,
+ 256 - _cairo_fixed_fractional_part (b->p1.y));
+ y1++;
+ }
+
+ if (y2 > y1)
+ do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
+
+ if (! _cairo_fixed_is_integer (b->p2.y))
+ do_unaligned_row(blt, closure, b, tx, y2, 1,
+ _cairo_fixed_fractional_part (b->p2.y));
+ } else
+ do_unaligned_row(blt, closure, b, tx, y1, 1,
+ b->p2.y - b->p1.y);
+}
+
+struct blt_in {
+ const cairo_mask_compositor_t *compositor;
+ cairo_surface_t *dst;
+};
+
+static void blt_in(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct blt_in *info = closure;
+ cairo_color_t color;
+ cairo_rectangle_int_t rect;
+
+ if (coverage == 0xffff)
+ return;
+
+ rect.x = x;
+ rect.y = y;
+ rect.width = w;
+ rect.height = h;
+
+ _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
+ info->compositor->fill_rectangles (info->dst, CAIRO_OPERATOR_IN,
+ &color, &rect, 1);
+}
+
+static cairo_surface_t *
+create_composite_mask (const cairo_mask_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *draw_closure,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *surface;
+ cairo_int_status_t status;
+ struct blt_in info;
+ int i;
+
+ surface = _cairo_surface_create_similar_scratch (dst, CAIRO_CONTENT_ALPHA,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (surface->status))
+ return surface;
+
+ status = compositor->acquire (surface);
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return _cairo_int_surface_create_in_error (status);
+ }
+
+ if (!surface->is_clear) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = rect.y = 0;
+ rect.width = extents->bounded.width;
+ rect.height = extents->bounded.height;
+
+ status = compositor->fill_rectangles (surface, CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &rect, 1);
+ if (unlikely (status))
+ goto error;
+ }
+
+ if (mask_func) {
+ status = mask_func (compositor, surface, draw_closure,
+ CAIRO_OPERATOR_SOURCE, NULL, NULL,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded, extents->clip);
+ if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
+ goto out;
+ }
+
+ /* Is it worth setting the clip region here? */
+ status = draw_func (compositor, surface, draw_closure,
+ CAIRO_OPERATOR_ADD, NULL, NULL,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded, NULL);
+ if (unlikely (status))
+ goto error;
+
+ info.compositor = compositor;
+ info.dst = surface;
+ for (i = 0; i < extents->clip->num_boxes; i++) {
+ cairo_box_t *b = &extents->clip->boxes[i];
+
+ if (! _cairo_fixed_is_integer (b->p1.x) ||
+ ! _cairo_fixed_is_integer (b->p1.y) ||
+ ! _cairo_fixed_is_integer (b->p2.x) ||
+ ! _cairo_fixed_is_integer (b->p2.y))
+ {
+ do_unaligned_box(blt_in, &info, b,
+ extents->bounded.x,
+ extents->bounded.y);
+ }
+ }
+
+ if (extents->clip->path != NULL) {
+ status = _cairo_clip_combine_with_surface (extents->clip, surface,
+ extents->bounded.x,
+ extents->bounded.y);
+ if (unlikely (status))
+ goto error;
+ }
+
+out:
+ compositor->release (surface);
+ surface->is_clear = FALSE;
+ return surface;
+
+error:
+ compositor->release (surface);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ cairo_surface_destroy (surface);
+ surface = _cairo_int_surface_create_in_error (status);
+ }
+ return surface;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+clip_and_composite_with_mask (const cairo_mask_compositor_t *compositor,
+ void *draw_closure,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ cairo_operator_t op,
+ cairo_pattern_t *pattern,
+ const cairo_composite_rectangles_t*extents)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_surface_t *mask, *src;
+ int src_x, src_y;
+
+ mask = create_composite_mask (compositor, dst, draw_closure,
+ draw_func, mask_func,
+ extents);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ if (pattern != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
+ src = compositor->pattern_to_surface (dst,
+ &extents->source_pattern.base,
+ FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (src->status)) {
+ cairo_surface_destroy (mask);
+ return src->status;
+ }
+
+ compositor->composite (dst, op, src, mask,
+ extents->bounded.x + src_x,
+ extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ cairo_surface_destroy (src);
+ } else {
+ compositor->composite (dst, op, mask, NULL,
+ 0, 0,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+ cairo_surface_destroy (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+get_clip_source (const cairo_mask_compositor_t *compositor,
+ cairo_clip_t *clip,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *bounds,
+ int *out_x, int *out_y)
+{
+ cairo_surface_pattern_t pattern;
+ cairo_rectangle_int_t r;
+ cairo_surface_t *surface;
+
+ surface = _cairo_clip_get_image (clip, dst, bounds);
+ if (unlikely (surface->status))
+ return surface;
+
+ _cairo_pattern_init_for_surface (&pattern, surface);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ cairo_surface_destroy (surface);
+
+ r.x = r.y = 0;
+ r.width = bounds->width;
+ r.height = bounds->height;
+
+ surface = compositor->pattern_to_surface (dst, &pattern.base, TRUE,
+ &r, &r, out_x, out_y);
+ _cairo_pattern_fini (&pattern.base);
+
+ *out_x += -bounds->x;
+ *out_y += -bounds->y;
+ return surface;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+clip_and_composite_combine (const cairo_mask_compositor_t *compositor,
+ void *draw_closure,
+ draw_func_t draw_func,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_composite_rectangles_t*extents)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_surface_t *tmp, *clip;
+ cairo_status_t status;
+ int clip_x, clip_y;
+
+ tmp = _cairo_surface_create_similar_scratch (dst, dst->content,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (tmp->status)) {
+ status = tmp->status;
+ goto cleanup;
+ }
+
+ compositor->composite (tmp, CAIRO_OPERATOR_SOURCE, dst, NULL,
+ extents->bounded.x, extents->bounded.y,
+ 0, 0,
+ 0, 0,
+ extents->bounded.width, extents->bounded.height);
+
+ status = draw_func (compositor, tmp, draw_closure, op,
+ pattern, &extents->source_sample_area,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded, NULL);
+ if (unlikely (status))
+ goto cleanup;
+
+ clip = get_clip_source (compositor,
+ extents->clip, dst, &extents->bounded,
+ &clip_x, &clip_y);
+ if (unlikely ((status = clip->status))) {
+ cairo_surface_destroy (clip);
+ goto cleanup;
+ }
+
+ if (dst->is_clear) {
+ compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
+ 0, 0,
+ clip_x, clip_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ } else {
+ /* Punch the clip out of the destination */
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL,
+ clip_x, clip_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ /* Now add the two results together */
+ compositor->composite (dst, CAIRO_OPERATOR_ADD, tmp, clip,
+ 0, 0,
+ clip_x, clip_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+ cairo_surface_destroy (clip);
+
+cleanup:
+ cairo_surface_destroy (tmp);
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+clip_and_composite_source (const cairo_mask_compositor_t *compositor,
+ void *draw_closure,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ cairo_pattern_t *pattern,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_surface_t *mask, *src;
+ int src_x, src_y;
+
+ /* Create a surface that is mask IN clip */
+ mask = create_composite_mask (compositor, dst, draw_closure,
+ draw_func, mask_func,
+ extents);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ src = compositor->pattern_to_surface (dst,
+ pattern,
+ FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (src->status)) {
+ cairo_surface_destroy (mask);
+ return src->status;
+ }
+
+ if (dst->is_clear) {
+ compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ } else {
+ /* Compute dest' = dest OUT (mask IN clip) */
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ 0, 0, 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ /* Now compute (src IN (mask IN clip)) ADD dest' */
+ compositor->composite (dst, CAIRO_OPERATOR_ADD, src, mask,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+
+ cairo_surface_destroy (src);
+ cairo_surface_destroy (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+ int iop = op;
+ switch (iop) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ return dst->is_clear &&
+ dst->content == CAIRO_CONTENT_ALPHA &&
+ _cairo_pattern_is_opaque_solid (pattern) &&
+ can_reduce_alpha_op (op);
+}
+
+static cairo_status_t
+fixup_unbounded (const cairo_mask_compositor_t *compositor,
+ cairo_surface_t *dst,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_rectangle_int_t rects[4];
+ int n;
+
+ if (extents->bounded.width == extents->unbounded.width &&
+ extents->bounded.height == extents->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ n = 0;
+ if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+ rects[n].x = extents->unbounded.x;
+ rects[n].width = extents->unbounded.width;
+ rects[n].y = extents->unbounded.y;
+ rects[n].height = extents->unbounded.height;
+ n++;
+ } else {
+ /* top */
+ if (extents->bounded.y != extents->unbounded.y) {
+ rects[n].x = extents->unbounded.x;
+ rects[n].width = extents->unbounded.width;
+ rects[n].y = extents->unbounded.y;
+ rects[n].height = extents->bounded.y - extents->unbounded.y;
+ n++;
+ }
+ /* left */
+ if (extents->bounded.x != extents->unbounded.x) {
+ rects[n].x = extents->unbounded.x;
+ rects[n].width = extents->bounded.x - extents->unbounded.x;
+ rects[n].y = extents->bounded.y;
+ rects[n].height = extents->bounded.height;
+ n++;
+ }
+ /* right */
+ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+ rects[n].x = extents->bounded.x + extents->bounded.width;
+ rects[n].width = extents->unbounded.x + extents->unbounded.width - rects[n].x;
+ rects[n].y = extents->bounded.y;
+ rects[n].height = extents->bounded.height;
+ n++;
+ }
+ /* bottom */
+ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+ rects[n].x = extents->unbounded.x;
+ rects[n].width = extents->unbounded.width;
+ rects[n].y = extents->bounded.y + extents->bounded.height;
+ rects[n].height = extents->unbounded.y + extents->unbounded.height - rects[n].y;
+ n++;
+ }
+ }
+
+ return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ rects, n);
+}
+
+static cairo_status_t
+fixup_unbounded_with_mask (const cairo_mask_compositor_t *compositor,
+ cairo_surface_t *dst,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *mask;
+ int mask_x, mask_y;
+
+ mask = get_clip_source (compositor,
+ extents->clip, dst, &extents->unbounded,
+ &mask_x, &mask_y);
+ if (unlikely (mask->status)) {
+ cairo_status_t status = mask->status;
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ /* top */
+ if (extents->bounded.y != extents->unbounded.y) {
+ int x = extents->unbounded.x;
+ int y = extents->unbounded.y;
+ int width = extents->unbounded.width;
+ int height = extents->bounded.y - y;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* left */
+ if (extents->bounded.x != extents->unbounded.x) {
+ int x = extents->unbounded.x;
+ int y = extents->bounded.y;
+ int width = extents->bounded.x - x;
+ int height = extents->bounded.height;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* right */
+ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+ int x = extents->bounded.x + extents->bounded.width;
+ int y = extents->bounded.y;
+ int width = extents->unbounded.x + extents->unbounded.width - x;
+ int height = extents->bounded.height;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* bottom */
+ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+ int x = extents->unbounded.x;
+ int y = extents->bounded.y + extents->bounded.height;
+ int width = extents->unbounded.width;
+ int height = extents->unbounded.y + extents->unbounded.height - y;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ cairo_surface_destroy (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fixup_unbounded_boxes (const cairo_mask_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_boxes_t clear;
+ cairo_region_t *clip_region;
+ cairo_box_t box;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ assert (boxes->is_pixel_aligned);
+
+ clip_region = NULL;
+ if (_cairo_clip_is_region (extents->clip) &&
+ (clip_region = _cairo_clip_get_region (extents->clip)) &&
+ cairo_region_contains_rectangle (clip_region,
+ &extents->bounded) == CAIRO_REGION_OVERLAP_IN)
+ clip_region = NULL;
+
+
+ if (boxes->num_boxes <= 1 && clip_region == NULL)
+ return fixup_unbounded (compositor, dst, extents);
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (clip_region == NULL) {
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+
+ tmp.chunks.next = NULL;
+ } else {
+ pixman_box32_t *pbox;
+
+ pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+ _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+ status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (&clear,
+ CAIRO_ANTIALIAS_DEFAULT,
+ &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&clear);
+ return status;
+ }
+ }
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ }
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = compositor->fill_boxes (dst,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+ }
+
+ _cairo_boxes_fini (&clear);
+
+ return status;
+}
+
+enum {
+ NEED_CLIP_REGION = 0x1,
+ NEED_CLIP_SURFACE = 0x2,
+ FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = NEED_CLIP_REGION;
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+ return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = 0;
+ if (! extents->is_bounded) {
+ flags |= NEED_CLIP_REGION;
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+ }
+ if (extents->clip->path != NULL)
+ flags |= NEED_CLIP_SURFACE;
+ return flags;
+}
+
+static cairo_status_t
+clip_and_composite (const cairo_mask_compositor_t *compositor,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ void *draw_closure,
+ cairo_composite_rectangles_t*extents,
+ unsigned int need_clip)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_operator_t op = extents->op;
+ cairo_pattern_t *src = &extents->source_pattern.base;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ compositor->acquire (dst);
+
+ if (need_clip & NEED_CLIP_REGION) {
+ clip_region = _cairo_clip_get_region (extents->clip);
+ if ((need_clip & FORCE_CLIP_REGION) == 0 &&
+ _cairo_composite_rectangles_can_reduce_clip (extents,
+ extents->clip))
+ clip_region = NULL;
+ if (clip_region != NULL) {
+ status = compositor->set_clip_region (dst, clip_region);
+ if (unlikely (status)) {
+ compositor->release (dst);
+ return status;
+ }
+ }
+ }
+
+ if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) {
+ op = CAIRO_OPERATOR_ADD;
+ src = NULL;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = clip_and_composite_source (compositor,
+ draw_closure, draw_func, mask_func,
+ src, extents);
+ } else {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ op = CAIRO_OPERATOR_DEST_OUT;
+ src = NULL;
+ }
+
+ if (need_clip & NEED_CLIP_SURFACE) {
+ if (extents->is_bounded) {
+ status = clip_and_composite_with_mask (compositor,
+ draw_closure,
+ draw_func,
+ mask_func,
+ op, src, extents);
+ } else {
+ status = clip_and_composite_combine (compositor,
+ draw_closure,
+ draw_func,
+ op, src, extents);
+ }
+ } else {
+ status = draw_func (compositor,
+ dst, draw_closure,
+ op, src, &extents->source_sample_area,
+ 0, 0,
+ &extents->bounded,
+ extents->clip);
+ }
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+ if (need_clip & NEED_CLIP_SURFACE)
+ status = fixup_unbounded_with_mask (compositor, dst, extents);
+ else
+ status = fixup_unbounded (compositor, dst, extents);
+ }
+
+ if (clip_region)
+ compositor->set_clip_region (dst, NULL);
+
+ compositor->release (dst);
+
+ return status;
+}
+
+static cairo_int_status_t
+trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_box_t box;
+
+ _cairo_boxes_extents (boxes, &box);
+ return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_status_t
+upload_boxes (const cairo_mask_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ const cairo_pattern_t *source = &extents->source_pattern.base;
+ cairo_surface_t *src;
+ cairo_rectangle_int_t limit;
+ cairo_int_status_t status;
+ int tx, ty;
+
+ src = _cairo_pattern_get_source ((cairo_surface_pattern_t *)source, &limit);
+ if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Check that the data is entirely within the image */
+ if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width ||
+ extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ tx += limit.x;
+ ty += limit.y;
+
+ if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
+ status = compositor->draw_image_boxes (dst,
+ (cairo_image_surface_t *)src,
+ boxes, tx, ty);
+ else
+ status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
+ tx, ty);
+
+ return status;
+}
+
+static cairo_status_t
+composite_boxes (const cairo_mask_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_operator_t op = extents->op;
+ const cairo_pattern_t *source = &extents->source_pattern.base;
+ cairo_bool_t need_clip_mask = extents->clip->path != NULL;
+ cairo_status_t status;
+
+ if (need_clip_mask &&
+ (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = compositor->acquire (dst);
+ if (unlikely (status))
+ return status;
+
+ if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_color_t *color;
+
+ color = &((cairo_solid_pattern_t *) source)->color;
+ status = compositor->fill_boxes (dst, op, color, boxes);
+ } else {
+ cairo_surface_t *src, *mask = NULL;
+ int src_x, src_y;
+ int mask_x = 0, mask_y = 0;
+
+ if (need_clip_mask) {
+ mask = get_clip_source (compositor,
+ extents->clip, dst, &extents->bounded,
+ &mask_x, &mask_y);
+ if (unlikely (mask->status)) {
+ status = mask->status;
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ source = NULL;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+ }
+
+ if (source || mask == NULL) {
+ src = compositor->pattern_to_surface (dst, source, FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ } else {
+ src = mask;
+ src_x = mask_x;
+ src_y = mask_y;
+ mask = NULL;
+ }
+
+ status = compositor->composite_boxes (dst, op, src, mask,
+ src_x, src_y,
+ mask_x, mask_y,
+ 0, 0,
+ boxes, &extents->bounded);
+
+ cairo_surface_destroy (src);
+ cairo_surface_destroy (mask);
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
+ status = fixup_unbounded_boxes (compositor, extents, boxes);
+
+ compositor->release (dst);
+
+ return status;
+}
+
+static cairo_status_t
+clip_and_composite_boxes (const cairo_mask_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_int_status_t status;
+
+ if (boxes->num_boxes == 0) {
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ return fixup_unbounded_boxes (compositor, extents, boxes);
+ }
+
+ if (! boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = trim_extents_to_boxes (extents, boxes);
+ if (unlikely (status))
+ return status;
+
+ if (extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ extents->clip->path == NULL &&
+ (extents->op == CAIRO_OPERATOR_SOURCE ||
+ (dst->is_clear && (extents->op == CAIRO_OPERATOR_OVER ||
+ extents->op == CAIRO_OPERATOR_ADD))))
+ {
+ status = upload_boxes (compositor, extents, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ return composite_boxes (compositor, extents, boxes);
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_mask_compositor_paint (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+ cairo_boxes_t boxes;
+ cairo_int_status_t status;
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ _cairo_clip_steal_boxes (extents->clip, &boxes);
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+ return status;
+}
+
+struct composite_opacity_info {
+ const cairo_mask_compositor_t *compositor;
+ uint8_t op;
+ cairo_surface_t *dst;
+ cairo_surface_t *src;
+ int src_x, src_y;
+ double opacity;
+};
+
+static void composite_opacity(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct composite_opacity_info *info = closure;
+ const cairo_mask_compositor_t *compositor = info->compositor;
+ cairo_surface_t *mask;
+ int mask_x, mask_y;
+ cairo_color_t color;
+ cairo_solid_pattern_t solid;
+
+ _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
+ _cairo_pattern_init_solid (&solid, &color);
+ mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
+ &_cairo_unbounded_rectangle,
+ &_cairo_unbounded_rectangle,
+ &mask_x, &mask_y);
+ if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+ if (info->src) {
+ compositor->composite (info->dst, info->op, info->src, mask,
+ x + info->src_x, y + info->src_y,
+ mask_x, mask_y,
+ x, y,
+ w, h);
+ } else {
+ compositor->composite (info->dst, info->op, mask, NULL,
+ mask_x, mask_y,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+ }
+
+ cairo_surface_destroy (mask);
+}
+
+static cairo_int_status_t
+composite_opacity_boxes (const cairo_mask_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_rectangle_int_t *src_sample,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ const cairo_solid_pattern_t *mask_pattern = closure;
+ struct composite_opacity_info info;
+ int i;
+
+ assert (clip);
+
+ info.compositor = compositor;
+ info.op = op;
+ info.dst = dst;
+
+ if (src_pattern != NULL) {
+ info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
+ extents, src_sample,
+ &info.src_x, &info.src_y);
+ if (unlikely (info.src->status))
+ return info.src->status;
+ } else
+ info.src = NULL;
+
+ info.opacity = mask_pattern->color.alpha / (double) 0xffff;
+
+ /* XXX for lots of boxes create a clip region for the fully opaque areas */
+ for (i = 0; i < clip->num_boxes; i++)
+ do_unaligned_box(composite_opacity, &info,
+ &clip->boxes[i], dst_x, dst_y);
+ cairo_surface_destroy (info.src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct composite_box_info {
+ const cairo_mask_compositor_t *compositor;
+ cairo_surface_t *dst;
+ cairo_surface_t *src;
+ int src_x, src_y;
+ uint8_t op;
+};
+
+static void composite_box(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct composite_box_info *info = closure;
+ const cairo_mask_compositor_t *compositor = info->compositor;
+
+ if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
+ cairo_surface_t *mask;
+ cairo_color_t color;
+ cairo_solid_pattern_t solid;
+ int mask_x, mask_y;
+
+ _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
+ _cairo_pattern_init_solid (&solid, &color);
+
+ mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
+ &_cairo_unbounded_rectangle,
+ &_cairo_unbounded_rectangle,
+ &mask_x, &mask_y);
+
+ if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+ compositor->composite (info->dst, info->op, info->src, mask,
+ x + info->src_x, y + info->src_y,
+ mask_x, mask_y,
+ x, y,
+ w, h);
+ }
+
+ cairo_surface_destroy (mask);
+ } else {
+ compositor->composite (info->dst, info->op, info->src, NULL,
+ x + info->src_x, y + info->src_y,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+}
+
+static cairo_int_status_t
+composite_mask_clip_boxes (const cairo_mask_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_rectangle_int_t *src_sample,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t *composite = closure;
+ struct composite_box_info info;
+ int i;
+
+ assert (src_pattern == NULL);
+ assert (op == CAIRO_OPERATOR_SOURCE);
+
+ info.compositor = compositor;
+ info.op = CAIRO_OPERATOR_SOURCE;
+ info.dst = dst;
+ info.src = compositor->pattern_to_surface (dst,
+ &composite->mask_pattern.base,
+ FALSE, extents,
+ &composite->mask_sample_area,
+ &info.src_x, &info.src_y);
+ if (unlikely (info.src->status))
+ return info.src->status;
+
+ info.src_x += dst_x;
+ info.src_y += dst_y;
+
+ for (i = 0; i < clip->num_boxes; i++)
+ do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
+
+ cairo_surface_destroy (info.src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_mask (const cairo_mask_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_rectangle_int_t *src_sample,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t *composite = closure;
+ cairo_surface_t *src, *mask;
+ int src_x, src_y;
+ int mask_x, mask_y;
+
+ if (src_pattern != NULL) {
+ src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
+ extents, src_sample,
+ &src_x, &src_y);
+ if (unlikely (src->status))
+ return src->status;
+
+ mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE,
+ extents, &composite->mask_sample_area,
+ &mask_x, &mask_y);
+ if (unlikely (mask->status)) {
+ cairo_surface_destroy (src);
+ return mask->status;
+ }
+
+ compositor->composite (dst, op, src, mask,
+ extents->x + src_x, extents->y + src_y,
+ extents->x + mask_x, extents->y + mask_y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ cairo_surface_destroy (mask);
+ cairo_surface_destroy (src);
+ } else {
+ src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE,
+ extents, &composite->mask_sample_area,
+ &src_x, &src_y);
+ if (unlikely (src->status))
+ return src->status;
+
+ compositor->composite (dst, op, src, NULL,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ cairo_surface_destroy (src);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_mask (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+ extents->clip->path == NULL &&
+ _cairo_clip_is_region (extents->clip)) {
+ status = clip_and_composite (compositor,
+ composite_opacity_boxes,
+ composite_opacity_boxes,
+ &extents->mask_pattern.solid,
+ extents, need_unbounded_clip (extents));
+ } else {
+ status = clip_and_composite (compositor,
+ composite_mask,
+ extents->clip->path == NULL ? composite_mask_clip_boxes : NULL,
+ extents,
+ extents, need_bounded_clip (extents));
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+ cairo_surface_t *mask;
+ cairo_surface_pattern_t pattern;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, extents->clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ mask = cairo_surface_create_similar_image (extents->surface,
+ CAIRO_FORMAT_A8,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->status)) {
+ status = mask->status;
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ status = _cairo_surface_offset_stroke (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ extents->clip);
+ if (unlikely (status)) {
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, mask);
+ cairo_surface_destroy (mask);
+
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ pattern.base.extend = CAIRO_EXTEND_NONE;
+ status = _cairo_surface_mask (extents->surface,
+ extents->op,
+ &extents->source_pattern.base,
+ &pattern.base,
+ extents->clip);
+ _cairo_pattern_fini (&pattern.base);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+ cairo_surface_t *mask;
+ cairo_surface_pattern_t pattern;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, extents->clip);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ mask = cairo_surface_create_similar_image (extents->surface,
+ CAIRO_FORMAT_A8,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->status)) {
+ status = mask->status;
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ status = _cairo_surface_offset_fill (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ path, fill_rule, tolerance, antialias,
+ extents->clip);
+ if (unlikely (status)) {
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, mask);
+ cairo_surface_destroy (mask);
+
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ pattern.base.extend = CAIRO_EXTEND_NONE;
+ status = _cairo_surface_mask (extents->surface,
+ extents->op,
+ &extents->source_pattern.base,
+ &pattern.base,
+ extents->clip);
+ _cairo_pattern_fini (&pattern.base);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_glyphs (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+ cairo_surface_t *mask;
+ cairo_surface_pattern_t pattern;
+ cairo_int_status_t status;
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ mask = cairo_surface_create_similar_image (extents->surface,
+ CAIRO_FORMAT_A8,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->status)) {
+ status = mask->status;
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ status = _cairo_surface_offset_glyphs (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ scaled_font, glyphs, num_glyphs,
+ extents->clip);
+ if (unlikely (status)) {
+ cairo_surface_destroy (mask);
+ return status;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, mask);
+ cairo_surface_destroy (mask);
+
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ pattern.base.extend = CAIRO_EXTEND_NONE;
+ status = _cairo_surface_mask (extents->surface,
+ extents->op,
+ &extents->source_pattern.base,
+ &pattern.base,
+ extents->clip);
+ _cairo_pattern_fini (&pattern.base);
+
+ return status;
+}
+
+void
+_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
+ const cairo_compositor_t *delegate)
+{
+ compositor->base.delegate = delegate;
+
+ compositor->base.paint = _cairo_mask_compositor_paint;
+ compositor->base.mask = _cairo_mask_compositor_mask;
+ compositor->base.fill = _cairo_mask_compositor_fill;
+ compositor->base.stroke = _cairo_mask_compositor_stroke;
+ compositor->base.glyphs = _cairo_mask_compositor_glyphs;
+}
diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
new file mode 100755
index 000000000..ba975beae
--- /dev/null
+++ b/src/cairo-matrix.c
@@ -0,0 +1,1203 @@
+/* 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 <float.h>
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+#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.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_init_identity (cairo_matrix_t *matrix)
+{
+ cairo_matrix_init (matrix,
+ 1, 0,
+ 0, 1,
+ 0, 0);
+}
+slim_hidden_def(cairo_matrix_init_identity);
+
+/**
+ * cairo_matrix_init:
+ * @matrix: a #cairo_matrix_t
+ * @xx: xx component of the affine transformation
+ * @yx: yx component of the affine transformation
+ * @xy: xy component of the affine transformation
+ * @yy: yy component of the affine transformation
+ * @x0: X translation component of the affine transformation
+ * @y0: Y translation component of the affine transformation
+ *
+ * Sets @matrix to be the affine transformation given by
+ * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given
+ * by:
+ * <programlisting>
+ * x_new = xx * x + xy * y + x0;
+ * y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_init (cairo_matrix_t *matrix,
+ double xx, double yx,
+
+ double xy, double yy,
+ double x0, double y0)
+{
+ matrix->xx = xx; matrix->yx = yx;
+ matrix->xy = xy; matrix->yy = yy;
+ matrix->x0 = x0; matrix->y0 = y0;
+}
+slim_hidden_def(cairo_matrix_init);
+
+/**
+ * _cairo_matrix_get_affine:
+ * @matrix: a #cairo_matrix_t
+ * @xx: location to store xx component of matrix
+ * @yx: location to store yx component of matrix
+ * @xy: location to store xy component of matrix
+ * @yy: location to store yy component of matrix
+ * @x0: location to store x0 (X-translation component) of matrix, or %NULL
+ * @y0: location to store y0 (Y-translation component) of matrix, or %NULL
+ *
+ * Gets the matrix values for the affine transformation that @matrix represents.
+ * See cairo_matrix_init().
+ *
+ *
+ * This function is a leftover from the old public API, but is still
+ * mildly useful as an internal means for getting at the matrix
+ * members in a positional way. For example, when reassigning to some
+ * external matrix type, or when renaming members to more meaningful
+ * names (such as a,b,c,d,e,f) for particular manipulations.
+ **/
+void
+_cairo_matrix_get_affine (const cairo_matrix_t *matrix,
+ double *xx, double *yx,
+ double *xy, double *yy,
+ double *x0, double *y0)
+{
+ *xx = matrix->xx;
+ *yx = matrix->yx;
+
+ *xy = matrix->xy;
+ *yy = matrix->yy;
+
+ if (x0)
+ *x0 = matrix->x0;
+ if (y0)
+ *y0 = matrix->y0;
+}
+
+/**
+ * cairo_matrix_init_translate:
+ * @matrix: a #cairo_matrix_t
+ * @tx: amount to translate in the X direction
+ * @ty: amount to translate in the Y direction
+ *
+ * Initializes @matrix to a transformation that translates by @tx and
+ * @ty in the X and Y dimensions, respectively.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_init_translate (cairo_matrix_t *matrix,
+ double tx, double ty)
+{
+ cairo_matrix_init (matrix,
+ 1, 0,
+ 0, 1,
+ tx, ty);
+}
+slim_hidden_def(cairo_matrix_init_translate);
+
+/**
+ * cairo_matrix_translate:
+ * @matrix: a #cairo_matrix_t
+ * @tx: amount to translate in the X direction
+ * @ty: amount to translate in the Y direction
+ *
+ * Applies a translation by @tx, @ty to the transformation in
+ * @matrix. The effect of the new transformation is to first translate
+ * the coordinates by @tx and @ty, then apply the original transformation
+ * to the coordinates.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty)
+{
+ cairo_matrix_t tmp;
+
+ cairo_matrix_init_translate (&tmp, tx, ty);
+
+ cairo_matrix_multiply (matrix, &tmp, matrix);
+}
+slim_hidden_def (cairo_matrix_translate);
+
+/**
+ * cairo_matrix_init_scale:
+ * @matrix: a #cairo_matrix_t
+ * @sx: scale factor in the X direction
+ * @sy: scale factor in the Y direction
+ *
+ * Initializes @matrix to a transformation that scales by @sx and @sy
+ * in the X and Y dimensions, respectively.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_init_scale (cairo_matrix_t *matrix,
+ double sx, double sy)
+{
+ cairo_matrix_init (matrix,
+ sx, 0,
+ 0, sy,
+ 0, 0);
+}
+slim_hidden_def(cairo_matrix_init_scale);
+
+/**
+ * cairo_matrix_scale:
+ * @matrix: a #cairo_matrix_t
+ * @sx: scale factor in the X direction
+ * @sy: scale factor in the Y direction
+ *
+ * Applies scaling by @sx, @sy to the transformation in @matrix. The
+ * effect of the new transformation is to first scale the coordinates
+ * by @sx and @sy, then apply the original transformation to the coordinates.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy)
+{
+ cairo_matrix_t tmp;
+
+ cairo_matrix_init_scale (&tmp, sx, sy);
+
+ cairo_matrix_multiply (matrix, &tmp, matrix);
+}
+slim_hidden_def(cairo_matrix_scale);
+
+/**
+ * cairo_matrix_init_rotate:
+ * @matrix: a #cairo_matrix_t
+ * @radians: angle of rotation, in radians. The direction of rotation
+ * is defined such that positive angles rotate in the direction from
+ * the positive X axis toward the positive Y axis. With the default
+ * axis orientation of cairo, positive angles rotate in a clockwise
+ * direction.
+ *
+ * Initialized @matrix to a transformation that rotates by @radians.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_init_rotate (cairo_matrix_t *matrix,
+ double radians)
+{
+ double s;
+ double c;
+
+ s = sin (radians);
+ c = cos (radians);
+
+ cairo_matrix_init (matrix,
+ c, s,
+ -s, c,
+ 0, 0);
+}
+slim_hidden_def(cairo_matrix_init_rotate);
+
+/**
+ * cairo_matrix_rotate:
+ * @matrix: a #cairo_matrix_t
+ * @radians: angle of rotation, in radians. The direction of rotation
+ * is defined such that positive angles rotate in the direction from
+ * the positive X axis toward the positive Y axis. With the default
+ * axis orientation of cairo, positive angles rotate in a clockwise
+ * direction.
+ *
+ * Applies rotation by @radians to the transformation in
+ * @matrix. The effect of the new transformation is to first rotate the
+ * coordinates by @radians, then apply the original transformation
+ * to the coordinates.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_rotate (cairo_matrix_t *matrix, double radians)
+{
+ cairo_matrix_t tmp;
+
+ cairo_matrix_init_rotate (&tmp, radians);
+
+ cairo_matrix_multiply (matrix, &tmp, matrix);
+}
+
+/**
+ * cairo_matrix_multiply:
+ * @result: a #cairo_matrix_t in which to store the result
+ * @a: a #cairo_matrix_t
+ * @b: a #cairo_matrix_t
+ *
+ * Multiplies the affine transformations in @a and @b together
+ * and stores the result in @result. The effect of the resulting
+ * transformation is to first apply the transformation in @a to the
+ * coordinates and then apply the transformation in @b to the
+ * coordinates.
+ *
+ * It is allowable for @result to be identical to either @a or @b.
+ *
+ * Since: 1.0
+ **/
+/*
+ * XXX: The ordering of the arguments to this function corresponds
+ * to [row_vector]*A*B. If we want to use column vectors instead,
+ * then we need to switch the two arguments and fix up all
+ * uses.
+ */
+void
+cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b)
+{
+ cairo_matrix_t r;
+
+ r.xx = a->xx * b->xx + a->yx * b->xy;
+ r.yx = a->xx * b->yx + a->yx * b->yy;
+
+ r.xy = a->xy * b->xx + a->yy * b->xy;
+ r.yy = a->xy * b->yx + a->yy * b->yy;
+
+ r.x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0;
+ r.y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0;
+
+ *result = r;
+}
+slim_hidden_def(cairo_matrix_multiply);
+
+void
+_cairo_matrix_multiply (cairo_matrix_t *r,
+ const cairo_matrix_t *a,
+ const cairo_matrix_t *b)
+{
+ r->xx = a->xx * b->xx + a->yx * b->xy;
+ r->yx = a->xx * b->yx + a->yx * b->yy;
+
+ r->xy = a->xy * b->xx + a->yy * b->xy;
+ r->yy = a->xy * b->yx + a->yy * b->yy;
+
+ r->x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0;
+ r->y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0;
+}
+
+/**
+ * cairo_matrix_transform_distance:
+ * @matrix: a #cairo_matrix_t
+ * @dx: X component of a distance vector. An in/out parameter
+ * @dy: Y component of a distance vector. An in/out parameter
+ *
+ * Transforms the distance vector (@dx,@dy) by @matrix. This is
+ * similar to cairo_matrix_transform_point() except that the translation
+ * components of the transformation are ignored. The calculation of
+ * the returned vector is as follows:
+ *
+ * <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.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy)
+{
+ double new_x, new_y;
+
+ new_x = (matrix->xx * *dx + matrix->xy * *dy);
+ new_y = (matrix->yx * *dx + matrix->yy * *dy);
+
+ *dx = new_x;
+ *dy = new_y;
+}
+slim_hidden_def(cairo_matrix_transform_distance);
+
+/**
+ * cairo_matrix_transform_point:
+ * @matrix: a #cairo_matrix_t
+ * @x: X position. An in/out parameter
+ * @y: Y position. An in/out parameter
+ *
+ * Transforms the point (@x, @y) by @matrix.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y)
+{
+ cairo_matrix_transform_distance (matrix, x, y);
+
+ *x += matrix->x0;
+ *y += matrix->y0;
+}
+slim_hidden_def(cairo_matrix_transform_point);
+
+void
+_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
+ double *x1, double *y1,
+ double *x2, double *y2,
+ cairo_bool_t *is_tight)
+{
+ int i;
+ double quad_x[4], quad_y[4];
+ double min_x, max_x;
+ double min_y, max_y;
+
+ if (matrix->xy == 0. && matrix->yx == 0.) {
+ /* non-rotation/skew matrix, just map the two extreme points */
+
+ if (matrix->xx != 1.) {
+ quad_x[0] = *x1 * matrix->xx;
+ quad_x[1] = *x2 * matrix->xx;
+ if (quad_x[0] < quad_x[1]) {
+ *x1 = quad_x[0];
+ *x2 = quad_x[1];
+ } else {
+ *x1 = quad_x[1];
+ *x2 = quad_x[0];
+ }
+ }
+ if (matrix->x0 != 0.) {
+ *x1 += matrix->x0;
+ *x2 += matrix->x0;
+ }
+
+ if (matrix->yy != 1.) {
+ quad_y[0] = *y1 * matrix->yy;
+ quad_y[1] = *y2 * matrix->yy;
+ if (quad_y[0] < quad_y[1]) {
+ *y1 = quad_y[0];
+ *y2 = quad_y[1];
+ } else {
+ *y1 = quad_y[1];
+ *y2 = quad_y[0];
+ }
+ }
+ if (matrix->y0 != 0.) {
+ *y1 += matrix->y0;
+ *y2 += matrix->y0;
+ }
+
+ if (is_tight)
+ *is_tight = TRUE;
+
+ return;
+ }
+
+ /* general matrix */
+ quad_x[0] = *x1;
+ quad_y[0] = *y1;
+ cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
+
+ quad_x[1] = *x2;
+ quad_y[1] = *y1;
+ cairo_matrix_transform_point (matrix, &quad_x[1], &quad_y[1]);
+
+ quad_x[2] = *x1;
+ quad_y[2] = *y2;
+ cairo_matrix_transform_point (matrix, &quad_x[2], &quad_y[2]);
+
+ quad_x[3] = *x2;
+ quad_y[3] = *y2;
+ cairo_matrix_transform_point (matrix, &quad_x[3], &quad_y[3]);
+
+ min_x = max_x = quad_x[0];
+ min_y = max_y = quad_y[0];
+
+ for (i=1; i < 4; i++) {
+ if (quad_x[i] < min_x)
+ min_x = quad_x[i];
+ if (quad_x[i] > max_x)
+ max_x = quad_x[i];
+
+ if (quad_y[i] < min_y)
+ min_y = quad_y[i];
+ if (quad_y[i] > max_y)
+ max_y = quad_y[i];
+ }
+
+ *x1 = min_x;
+ *y1 = min_y;
+ *x2 = max_x;
+ *y2 = max_y;
+
+ if (is_tight) {
+ /* it's tight if and only if the four corner points form an axis-aligned
+ rectangle.
+ And that's true if and only if we can derive corners 0 and 3 from
+ corners 1 and 2 in one of two straightforward ways...
+ We could use a tolerance here but for now we'll fall back to FALSE in the case
+ of floating point error.
+ */
+ *is_tight =
+ (quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] &&
+ quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) ||
+ (quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] &&
+ quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]);
+ }
+}
+
+cairo_private void
+_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix,
+ cairo_box_t *bbox,
+ cairo_bool_t *is_tight)
+{
+ double x1, y1, x2, y2;
+
+ _cairo_box_to_doubles (bbox, &x1, &y1, &x2, &y2);
+ _cairo_matrix_transform_bounding_box (matrix, &x1, &y1, &x2, &y2, is_tight);
+ _cairo_box_from_doubles (bbox, &x1, &y1, &x2, &y2);
+}
+
+static void
+_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar)
+{
+ matrix->xx *= scalar;
+ matrix->yx *= scalar;
+
+ matrix->xy *= scalar;
+ matrix->yy *= scalar;
+
+ matrix->x0 *= scalar;
+ matrix->y0 *= scalar;
+}
+
+/* This function isn't a correct adjoint in that the implicit 1 in the
+ homogeneous result should actually be ad-bc instead. But, since this
+ adjoint is only used in the computation of the inverse, which
+ divides by det (A)=ad-bc anyway, everything works out in the end. */
+static void
+_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix)
+{
+ /* adj (A) = transpose (C:cofactor (A,i,j)) */
+ double a, b, c, d, tx, ty;
+
+ _cairo_matrix_get_affine (matrix,
+ &a, &b,
+ &c, &d,
+ &tx, &ty);
+
+ cairo_matrix_init (matrix,
+ d, -b,
+ -c, a,
+ c*ty - d*tx, b*tx - a*ty);
+}
+
+/**
+ * cairo_matrix_invert:
+ * @matrix: a #cairo_matrix_t
+ *
+ * Changes @matrix to be the inverse of its original value. Not
+ * all transformation matrices have inverses; if the matrix
+ * collapses points together (it is <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.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_matrix_invert (cairo_matrix_t *matrix)
+{
+ double det;
+
+ /* Simple scaling|translation matrices are quite common... */
+ if (matrix->xy == 0. && matrix->yx == 0.) {
+ matrix->x0 = -matrix->x0;
+ matrix->y0 = -matrix->y0;
+
+ if (matrix->xx != 1.) {
+ if (matrix->xx == 0.)
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ matrix->xx = 1. / matrix->xx;
+ matrix->x0 *= matrix->xx;
+ }
+
+ if (matrix->yy != 1.) {
+ if (matrix->yy == 0.)
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ matrix->yy = 1. / matrix->yy;
+ matrix->y0 *= matrix->yy;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* inv (A) = 1/det (A) * adj (A) */
+ det = _cairo_matrix_compute_determinant (matrix);
+
+ if (! ISFINITE (det))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ if (det == 0)
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ _cairo_matrix_compute_adjoint (matrix);
+ _cairo_matrix_scalar_multiply (matrix, 1 / det);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def(cairo_matrix_invert);
+
+cairo_bool_t
+_cairo_matrix_is_invertible (const cairo_matrix_t *matrix)
+{
+ double det;
+
+ det = _cairo_matrix_compute_determinant (matrix);
+
+ return ISFINITE (det) && det != 0.;
+}
+
+cairo_bool_t
+_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix)
+{
+ return matrix->xx == 0. &&
+ matrix->xy == 0. &&
+ matrix->yx == 0. &&
+ matrix->yy == 0.;
+}
+
+double
+_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix)
+{
+ double a, b, c, d;
+
+ a = matrix->xx; b = matrix->yx;
+ c = matrix->xy; d = matrix->yy;
+
+ return a*d - b*c;
+}
+
+/**
+ * _cairo_matrix_compute_basis_scale_factors:
+ * @matrix: a matrix
+ * @basis_scale: the scale factor in the direction of basis
+ * @normal_scale: the scale factor in the direction normal to the basis
+ * @x_basis: basis to use. X basis if true, Y basis otherwise.
+ *
+ * Computes |Mv| and det(M)/|Mv| for v=[1,0] if x_basis is true, and v=[0,1]
+ * otherwise, and M is @matrix.
+ *
+ * Return value: the scale factor of @matrix on the height of the font,
+ * or 1.0 if @matrix is %NULL.
+ **/
+cairo_status_t
+_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
+ double *basis_scale, double *normal_scale,
+ cairo_bool_t x_basis)
+{
+ double det;
+
+ det = _cairo_matrix_compute_determinant (matrix);
+
+ if (! ISFINITE (det))
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+ if (det == 0)
+ {
+ *basis_scale = *normal_scale = 0;
+ }
+ else
+ {
+ double x = x_basis != 0;
+ double y = x == 0;
+ double major, minor;
+
+ cairo_matrix_transform_distance (matrix, &x, &y);
+ major = hypot (x, y);
+ /*
+ * ignore mirroring
+ */
+ if (det < 0)
+ det = -det;
+ if (major)
+ minor = det / major;
+ else
+ minor = 0.0;
+ if (x_basis)
+ {
+ *basis_scale = major;
+ *normal_scale = minor;
+ }
+ else
+ {
+ *basis_scale = minor;
+ *normal_scale = major;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix,
+ int *itx, int *ity)
+{
+ if (_cairo_matrix_is_translation (matrix))
+ {
+ cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0);
+ cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0);
+
+ if (_cairo_fixed_is_integer (x0_fixed) &&
+ _cairo_fixed_is_integer (y0_fixed))
+ {
+ if (itx)
+ *itx = _cairo_fixed_integer_part (x0_fixed);
+ if (ity)
+ *ity = _cairo_fixed_integer_part (y0_fixed);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+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 axis of a circle of the given radius
+ after applying the transformation matrix. */
+double
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ double radius)
+{
+ double a, b, c, d, f, g, h, i, j;
+
+ if (_cairo_matrix_has_unity_scale (matrix))
+ return radius;
+
+ _cairo_matrix_get_affine (matrix,
+ &a, &b,
+ &c, &d,
+ NULL, NULL);
+
+ i = a*a + b*b;
+ j = c*c + d*d;
+
+ f = 0.5 * (i + j);
+ g = 0.5 * (i - j);
+ h = a*c + b*d;
+
+ return radius * sqrt (f + hypot (g, h));
+
+ /*
+ * we don't need the minor axis length, which is
+ * double min = radius * sqrt (f - sqrt (g*g+h*h));
+ */
+}
+
+static const pixman_transform_t pixman_identity_transform = {{
+ {1 << 16, 0, 0},
+ { 0, 1 << 16, 0},
+ { 0, 0, 1 << 16}
+ }};
+
+static cairo_status_t
+_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
+ pixman_transform_t *pixman_transform,
+ double xc,
+ double yc)
+{
+ cairo_matrix_t inv;
+ unsigned max_iterations;
+
+ pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx);
+ pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy);
+ pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0);
+
+ pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx);
+ pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy);
+ pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0);
+
+ pixman_transform->matrix[2][0] = 0;
+ pixman_transform->matrix[2][1] = 0;
+ pixman_transform->matrix[2][2] = 1 << 16;
+
+ /* The conversion above breaks cairo's translation invariance:
+ * a translation of (a, b) in device space translates to
+ * a translation of (xx * a + xy * b, yx * a + yy * b)
+ * for cairo, while pixman uses rounded versions of xx ... yy.
+ * This error increases as a and b get larger.
+ *
+ * To compensate for this, we fix the point (xc, yc) in pattern
+ * space and adjust pixman's transform to agree with cairo's at
+ * that point.
+ */
+
+ if (_cairo_matrix_has_unity_scale (matrix))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (unlikely (fabs (matrix->xx) > PIXMAN_MAX_INT ||
+ fabs (matrix->xy) > PIXMAN_MAX_INT ||
+ fabs (matrix->x0) > PIXMAN_MAX_INT ||
+ fabs (matrix->yx) > PIXMAN_MAX_INT ||
+ fabs (matrix->yy) > PIXMAN_MAX_INT ||
+ fabs (matrix->y0) > PIXMAN_MAX_INT))
+ {
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+ }
+
+ /* Note: If we can't invert the transformation, skip the adjustment. */
+ inv = *matrix;
+ if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* find the pattern space coordinate that maps to (xc, yc) */
+ max_iterations = 5;
+ do {
+ double x,y;
+ pixman_vector_t vector;
+ cairo_fixed_16_16_t dx, dy;
+
+ vector.vector[0] = _cairo_fixed_16_16_from_double (xc);
+ vector.vector[1] = _cairo_fixed_16_16_from_double (yc);
+ vector.vector[2] = 1 << 16;
+
+ /* If we can't transform the reference point, skip the adjustment. */
+ if (! pixman_transform_point_3d (pixman_transform, &vector))
+ return CAIRO_STATUS_SUCCESS;
+
+ x = pixman_fixed_to_double (vector.vector[0]);
+ y = pixman_fixed_to_double (vector.vector[1]);
+ cairo_matrix_transform_point (&inv, &x, &y);
+
+ /* Ideally, the vector should now be (xc, yc).
+ * We can now compensate for the resulting error.
+ */
+ x -= xc;
+ y -= yc;
+ cairo_matrix_transform_distance (matrix, &x, &y);
+ dx = _cairo_fixed_16_16_from_double (x);
+ dy = _cairo_fixed_16_16_from_double (y);
+ pixman_transform->matrix[0][2] -= dx;
+ pixman_transform->matrix[1][2] -= dy;
+
+ if (dx == 0 && dy == 0)
+ return CAIRO_STATUS_SUCCESS;
+ } while (--max_iterations);
+
+ /* We didn't find an exact match between cairo and pixman, but
+ * the matrix should be mostly correct */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline double
+_pixman_nearest_sample (double d)
+{
+ return ceil (d - .5);
+}
+
+/**
+ * _cairo_matrix_is_pixman_translation:
+ * @matrix: a matrix
+ * @filter: the filter to be used on the pattern transformed by @matrix
+ * @x_offset: the translation in the X direction
+ * @y_offset: the translation in the Y direction
+ *
+ * Checks if @matrix translated by (x_offset, y_offset) can be
+ * represented using just an offset (within the range pixman can
+ * accept) and an identity matrix.
+ *
+ * Passing a non-zero value in x_offset/y_offset has the same effect
+ * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and
+ * setting x_offset and y_offset to 0.
+ *
+ * Upon return x_offset and y_offset contain the translation vector if
+ * the return value is %TRUE. If the return value is %FALSE, they will
+ * not be modified.
+ *
+ * Return value: %TRUE if @matrix can be represented as a pixman
+ * translation, %FALSE otherwise.
+ **/
+cairo_bool_t
+_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix,
+ cairo_filter_t filter,
+ int *x_offset,
+ int *y_offset)
+{
+ double tx, ty;
+
+ if (!_cairo_matrix_is_translation (matrix))
+ return FALSE;
+
+ if (matrix->x0 == 0. && matrix->y0 == 0.)
+ return TRUE;
+
+ tx = matrix->x0 + *x_offset;
+ ty = matrix->y0 + *y_offset;
+
+ if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) {
+ tx = _pixman_nearest_sample (tx);
+ ty = _pixman_nearest_sample (ty);
+ } else if (tx != floor (tx) || ty != floor (ty)) {
+ return FALSE;
+ }
+
+ if (fabs (tx) > PIXMAN_MAX_INT || fabs (ty) > PIXMAN_MAX_INT)
+ return FALSE;
+
+ *x_offset = _cairo_lround (tx);
+ *y_offset = _cairo_lround (ty);
+ return TRUE;
+}
+
+/**
+ * _cairo_matrix_to_pixman_matrix_offset:
+ * @matrix: a matrix
+ * @filter: the filter to be used on the pattern transformed by @matrix
+ * @xc: the X coordinate of the point to fix in pattern space
+ * @yc: the Y coordinate of the point to fix in pattern space
+ * @out_transform: the transformation which best approximates @matrix
+ * @x_offset: the translation in the X direction
+ * @y_offset: the translation in the Y direction
+ *
+ * This function tries to represent @matrix translated by (x_offset,
+ * y_offset) as a %pixman_transform_t and an translation.
+ *
+ * Passing a non-zero value in x_offset/y_offset has the same effect
+ * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and
+ * setting x_offset and y_offset to 0.
+ *
+ * If it is possible to represent the matrix with an identity
+ * %pixman_transform_t and a translation within the valid range for
+ * pixman, this function will set @out_transform to be the identity,
+ * @x_offset and @y_offset to be the translation vector and will
+ * return %CAIRO_INT_STATUS_NOTHING_TO_DO. Otherwise it will try to
+ * evenly divide the translational component of @matrix between
+ * @out_transform and (@x_offset, @y_offset).
+ *
+ * Upon return x_offset and y_offset contain the translation vector.
+ *
+ * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the out_transform
+ * is the identity, %CAIRO_STATUS_INVALID_MATRIX if it was not
+ * possible to represent @matrix as a pixman_transform_t without
+ * overflows, %CAIRO_STATUS_SUCCESS otherwise.
+ **/
+cairo_status_t
+_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix,
+ cairo_filter_t filter,
+ double xc,
+ double yc,
+ pixman_transform_t *out_transform,
+ int *x_offset,
+ int *y_offset)
+{
+ cairo_bool_t is_pixman_translation;
+
+ is_pixman_translation = _cairo_matrix_is_pixman_translation (matrix,
+ filter,
+ x_offset,
+ y_offset);
+
+ if (is_pixman_translation) {
+ *out_transform = pixman_identity_transform;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ } else {
+ cairo_matrix_t m;
+
+ m = *matrix;
+ cairo_matrix_translate (&m, *x_offset, *y_offset);
+ if (m.x0 != 0.0 || m.y0 != 0.0) {
+ double tx, ty, norm;
+ int i, j;
+
+ /* pixman also limits the [xy]_offset to 16 bits so evenly
+ * spread the bits between the two.
+ *
+ * To do this, find the solutions of:
+ * |x| = |x*m.xx + y*m.xy + m.x0|
+ * |y| = |x*m.yx + y*m.yy + m.y0|
+ *
+ * and select the one whose maximum norm is smallest.
+ */
+ tx = m.x0;
+ ty = m.y0;
+ norm = MAX (fabs (tx), fabs (ty));
+
+ for (i = -1; i < 2; i+=2) {
+ for (j = -1; j < 2; j+=2) {
+ double x, y, den, new_norm;
+
+ den = (m.xx + i) * (m.yy + j) - m.xy * m.yx;
+ if (fabs (den) < DBL_EPSILON)
+ continue;
+
+ x = m.y0 * m.xy - m.x0 * (m.yy + j);
+ y = m.x0 * m.yx - m.y0 * (m.xx + i);
+
+ den = 1 / den;
+ x *= den;
+ y *= den;
+
+ new_norm = MAX (fabs (x), fabs (y));
+ if (norm > new_norm) {
+ norm = new_norm;
+ tx = x;
+ ty = y;
+ }
+ }
+ }
+
+ tx = floor (tx);
+ ty = floor (ty);
+ *x_offset = -tx;
+ *y_offset = -ty;
+ cairo_matrix_translate (&m, tx, ty);
+ } else {
+ *x_offset = 0;
+ *y_offset = 0;
+ }
+
+ return _cairo_matrix_to_pixman_matrix (&m, out_transform, xc, yc);
+ }
+}
diff --git a/src/cairo-mempool-private.h b/src/cairo-mempool-private.h
new file mode 100755
index 000000000..a09f6ce51
--- /dev/null
+++ b/src/cairo-mempool-private.h
@@ -0,0 +1,85 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributors(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_MEMPOOL_PRIVATE_H
+#define CAIRO_MEMPOOL_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+
+#include <stddef.h> /* for size_t */
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_mempool cairo_mempool_t;
+
+struct _cairo_mempool {
+ char *base;
+ struct _cairo_memblock {
+ int bits;
+ cairo_list_t link;
+ } *blocks;
+ cairo_list_t free[32];
+ unsigned char *map;
+
+ unsigned int num_blocks;
+ int min_bits; /* Minimum block size is 1 << min_bits */
+ int num_sizes;
+ int max_free_bits;
+
+ size_t free_bytes;
+ size_t max_bytes;
+};
+
+cairo_private cairo_status_t
+_cairo_mempool_init (cairo_mempool_t *pool,
+ void *base,
+ size_t bytes,
+ int min_bits,
+ int num_sizes);
+
+cairo_private void *
+_cairo_mempool_alloc (cairo_mempool_t *pi, size_t bytes);
+
+cairo_private void
+_cairo_mempool_free (cairo_mempool_t *pi, void *storage);
+
+cairo_private void
+_cairo_mempool_fini (cairo_mempool_t *pool);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_MEMPOOL_PRIVATE_H */
diff --git a/src/cairo-mempool.c b/src/cairo-mempool.c
new file mode 100755
index 000000000..96e4a62b2
--- /dev/null
+++ b/src/cairo-mempool.c
@@ -0,0 +1,368 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipoolent may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributors(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-mempool-private.h"
+#include "cairo-list-inline.h"
+
+/* a simple buddy allocator for memory pools
+ * XXX fragmentation? use Doug Lea's malloc?
+ */
+
+#define BITTEST(p, n) ((p)->map[(n) >> 3] & (128 >> ((n) & 7)))
+#define BITSET(p, n) ((p)->map[(n) >> 3] |= (128 >> ((n) & 7)))
+#define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7)))
+
+static void
+clear_bits (cairo_mempool_t *pool, size_t first, size_t last)
+{
+ size_t i, n = last;
+ size_t first_full = (first + 7) & ~7;
+ size_t past_full = last & ~7;
+ size_t bytes;
+
+ if (n > first_full)
+ n = first_full;
+ for (i = first; i < n; i++)
+ BITCLEAR (pool, i);
+
+ if (past_full > first_full) {
+ bytes = past_full - first_full;
+ bytes = bytes >> 3;
+ memset (pool->map + (first_full >> 3), 0, bytes);
+ }
+
+ if (past_full < n)
+ past_full = n;
+ for (i = past_full; i < last; i++)
+ BITCLEAR (pool, i);
+}
+
+static void
+free_bits (cairo_mempool_t *pool, size_t start, int bits, cairo_bool_t clear)
+{
+ struct _cairo_memblock *block;
+
+ if (clear)
+ clear_bits (pool, start, start + (1 << bits));
+
+ block = pool->blocks + start;
+ block->bits = bits;
+
+ cairo_list_add (&block->link, &pool->free[bits]);
+
+ pool->free_bytes += 1 << (bits + pool->min_bits);
+ if (bits > pool->max_free_bits)
+ pool->max_free_bits = bits;
+}
+
+/* Add a chunk to the free list */
+static void
+free_blocks (cairo_mempool_t *pool,
+ size_t first,
+ size_t last,
+ cairo_bool_t clear)
+{
+ size_t i, len;
+ int bits = 0;
+
+ for (i = first, len = 1; i < last; i += len) {
+ /* To avoid cost quadratic in the number of different
+ * blocks produced from this chunk of store, we have to
+ * use the size of the previous block produced from this
+ * chunk as the starting point to work out the size of the
+ * next block we can produce. If you look at the binary
+ * representation of the starting points of the blocks
+ * produced, you can see that you first of all increase the
+ * size of the blocks produced up to some maximum as the
+ * address dealt with gets offsets added on which zap out
+ * low order bits, then decrease as the low order bits of the
+ * final block produced get added in. E.g. as you go from
+ * 001 to 0111 you generate blocks
+ * of size 001 at 001 taking you to 010
+ * of size 010 at 010 taking you to 100
+ * of size 010 at 100 taking you to 110
+ * of size 001 at 110 taking you to 111
+ * So the maximum total cost of the loops below this comment
+ * is one trip from the lowest blocksize to the highest and
+ * back again.
+ */
+ while (bits < pool->num_sizes - 1) {
+ size_t next_bits = bits + 1;
+ size_t next_len = len << 1;
+
+ if (i + next_bits > last) {
+ /* off end of chunk to be freed */
+ break;
+ }
+
+ if (i & (next_len - 1)) /* block would not be on boundary */
+ break;
+
+ bits = next_bits;
+ len = next_len;
+ }
+
+ do {
+ if (i + len <= last && /* off end of chunk to be freed */
+ (i & (len - 1)) == 0) /* block would not be on boundary */
+ break;
+
+ bits--; len >>=1;
+ } while (len);
+
+ if (len == 0)
+ break;
+
+ free_bits (pool, i, bits, clear);
+ }
+}
+
+static struct _cairo_memblock *
+get_buddy (cairo_mempool_t *pool, size_t offset, int bits)
+{
+ struct _cairo_memblock *block;
+
+ assert (offset + (1 << bits) <= pool->num_blocks);
+
+ if (BITTEST (pool, offset + (1 << bits) - 1))
+ return NULL; /* buddy is allocated */
+
+ block = pool->blocks + offset;
+ if (block->bits != bits)
+ return NULL; /* buddy is partially allocated */
+
+ return block;
+}
+
+static void
+merge_buddies (cairo_mempool_t *pool,
+ struct _cairo_memblock *block,
+ int max_bits)
+{
+ size_t block_offset = block - pool->blocks;
+ int bits = block->bits;
+
+ while (bits < max_bits - 1) {
+ /* while you can, merge two blocks and get a legal block size */
+ size_t buddy_offset = block_offset ^ (1 << bits);
+
+ block = get_buddy (pool, buddy_offset, bits);
+ if (block == NULL)
+ break;
+
+ cairo_list_del (&block->link);
+
+ /* Merged block starts at buddy */
+ if (buddy_offset < block_offset)
+ block_offset = buddy_offset;
+
+ bits++;
+ }
+
+ block = pool->blocks + block_offset;
+ block->bits = bits;
+ cairo_list_add (&block->link, &pool->free[bits]);
+
+ if (bits > pool->max_free_bits)
+ pool->max_free_bits = bits;
+}
+
+/* attempt to merge all available buddies up to a particular size */
+static int
+merge_bits (cairo_mempool_t *pool, int max_bits)
+{
+ struct _cairo_memblock *block, *buddy, *next;
+ int bits;
+
+ for (bits = 0; bits < max_bits - 1; bits++) {
+ cairo_list_foreach_entry_safe (block, next,
+ struct _cairo_memblock,
+ &pool->free[bits],
+ link)
+ {
+ size_t buddy_offset = (block - pool->blocks) ^ (1 << bits);
+
+ buddy = get_buddy (pool, buddy_offset, bits);
+ if (buddy == NULL)
+ continue;
+
+ if (buddy == next) {
+ next = cairo_container_of (buddy->link.next,
+ struct _cairo_memblock,
+ link);
+ }
+
+ cairo_list_del (&block->link);
+ merge_buddies (pool, block, max_bits);
+ }
+ }
+
+ return pool->max_free_bits;
+}
+
+/* find store for 1 << bits blocks */
+static void *
+buddy_malloc (cairo_mempool_t *pool, int bits)
+{
+ size_t past, offset;
+ struct _cairo_memblock *block;
+ int b;
+
+ if (bits > pool->max_free_bits && bits > merge_bits (pool, bits))
+ return NULL;
+
+ /* Find a list with blocks big enough on it */
+ block = NULL;
+ for (b = bits; b <= pool->max_free_bits; b++) {
+ if (! cairo_list_is_empty (&pool->free[b])) {
+ block = cairo_list_first_entry (&pool->free[b],
+ struct _cairo_memblock,
+ link);
+ break;
+ }
+ }
+ assert (block != NULL);
+
+ cairo_list_del (&block->link);
+
+ while (cairo_list_is_empty (&pool->free[pool->max_free_bits])) {
+ if (--pool->max_free_bits == -1)
+ break;
+ }
+
+ /* Mark end of allocated area */
+ offset = block - pool->blocks;
+ past = offset + (1 << bits);
+ BITSET (pool, past - 1);
+ block->bits = bits;
+
+ /* If we used a larger free block than we needed, free the rest */
+ pool->free_bytes -= 1 << (b + pool->min_bits);
+ free_blocks (pool, past, offset + (1 << b), 0);
+
+ return pool->base + ((block - pool->blocks) << pool->min_bits);
+}
+
+cairo_status_t
+_cairo_mempool_init (cairo_mempool_t *pool,
+ void *base, size_t bytes,
+ int min_bits, int num_sizes)
+{
+ unsigned long tmp;
+ int num_blocks;
+ int i;
+
+ /* Align the start to an integral chunk */
+ tmp = ((unsigned long) base) & ((1 << min_bits) - 1);
+ if (tmp) {
+ tmp = (1 << min_bits) - tmp;
+ base = (char *)base + tmp;
+ bytes -= tmp;
+ }
+
+ assert ((((unsigned long) base) & ((1 << min_bits) - 1)) == 0);
+ assert (num_sizes < ARRAY_LENGTH (pool->free));
+
+ pool->base = base;
+ pool->free_bytes = 0;
+ pool->max_bytes = bytes;
+ pool->max_free_bits = -1;
+
+ num_blocks = bytes >> min_bits;
+ pool->blocks = calloc (num_blocks, sizeof (struct _cairo_memblock));
+ if (pool->blocks == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ pool->num_blocks = num_blocks;
+ pool->min_bits = min_bits;
+ pool->num_sizes = num_sizes;
+
+ for (i = 0; i < ARRAY_LENGTH (pool->free); i++)
+ cairo_list_init (&pool->free[i]);
+
+ pool->map = malloc ((num_blocks + 7) >> 3);
+ if (pool->map == NULL) {
+ free (pool->blocks);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ memset (pool->map, -1, (num_blocks + 7) >> 3);
+ clear_bits (pool, 0, num_blocks);
+
+ /* Now add all blocks to the free list */
+ free_blocks (pool, 0, num_blocks, 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void *
+_cairo_mempool_alloc (cairo_mempool_t *pool, size_t bytes)
+{
+ size_t size;
+ int bits;
+
+ size = 1 << pool->min_bits;
+ for (bits = 0; size < bytes; bits++)
+ size <<= 1;
+ if (bits >= pool->num_sizes)
+ return NULL;
+
+ return buddy_malloc (pool, bits);
+}
+
+void
+_cairo_mempool_free (cairo_mempool_t *pool, void *storage)
+{
+ size_t block_offset;
+ struct _cairo_memblock *block;
+
+ block_offset = ((char *)storage - pool->base) >> pool->min_bits;
+ block = pool->blocks + block_offset;
+
+ BITCLEAR (pool, block_offset + ((1 << block->bits) - 1));
+ pool->free_bytes += 1 << (block->bits + pool->min_bits);
+
+ merge_buddies (pool, block, pool->num_sizes);
+}
+
+void
+_cairo_mempool_fini (cairo_mempool_t *pool)
+{
+ free (pool->map);
+ free (pool->blocks);
+}
diff --git a/src/cairo-mesh-pattern-rasterizer.c b/src/cairo-mesh-pattern-rasterizer.c
new file mode 100755
index 000000000..5342b3d22
--- /dev/null
+++ b/src/cairo-mesh-pattern-rasterizer.c
@@ -0,0 +1,944 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright 2009 Andrea Canciani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Andrea Canciani.
+ *
+ * Contributor(s):
+ * Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-pattern-private.h"
+
+/*
+ * Rasterizer for mesh patterns.
+ *
+ * This implementation is based on techniques derived from several
+ * papers (available from ACM):
+ *
+ * - Lien, Shantz and Pratt "Adaptive Forward Differencing for
+ * Rendering Curves and Surfaces" (discussion of the AFD technique,
+ * bound of 1/sqrt(2) on step length without proof)
+ *
+ * - Popescu and Rosen, "Forward rasterization" (description of
+ * forward rasterization, proof of the previous bound)
+ *
+ * - Klassen, "Integer Forward Differencing of Cubic Polynomials:
+ * Analysis and Algorithms"
+ *
+ * - Klassen, "Exact Integer Hybrid Subdivision and Forward
+ * Differencing of Cubics" (improving the bound on the minimum
+ * number of steps)
+ *
+ * - Chang, Shantz and Rocchetti, "Rendering Cubic Curves and Surfaces
+ * with Integer Adaptive Forward Differencing" (analysis of forward
+ * differencing applied to Bezier patches)
+ *
+ * Notes:
+ * - Poor performance expected in degenerate cases
+ *
+ * - Patches mostly outside the drawing area are drawn completely (and
+ * clipped), wasting time
+ *
+ * - Both previous problems are greatly reduced by splitting until a
+ * reasonably small size and clipping the new tiles: execution time
+ * is quadratic in the convex-hull diameter instead than linear to
+ * the painted area. Splitting the tiles doesn't change the painted
+ * area but (usually) reduces the bounding box area (bbox area can
+ * remain the same after splitting, but cannot grow)
+ *
+ * - The initial implementation used adaptive forward differencing,
+ * but simple forward differencing scored better in benchmarks
+ *
+ * Idea:
+ *
+ * We do a sampling over the cubic patch with step du and dv (in the
+ * two parameters) that guarantees that any point of our sampling will
+ * be at most at 1/sqrt(2) from its adjacent points. In formulae
+ * (assuming B is the patch):
+ *
+ * |B(u,v) - B(u+du,v)| < 1/sqrt(2)
+ * |B(u,v) - B(u,v+dv)| < 1/sqrt(2)
+ *
+ * This means that every pixel covered by the patch will contain at
+ * least one of the samples, thus forward rasterization can be
+ * performed. Sketch of proof (from Popescu and Rosen):
+ *
+ * Let's take the P pixel we're interested into. If we assume it to be
+ * square, its boundaries define 9 regions on the plane:
+ *
+ * 1|2|3
+ * -+-+-
+ * 8|P|4
+ * -+-+-
+ * 7|6|5
+ *
+ * Let's check that the pixel P will contain at least one point
+ * assuming that it is covered by the patch.
+ *
+ * Since the pixel is covered by the patch, its center will belong to
+ * (at least) one of the quads:
+ *
+ * {(B(u,v), B(u+du,v), B(u,v+dv), B(u+du,v+dv)) for u,v in [0,1]}
+ *
+ * If P doesn't contain any of the corners of the quad:
+ *
+ * - if one of the corners is in 1,3,5 or 7, other two of them have to
+ * be in 2,4,6 or 8, thus if the last corner is not in P, the length
+ * of one of the edges will be > 1/sqrt(2)
+ *
+ * - if none of the corners is in 1,3,5 or 7, all of them are in 2,4,6
+ * and/or 8. If they are all in different regions, they can't
+ * satisfy the distance constraint. If two of them are in the same
+ * region (let's say 2), no point is in 6 and again it is impossible
+ * to have the center of P in the quad respecting the distance
+ * constraint (both these assertions can be checked by continuity
+ * considering the length of the edges of a quad with the vertices
+ * on the edges of P)
+ *
+ * Each of the cases led to a contradiction, so P contains at least
+ * one of the corners of the quad.
+ */
+
+/*
+ * Make sure that errors are less than 1 in fixed point math if you
+ * change these values.
+ *
+ * The error is amplified by about steps^3/4 times.
+ * The rasterizer always uses a number of steps that is a power of 2.
+ *
+ * 256 is the maximum allowed number of steps (to have error < 1)
+ * using 8.24 for the differences.
+ */
+#define STEPS_MAX_V 256.0
+#define STEPS_MAX_U 256.0
+
+/*
+ * If the patch/curve is only partially visible, split it to a finer
+ * resolution to get higher chances to clip (part of) it.
+ *
+ * These values have not been computed, but simply obtained
+ * empirically (by benchmarking some patches). They should never be
+ * greater than STEPS_MAX_V (or STEPS_MAX_U), but they can be as small
+ * as 1 (depending on how much you want to spend time in splitting the
+ * patch/curve when trying to save some rasterization time).
+ */
+#define STEPS_CLIP_V 64.0
+#define STEPS_CLIP_U 64.0
+
+
+/* Utils */
+static inline double
+sqlen (cairo_point_double_t p0, cairo_point_double_t p1)
+{
+ cairo_point_double_t delta;
+
+ delta.x = p0.x - p1.x;
+ delta.y = p0.y - p1.y;
+
+ return delta.x * delta.x + delta.y * delta.y;
+}
+
+static inline int16_t
+_color_delta_to_shifted_short (int32_t from, int32_t to, int shift)
+{
+ int32_t delta = to - from;
+
+ /* We need to round toward zero, because otherwise adding the
+ * delta 2^shift times can overflow */
+ if (delta >= 0)
+ return delta >> shift;
+ else
+ return -((-delta) >> shift);
+}
+
+/*
+ * Convert a number of steps to the equivalent shift.
+ *
+ * Input: the square of the minimum number of steps
+ *
+ * Output: the smallest integer x such that 2^x > steps
+ */
+static inline int
+sqsteps2shift (double steps_sq)
+{
+ int r;
+ frexp (MAX (1.0, steps_sq), &r);
+ return (r + 1) >> 1;
+}
+
+/*
+ * FD functions
+ *
+ * A Bezier curve is defined (with respect to a parameter t in
+ * [0,1]) from its nodes (x,y,z,w) like this:
+ *
+ * B(t) = x(1-t)^3 + 3yt(1-t)^2 + 3zt^2(1-t) + wt^3
+ *
+ * To efficiently evaluate a Bezier curve, the rasterizer uses forward
+ * differences. Given x, y, z, w (the 4 nodes of the Bezier curve), it
+ * is possible to convert them to forward differences form and walk
+ * over the curve using fd_init (), fd_down () and fd_fwd ().
+ *
+ * f[0] is always the value of the Bezier curve for "current" t.
+ */
+
+/*
+ * Initialize the coefficient for forward differences.
+ *
+ * Input: x,y,z,w are the 4 nodes of the Bezier curve
+ *
+ * Output: f[i] is the i-th difference of the curve
+ *
+ * f[0] is the value of the curve for t==0, i.e. f[0]==x.
+ *
+ * The initial step is 1; this means that each step increases t by 1
+ * (so fd_init () immediately followed by fd_fwd (f) n times makes
+ * f[0] be the value of the curve for t==n).
+ */
+static inline void
+fd_init (double x, double y, double z, double w, double f[4])
+{
+ f[0] = x;
+ f[1] = w - x;
+ f[2] = 6. * (w - 2. * z + y);
+ f[3] = 6. * (w - 3. * z + 3. * y - x);
+}
+
+/*
+ * Halve the step of the coefficients for forward differences.
+ *
+ * Input: f[i] is the i-th difference of the curve
+ *
+ * Output: f[i] is the i-th difference of the curve with half the
+ * original step
+ *
+ * f[0] is not affected, so the current t is not changed.
+ *
+ * The other coefficients are changed so that the step is half the
+ * original step. This means that doing fd_fwd (f) n times with the
+ * input f results in the same f[0] as doing fd_fwd (f) 2n times with
+ * the output f.
+ */
+static inline void
+fd_down (double f[4])
+{
+ f[3] *= 0.125;
+ f[2] = f[2] * 0.25 - f[3];
+ f[1] = (f[1] - f[2]) * 0.5;
+}
+
+/*
+ * Perform one step of forward differences along the curve.
+ *
+ * Input: f[i] is the i-th difference of the curve
+ *
+ * Output: f[i] is the i-th difference of the curve after one step
+ */
+static inline void
+fd_fwd (double f[4])
+{
+ f[0] += f[1];
+ f[1] += f[2];
+ f[2] += f[3];
+}
+
+/*
+ * Transform to integer forward differences.
+ *
+ * Input: d[n] is the n-th difference (in double precision)
+ *
+ * Output: i[n] is the n-th difference (in fixed point precision)
+ *
+ * i[0] is 9.23 fixed point, other differences are 4.28 fixed point.
+ */
+static inline void
+fd_fixed (double d[4], int32_t i[4])
+{
+ i[0] = _cairo_fixed_16_16_from_double (256 * 2 * d[0]);
+ i[1] = _cairo_fixed_16_16_from_double (256 * 16 * d[1]);
+ i[2] = _cairo_fixed_16_16_from_double (256 * 16 * d[2]);
+ i[3] = _cairo_fixed_16_16_from_double (256 * 16 * d[3]);
+}
+
+/*
+ * Perform one step of integer forward differences along the curve.
+ *
+ * Input: f[n] is the n-th difference
+ *
+ * Output: f[n] is the n-th difference
+ *
+ * f[0] is 9.23 fixed point, other differences are 4.28 fixed point.
+ */
+static inline void
+fd_fixed_fwd (int32_t f[4])
+{
+ f[0] += (f[1] >> 5) + ((f[1] >> 4) & 1);
+ f[1] += f[2];
+ f[2] += f[3];
+}
+
+/*
+ * Compute the minimum number of steps that guarantee that walking
+ * over a curve will leave no holes.
+ *
+ * Input: p[0..3] the nodes of the Bezier curve
+ *
+ * Returns: the square of the number of steps
+ *
+ * Idea:
+ *
+ * We want to make sure that at every step we move by less than
+ * 1/sqrt(2).
+ *
+ * The derivative of the cubic Bezier with nodes (p0, p1, p2, p3) is
+ * the quadratic Bezier with nodes (p1-p0, p2-p1, p3-p2) scaled by 3,
+ * so (since a Bezier curve is always bounded by its convex hull), we
+ * can say that:
+ *
+ * max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p1|, |p3-p2|)
+ *
+ * We can improve this by noticing that a quadratic Bezier (a,b,c) is
+ * bounded by the quad (a,lerp(a,b,t),lerp(b,c,t),c) for any t, so
+ * (substituting the previous values, using t=0.5 and simplifying):
+ *
+ * max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|)
+ *
+ * So, to guarantee a maximum step length of 1/sqrt(2) we must do:
+ *
+ * 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) sqrt(2) steps
+ */
+static inline double
+bezier_steps_sq (cairo_point_double_t p[4])
+{
+ double tmp = sqlen (p[0], p[1]);
+ tmp = MAX (tmp, sqlen (p[2], p[3]));
+ tmp = MAX (tmp, sqlen (p[0], p[2]) * .25);
+ tmp = MAX (tmp, sqlen (p[1], p[3]) * .25);
+ return 18.0 * tmp;
+}
+
+/*
+ * Split a 1D Bezier cubic using de Casteljau's algorithm.
+ *
+ * Input: x,y,z,w the nodes of the Bezier curve
+ *
+ * Output: x0,y0,z0,w0 and x1,y1,z1,w1 are respectively the nodes of
+ * the first half and of the second half of the curve
+ *
+ * The output control nodes have to be distinct.
+ */
+static inline void
+split_bezier_1D (double x, double y, double z, double w,
+ double *x0, double *y0, double *z0, double *w0,
+ double *x1, double *y1, double *z1, double *w1)
+{
+ double tmp;
+
+ *x0 = x;
+ *w1 = w;
+
+ tmp = 0.5 * (y + z);
+ *y0 = 0.5 * (x + y);
+ *z1 = 0.5 * (z + w);
+
+ *z0 = 0.5 * (*y0 + tmp);
+ *y1 = 0.5 * (tmp + *z1);
+
+ *w0 = *x1 = 0.5 * (*z0 + *y1);
+}
+
+/*
+ * Split a Bezier curve using de Casteljau's algorithm.
+ *
+ * Input: p[0..3] the nodes of the Bezier curve
+ *
+ * Output: fst_half[0..3] and snd_half[0..3] are respectively the
+ * nodes of the first and of the second half of the curve
+ *
+ * fst_half and snd_half must be different, but they can be the same as
+ * nodes.
+ */
+static void
+split_bezier (cairo_point_double_t p[4],
+ cairo_point_double_t fst_half[4],
+ cairo_point_double_t snd_half[4])
+{
+ split_bezier_1D (p[0].x, p[1].x, p[2].x, p[3].x,
+ &fst_half[0].x, &fst_half[1].x, &fst_half[2].x, &fst_half[3].x,
+ &snd_half[0].x, &snd_half[1].x, &snd_half[2].x, &snd_half[3].x);
+
+ split_bezier_1D (p[0].y, p[1].y, p[2].y, p[3].y,
+ &fst_half[0].y, &fst_half[1].y, &fst_half[2].y, &fst_half[3].y,
+ &snd_half[0].y, &snd_half[1].y, &snd_half[2].y, &snd_half[3].y);
+}
+
+
+typedef enum _intersection {
+ INSIDE = -1, /* the interval is entirely contained in the reference interval */
+ OUTSIDE = 0, /* the interval has no intersection with the reference interval */
+ PARTIAL = 1 /* the interval intersects the reference interval (but is not fully inside it) */
+} intersection_t;
+
+/*
+ * Check if an interval if inside another.
+ *
+ * Input: a,b are the extrema of the first interval
+ * c,d are the extrema of the second interval
+ *
+ * Returns: INSIDE iff [a,b) intersection [c,d) = [a,b)
+ * OUTSIDE iff [a,b) intersection [c,d) = {}
+ * PARTIAL otherwise
+ *
+ * The function assumes a < b and c < d
+ *
+ * Note: Bitwise-anding the results along each component gives the
+ * expected result for [a,b) x [A,B) intersection [c,d) x [C,D).
+ */
+static inline int
+intersect_interval (double a, double b, double c, double d)
+{
+ if (c <= a && b <= d)
+ return INSIDE;
+ else if (a >= d || b <= c)
+ return OUTSIDE;
+ else
+ return PARTIAL;
+}
+
+/*
+ * Set the color of a pixel.
+ *
+ * Input: data is the base pointer of the image
+ * width, height are the dimensions of the image
+ * stride is the stride in bytes between adjacent rows
+ * x, y are the coordinates of the pixel to be colored
+ * r,g,b,a are the color components of the color to be set
+ *
+ * Output: the (x,y) pixel in data has the (r,g,b,a) color
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the pixel to be set is outside the image, this function does
+ * nothing.
+ */
+static inline void
+draw_pixel (unsigned char *data, int width, int height, int stride,
+ int x, int y, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
+{
+ if (likely (0 <= x && 0 <= y && x < width && y < height)) {
+ uint32_t tr, tg, tb, ta;
+
+ /* Premultiply and round */
+ ta = a;
+ tr = r * ta + 0x8000;
+ tg = g * ta + 0x8000;
+ tb = b * ta + 0x8000;
+
+ tr += tr >> 16;
+ tg += tg >> 16;
+ tb += tb >> 16;
+
+ *((uint32_t*) (data + y*stride + 4*x)) = ((ta << 16) & 0xff000000) |
+ ((tr >> 8) & 0xff0000) | ((tg >> 16) & 0xff00) | (tb >> 24);
+ }
+}
+
+/*
+ * Forward-rasterize a cubic curve using forward differences.
+ *
+ * Input: data is the base pointer of the image
+ * width, height are the dimensions of the image
+ * stride is the stride in bytes between adjacent rows
+ * ushift is log2(n) if n is the number of desired steps
+ * dxu[i], dyu[i] are the x,y forward differences of the curve
+ * r0,g0,b0,a0 are the color components of the start point
+ * r3,g3,b3,a3 are the color components of the end point
+ *
+ * Output: data will be changed to have the requested curve drawn in
+ * the specified colors
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * The function draws n+1 pixels, that is from the point at step 0 to
+ * the point at step n, both included. This is the discrete equivalent
+ * to drawing the curve for values of the interpolation parameter in
+ * [0,1] (including both extremes).
+ */
+static inline void
+rasterize_bezier_curve (unsigned char *data, int width, int height, int stride,
+ int ushift, double dxu[4], double dyu[4],
+ uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a0,
+ uint16_t r3, uint16_t g3, uint16_t b3, uint16_t a3)
+{
+ int32_t xu[4], yu[4];
+ int x0, y0, u, usteps = 1 << ushift;
+
+ uint16_t r = r0, g = g0, b = b0, a = a0;
+ int16_t dr = _color_delta_to_shifted_short (r0, r3, ushift);
+ int16_t dg = _color_delta_to_shifted_short (g0, g3, ushift);
+ int16_t db = _color_delta_to_shifted_short (b0, b3, ushift);
+ int16_t da = _color_delta_to_shifted_short (a0, a3, ushift);
+
+ fd_fixed (dxu, xu);
+ fd_fixed (dyu, yu);
+
+ /*
+ * Use (dxu[0],dyu[0]) as origin for the forward differences.
+ *
+ * This makes it possible to handle much larger coordinates (the
+ * ones that can be represented as cairo_fixed_t)
+ */
+ x0 = _cairo_fixed_from_double (dxu[0]);
+ y0 = _cairo_fixed_from_double (dyu[0]);
+ xu[0] = 0;
+ yu[0] = 0;
+
+ for (u = 0; u <= usteps; ++u) {
+ /*
+ * This rasterizer assumes that pixels are integer aligned
+ * squares, so a generic (x,y) point belongs to the pixel with
+ * top-left coordinates (floor(x), floor(y))
+ */
+
+ int x = _cairo_fixed_integer_floor (x0 + (xu[0] >> 15) + ((xu[0] >> 14) & 1));
+ int y = _cairo_fixed_integer_floor (y0 + (yu[0] >> 15) + ((yu[0] >> 14) & 1));
+
+ draw_pixel (data, width, height, stride, x, y, r, g, b, a);
+
+ fd_fixed_fwd (xu);
+ fd_fixed_fwd (yu);
+ r += dr;
+ g += dg;
+ b += db;
+ a += da;
+ }
+}
+
+/*
+ * Clip, split and rasterize a Bezier curve.
+ *
+ * Input: data is the base pointer of the image
+ * width, height are the dimensions of the image
+ * stride is the stride in bytes between adjacent rows
+ * p[i] is the i-th node of the Bezier curve
+ * c0[i] is the i-th color component at the start point
+ * c3[i] is the i-th color component at the end point
+ *
+ * Output: data will be changed to have the requested curve drawn in
+ * the specified colors
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ *
+ * The function guarantees that it will draw the curve with a step
+ * small enough to never have a distance above 1/sqrt(2) between two
+ * consecutive points (which is needed to ensure that no hole can
+ * appear when using this function to rasterize a patch).
+ */
+static void
+draw_bezier_curve (unsigned char *data, int width, int height, int stride,
+ cairo_point_double_t p[4], double c0[4], double c3[4])
+{
+ double top, bottom, left, right, steps_sq;
+ int i, v;
+
+ top = bottom = p[0].y;
+ for (i = 1; i < 4; ++i) {
+ top = MIN (top, p[i].y);
+ bottom = MAX (bottom, p[i].y);
+ }
+
+ /* Check visibility */
+ v = intersect_interval (top, bottom, 0, height);
+ if (v == OUTSIDE)
+ return;
+
+ left = right = p[0].x;
+ for (i = 1; i < 4; ++i) {
+ left = MIN (left, p[i].x);
+ right = MAX (right, p[i].x);
+ }
+
+ v &= intersect_interval (left, right, 0, width);
+ if (v == OUTSIDE)
+ return;
+
+ steps_sq = bezier_steps_sq (p);
+ if (steps_sq >= (v == INSIDE ? STEPS_MAX_U * STEPS_MAX_U : STEPS_CLIP_U * STEPS_CLIP_U)) {
+ /*
+ * The number of steps is greater than the threshold. This
+ * means that either the error would become too big if we
+ * directly rasterized it or that we can probably save some
+ * time by splitting the curve and clipping part of it
+ */
+ cairo_point_double_t first[4], second[4];
+ double midc[4];
+ split_bezier (p, first, second);
+ midc[0] = (c0[0] + c3[0]) * 0.5;
+ midc[1] = (c0[1] + c3[1]) * 0.5;
+ midc[2] = (c0[2] + c3[2]) * 0.5;
+ midc[3] = (c0[3] + c3[3]) * 0.5;
+ draw_bezier_curve (data, width, height, stride, first, c0, midc);
+ draw_bezier_curve (data, width, height, stride, second, midc, c3);
+ } else {
+ double xu[4], yu[4];
+ int ushift = sqsteps2shift (steps_sq), k;
+
+ fd_init (p[0].x, p[1].x, p[2].x, p[3].x, xu);
+ fd_init (p[0].y, p[1].y, p[2].y, p[3].y, yu);
+
+ for (k = 0; k < ushift; ++k) {
+ fd_down (xu);
+ fd_down (yu);
+ }
+
+ rasterize_bezier_curve (data, width, height, stride, ushift,
+ xu, yu,
+ _cairo_color_double_to_short (c0[0]),
+ _cairo_color_double_to_short (c0[1]),
+ _cairo_color_double_to_short (c0[2]),
+ _cairo_color_double_to_short (c0[3]),
+ _cairo_color_double_to_short (c3[0]),
+ _cairo_color_double_to_short (c3[1]),
+ _cairo_color_double_to_short (c3[2]),
+ _cairo_color_double_to_short (c3[3]));
+
+ /* Draw the end point, to make sure that we didn't leave it
+ * out because of rounding */
+ draw_pixel (data, width, height, stride,
+ _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].x)),
+ _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].y)),
+ _cairo_color_double_to_short (c3[0]),
+ _cairo_color_double_to_short (c3[1]),
+ _cairo_color_double_to_short (c3[2]),
+ _cairo_color_double_to_short (c3[3]));
+ }
+}
+
+/*
+ * Forward-rasterize a cubic Bezier patch using forward differences.
+ *
+ * Input: data is the base pointer of the image
+ * width, height are the dimensions of the image
+ * stride is the stride in bytes between adjacent rows
+ * vshift is log2(n) if n is the number of desired steps
+ * p[i][j], p[i][j] are the the nodes of the Bezier patch
+ * col[i][j] is the j-th color component of the i-th corner
+ *
+ * Output: data will be changed to have the requested patch drawn in
+ * the specified colors
+ *
+ * The nodes of the patch are as follows:
+ *
+ * u\v 0 - > 1
+ * 0 p00 p01 p02 p03
+ * | p10 p11 p12 p13
+ * v p20 p21 p22 p23
+ * 1 p30 p31 p32 p33
+ *
+ * i.e. u varies along the first component (rows), v varies along the
+ * second one (columns).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ * c[0..3] are the colors in p00, p30, p03, p33 respectively
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the patch folds over itself, the part with the highest v
+ * parameter is considered above. If both have the same v, the one
+ * with the highest u parameter is above.
+ *
+ * The function draws n+1 curves, that is from the curve at step 0 to
+ * the curve at step n, both included. This is the discrete equivalent
+ * to drawing the patch for values of the interpolation parameter in
+ * [0,1] (including both extremes).
+ */
+static inline void
+rasterize_bezier_patch (unsigned char *data, int width, int height, int stride, int vshift,
+ cairo_point_double_t p[4][4], double col[4][4])
+{
+ double pv[4][2][4], cstart[4], cend[4], dcstart[4], dcend[4];
+ int vsteps, v, i, k;
+
+ vsteps = 1 << vshift;
+
+ /*
+ * pv[i][0] is the function (represented using forward
+ * differences) mapping v to the x coordinate of the i-th node of
+ * the Bezier curve with parameter u.
+ * (Likewise p[i][0] gives the y coordinate).
+ *
+ * This means that (pv[0][0][0],pv[0][1][0]),
+ * (pv[1][0][0],pv[1][1][0]), (pv[2][0][0],pv[2][1][0]) and
+ * (pv[3][0][0],pv[3][1][0]) are the nodes of the Bezier curve for
+ * the "current" v value (see the FD comments for more details).
+ */
+ for (i = 0; i < 4; ++i) {
+ fd_init (p[i][0].x, p[i][1].x, p[i][2].x, p[i][3].x, pv[i][0]);
+ fd_init (p[i][0].y, p[i][1].y, p[i][2].y, p[i][3].y, pv[i][1]);
+ for (k = 0; k < vshift; ++k) {
+ fd_down (pv[i][0]);
+ fd_down (pv[i][1]);
+ }
+ }
+
+ for (i = 0; i < 4; ++i) {
+ cstart[i] = col[0][i];
+ cend[i] = col[1][i];
+ dcstart[i] = (col[2][i] - col[0][i]) / vsteps;
+ dcend[i] = (col[3][i] - col[1][i]) / vsteps;
+ }
+
+ for (v = 0; v <= vsteps; ++v) {
+ cairo_point_double_t nodes[4];
+ for (i = 0; i < 4; ++i) {
+ nodes[i].x = pv[i][0][0];
+ nodes[i].y = pv[i][1][0];
+ }
+
+ draw_bezier_curve (data, width, height, stride, nodes, cstart, cend);
+
+ for (i = 0; i < 4; ++i) {
+ fd_fwd (pv[i][0]);
+ fd_fwd (pv[i][1]);
+ cstart[i] += dcstart[i];
+ cend[i] += dcend[i];
+ }
+ }
+}
+
+/*
+ * Clip, split and rasterize a Bezier cubic patch.
+ *
+ * Input: data is the base pointer of the image
+ * width, height are the dimensions of the image
+ * stride is the stride in bytes between adjacent rows
+ * p[i][j], p[i][j] are the nodes of the patch
+ * col[i][j] is the j-th color component of the i-th corner
+ *
+ * Output: data will be changed to have the requested patch drawn in
+ * the specified colors
+ *
+ * The nodes of the patch are as follows:
+ *
+ * u\v 0 - > 1
+ * 0 p00 p01 p02 p03
+ * | p10 p11 p12 p13
+ * v p20 p21 p22 p23
+ * 1 p30 p31 p32 p33
+ *
+ * i.e. u varies along the first component (rows), v varies along the
+ * second one (columns).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ * c[0..3] are the colors in p00, p30, p03, p33 respectively
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the patch folds over itself, the part with the highest v
+ * parameter is considered above. If both have the same v, the one
+ * with the highest u parameter is above.
+ *
+ * The function guarantees that it will draw the patch with a step
+ * small enough to never have a distance above 1/sqrt(2) between two
+ * adjacent points (which guarantees that no hole can appear).
+ *
+ * This function can be used to rasterize a tile of PDF type 7
+ * shadings (see http://www.adobe.com/devnet/pdf/pdf_reference.html).
+ */
+static void
+draw_bezier_patch (unsigned char *data, int width, int height, int stride,
+ cairo_point_double_t p[4][4], double c[4][4])
+{
+ double top, bottom, left, right, steps_sq;
+ int i, j, v;
+
+ top = bottom = p[0][0].y;
+ for (i = 0; i < 4; ++i) {
+ for (j= 0; j < 4; ++j) {
+ top = MIN (top, p[i][j].y);
+ bottom = MAX (bottom, p[i][j].y);
+ }
+ }
+
+ v = intersect_interval (top, bottom, 0, height);
+ if (v == OUTSIDE)
+ return;
+
+ left = right = p[0][0].x;
+ for (i = 0; i < 4; ++i) {
+ for (j= 0; j < 4; ++j) {
+ left = MIN (left, p[i][j].x);
+ right = MAX (right, p[i][j].x);
+ }
+ }
+
+ v &= intersect_interval (left, right, 0, width);
+ if (v == OUTSIDE)
+ return;
+
+ steps_sq = 0;
+ for (i = 0; i < 4; ++i)
+ steps_sq = MAX (steps_sq, bezier_steps_sq (p[i]));
+
+ if (steps_sq >= (v == INSIDE ? STEPS_MAX_V * STEPS_MAX_V : STEPS_CLIP_V * STEPS_CLIP_V)) {
+ /* The number of steps is greater than the threshold. This
+ * means that either the error would become too big if we
+ * directly rasterized it or that we can probably save some
+ * time by splitting the curve and clipping part of it. The
+ * patch is only split in the v direction to guarantee that
+ * rasterizing each part will overwrite parts with low v with
+ * overlapping parts with higher v. */
+
+ cairo_point_double_t first[4][4], second[4][4];
+ double subc[4][4];
+
+ for (i = 0; i < 4; ++i)
+ split_bezier (p[i], first[i], second[i]);
+
+ for (i = 0; i < 4; ++i) {
+ subc[0][i] = c[0][i];
+ subc[1][i] = c[1][i];
+ subc[2][i] = 0.5 * (c[0][i] + c[2][i]);
+ subc[3][i] = 0.5 * (c[1][i] + c[3][i]);
+ }
+
+ draw_bezier_patch (data, width, height, stride, first, subc);
+
+ for (i = 0; i < 4; ++i) {
+ subc[0][i] = subc[2][i];
+ subc[1][i] = subc[3][i];
+ subc[2][i] = c[2][i];
+ subc[3][i] = c[3][i];
+ }
+ draw_bezier_patch (data, width, height, stride, second, subc);
+ } else {
+ rasterize_bezier_patch (data, width, height, stride, sqsteps2shift (steps_sq), p, c);
+ }
+}
+
+/*
+ * Draw a tensor product shading pattern.
+ *
+ * Input: mesh is the mesh pattern
+ * data is the base pointer of the image
+ * width, height are the dimensions of the image
+ * stride is the stride in bytes between adjacent rows
+ *
+ * Output: data will be changed to have the pattern drawn on it
+ *
+ * data is assumed to be clear and its content is assumed to be in
+ * CAIRO_FORMAT_ARGB32 (8 bpc, premultiplied).
+ *
+ * This function can be used to rasterize a PDF type 7 shading (see
+ * http://www.adobe.com/devnet/pdf/pdf_reference.html).
+ */
+void
+_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
+ void *data,
+ int width,
+ int height,
+ int stride,
+ double x_offset,
+ double y_offset)
+{
+ cairo_point_double_t nodes[4][4];
+ double colors[4][4];
+ cairo_matrix_t p2u;
+ unsigned int i, j, k, n;
+ cairo_status_t status;
+ const cairo_mesh_patch_t *patch;
+ const cairo_color_t *c;
+
+ assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
+ assert (mesh->current_patch == NULL);
+
+ p2u = mesh->base.matrix;
+ status = cairo_matrix_invert (&p2u);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ n = _cairo_array_num_elements (&mesh->patches);
+ patch = _cairo_array_index_const (&mesh->patches, 0);
+ if (patch == NULL)
+ status = CAIRO_STATUS_NULL_POINTER;
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < 4; j++) {
+ for (k = 0; k < 4; k++) {
+ nodes[j][k] = patch->points[j][k];
+ cairo_matrix_transform_point (&p2u, &nodes[j][k].x, &nodes[j][k].y);
+ nodes[j][k].x += x_offset;
+ nodes[j][k].y += y_offset;
+ }
+ }
+
+ c = &patch->colors[0];
+ colors[0][0] = c->red;
+ colors[0][1] = c->green;
+ colors[0][2] = c->blue;
+ colors[0][3] = c->alpha;
+
+ c = &patch->colors[3];
+ colors[1][0] = c->red;
+ colors[1][1] = c->green;
+ colors[1][2] = c->blue;
+ colors[1][3] = c->alpha;
+
+ c = &patch->colors[1];
+ colors[2][0] = c->red;
+ colors[2][1] = c->green;
+ colors[2][2] = c->blue;
+ colors[2][3] = c->alpha;
+
+ c = &patch->colors[2];
+ colors[3][0] = c->red;
+ colors[3][1] = c->green;
+ colors[3][2] = c->blue;
+ colors[3][3] = c->alpha;
+
+ draw_bezier_patch (data, width, height, stride, nodes, colors);
+ patch++;
+ }
+}
diff --git a/src/cairo-misc.c b/src/cairo-misc.c
new file mode 100755
index 000000000..bb37e1a0a
--- /dev/null
+++ b/src/cairo-misc.c
@@ -0,0 +1,935 @@
+/* -*- 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 ((int)CAIRO_STATUS_LAST_STATUS < (int)CAIRO_INT_STATUS_UNSUPPORTED);
+COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127);
+
+/**
+ * SECTION:cairo-status
+ * @Title: Error handling
+ * @Short_Description: Decoding cairo's status
+ * @See_Also: cairo_status(), cairo_surface_status(), cairo_pattern_status(),
+ * cairo_font_face_status(), cairo_scaled_font_status(),
+ * cairo_region_status()
+ *
+ * Cairo uses a single status type to represent all kinds of errors. A status
+ * value of %CAIRO_STATUS_SUCCESS represents no error and has an integer value
+ * of zero. All other status values represent an error.
+ *
+ * Cairo's error handling is designed to be easy to use and safe. All major
+ * cairo objects <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
+ *
+ * Since: 1.0
+ **/
+const char *
+cairo_status_to_string (cairo_status_t status)
+{
+ switch (status) {
+ case CAIRO_STATUS_SUCCESS:
+ return "no error has occurred";
+ case CAIRO_STATUS_NO_MEMORY:
+ return "out of memory";
+ case CAIRO_STATUS_INVALID_RESTORE:
+ return "cairo_restore() without matching cairo_save()";
+ case CAIRO_STATUS_INVALID_POP_GROUP:
+ return "no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()";
+ case CAIRO_STATUS_NO_CURRENT_POINT:
+ return "no current point defined";
+ case CAIRO_STATUS_INVALID_MATRIX:
+ return "invalid matrix (not invertible)";
+ case CAIRO_STATUS_INVALID_STATUS:
+ return "invalid value for an input cairo_status_t";
+ case CAIRO_STATUS_NULL_POINTER:
+ return "NULL pointer";
+ case CAIRO_STATUS_INVALID_STRING:
+ return "input string not valid UTF-8";
+ case CAIRO_STATUS_INVALID_PATH_DATA:
+ return "input path data not valid";
+ case CAIRO_STATUS_READ_ERROR:
+ return "error while reading from input stream";
+ case CAIRO_STATUS_WRITE_ERROR:
+ return "error while writing to output stream";
+ case CAIRO_STATUS_SURFACE_FINISHED:
+ return "the target surface has been finished";
+ case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
+ return "the surface type is not appropriate for the operation";
+ case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
+ return "the pattern type is not appropriate for the operation";
+ case CAIRO_STATUS_INVALID_CONTENT:
+ return "invalid value for an input cairo_content_t";
+ case CAIRO_STATUS_INVALID_FORMAT:
+ return "invalid value for an input cairo_format_t";
+ case CAIRO_STATUS_INVALID_VISUAL:
+ return "invalid value for an input Visual*";
+ case CAIRO_STATUS_FILE_NOT_FOUND:
+ return "file not found";
+ case CAIRO_STATUS_INVALID_DASH:
+ return "invalid value for a dash setting";
+ case CAIRO_STATUS_INVALID_DSC_COMMENT:
+ return "invalid value for a DSC comment";
+ case CAIRO_STATUS_INVALID_INDEX:
+ return "invalid index passed to getter";
+ case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
+ return "clip region not representable in desired format";
+ case CAIRO_STATUS_TEMP_FILE_ERROR:
+ return "error creating or writing to a temporary file";
+ case CAIRO_STATUS_INVALID_STRIDE:
+ return "invalid value for stride";
+ case CAIRO_STATUS_FONT_TYPE_MISMATCH:
+ return "the font type is not appropriate for the operation";
+ case CAIRO_STATUS_USER_FONT_IMMUTABLE:
+ return "the user-font is immutable";
+ case CAIRO_STATUS_USER_FONT_ERROR:
+ return "error occurred in a user-font callback function";
+ case CAIRO_STATUS_NEGATIVE_COUNT:
+ return "negative number used where it is not allowed";
+ case CAIRO_STATUS_INVALID_CLUSTERS:
+ return "input clusters do not represent the accompanying text and glyph arrays";
+ case CAIRO_STATUS_INVALID_SLANT:
+ return "invalid value for an input cairo_font_slant_t";
+ case CAIRO_STATUS_INVALID_WEIGHT:
+ return "invalid value for an input cairo_font_weight_t";
+ case CAIRO_STATUS_INVALID_SIZE:
+ return "invalid value (typically too big) for the size of the input (surface, pattern, etc.)";
+ case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+ return "user-font method not implemented";
+ case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
+ return "the device type is not appropriate for the operation";
+ case CAIRO_STATUS_DEVICE_ERROR:
+ return "an operation to the device caused an unspecified error";
+ case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+ return "invalid operation during mesh pattern construction";
+ case CAIRO_STATUS_DEVICE_FINISHED:
+ return "the target device has been finished";
+ 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)
+{
+ free (glyphs);
+}
+slim_hidden_def (cairo_glyph_free);
+
+/**
+ * cairo_text_cluster_allocate:
+ * @num_clusters: number of text_clusters to allocate
+ *
+ * Allocates an array of #cairo_text_cluster_t's.
+ * This function is only useful in implementations of
+ * #cairo_user_scaled_font_text_to_glyphs_func_t where the user
+ * needs to allocate an array of text clusters that cairo will free.
+ * For all other uses, user can use their own allocation method
+ * for text clusters.
+ *
+ * This function returns %NULL if @num_clusters is not positive,
+ * or if out of memory. That means, the %NULL return value
+ * signals out-of-memory only if @num_clusters was positive.
+ *
+ * Returns: the newly allocated array of text clusters that should be
+ * freed using cairo_text_cluster_free()
+ *
+ * Since: 1.8
+ **/
+cairo_text_cluster_t *
+cairo_text_cluster_allocate (int num_clusters)
+{
+ if (num_clusters <= 0)
+ return NULL;
+
+ return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
+}
+slim_hidden_def (cairo_text_cluster_allocate);
+
+/**
+ * cairo_text_cluster_free:
+ * @clusters: array of text clusters to free, or %NULL
+ *
+ * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate().
+ * This function is only useful to free text cluster array returned
+ * by cairo_scaled_font_text_to_glyphs() where cairo returns
+ * an array of text clusters that the user will free.
+ * For all other uses, user can use their own allocation method
+ * for text clusters.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_text_cluster_free (cairo_text_cluster_t *clusters)
+{
+ free (clusters);
+}
+slim_hidden_def (cairo_text_cluster_free);
+
+
+/* Private stuff */
+
+/**
+ * _cairo_validate_text_clusters:
+ * @utf8: UTF-8 text
+ * @utf8_len: length of @utf8 in bytes
+ * @glyphs: array of glyphs
+ * @num_glyphs: number of glyphs
+ * @clusters: array of cluster mapping information
+ * @num_clusters: number of clusters in the mapping
+ * @cluster_flags: cluster flags
+ *
+ * Check that clusters cover the entire glyphs and utf8 arrays,
+ * and that cluster boundaries are UTF-8 boundaries.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS upon success, or
+ * %CAIRO_STATUS_INVALID_CLUSTERS on error.
+ * The error is either invalid UTF-8 input,
+ * or bad cluster mapping.
+ **/
+cairo_status_t
+_cairo_validate_text_clusters (const char *utf8,
+ int utf8_len,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags)
+{
+ cairo_status_t status;
+ unsigned int n_bytes = 0;
+ unsigned int n_glyphs = 0;
+ int i;
+
+ for (i = 0; i < num_clusters; i++) {
+ int cluster_bytes = clusters[i].num_bytes;
+ int cluster_glyphs = clusters[i].num_glyphs;
+
+ if (cluster_bytes < 0 || cluster_glyphs < 0)
+ goto BAD;
+
+ /* A cluster should cover at least one character or glyph.
+ * I can't see any use for a 0,0 cluster.
+ * I can't see an immediate use for a zero-text cluster
+ * right now either, but they don't harm.
+ * Zero-glyph clusters on the other hand are useful for
+ * things like U+200C ZERO WIDTH NON-JOINER */
+ if (cluster_bytes == 0 && cluster_glyphs == 0)
+ goto BAD;
+
+ /* Since n_bytes and n_glyphs are unsigned, but the rest of
+ * values involved are signed, we can detect overflow easily */
+ if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs)
+ goto BAD;
+
+ /* Make sure we've got valid UTF-8 for the cluster */
+ status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL);
+ if (unlikely (status))
+ return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS);
+
+ n_bytes += cluster_bytes ;
+ n_glyphs += cluster_glyphs;
+ }
+
+ if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) {
+ BAD:
+ return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_operator_bounded_by_mask:
+ * @op: a #cairo_operator_t
+ *
+ * A bounded operator is one where mask pixel
+ * of zero results in no effect on the destination image.
+ *
+ * Unbounded operators often require special handling; if you, for
+ * example, draw trapezoids with an unbounded operator, the effect
+ * extends past the bounding box of the trapezoids.
+ *
+ * Return value: %TRUE if the operator is bounded by the mask operand
+ **/
+cairo_bool_t
+_cairo_operator_bounded_by_mask (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return TRUE;
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return FALSE;
+ }
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+/**
+ * _cairo_operator_bounded_by_source:
+ * @op: a #cairo_operator_t
+ *
+ * A bounded operator is one where source pixels of zero
+ * (in all four components, r, g, b and a) effect no change
+ * in the resulting destination image.
+ *
+ * Unbounded operators often require special handling; if you, for
+ * example, copy a surface with the SOURCE operator, the effect
+ * extends past the bounding box of the source surface.
+ *
+ * Return value: %TRUE if the operator is bounded by the source operand
+ **/
+cairo_bool_t
+_cairo_operator_bounded_by_source (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return TRUE;
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return FALSE;
+ }
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+uint32_t
+_cairo_operator_bounded_by_either (cairo_operator_t op)
+{
+ switch (op) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE;
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ return CAIRO_OPERATOR_BOUND_BY_MASK;
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return 0;
+ }
+
+}
+
+#if DISABLE_SOME_FLOATING_POINT
+/* This function is identical to the C99 function lround(), except that it
+ * performs arithmetic rounding (floor(d + .5) instead of away-from-zero rounding) and
+ * has a valid input range of (INT_MIN, INT_MAX] instead of
+ * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems
+ * than other commonly used methods for rounding (lround, round, rint, lrint
+ * or float (d + 0.5)).
+ *
+ * The reason why this function is much faster on x86 than other
+ * methods is due to the fact that it avoids the fldcw instruction.
+ * This instruction incurs a large performance penalty on modern Intel
+ * processors due to how it prevents efficient instruction pipelining.
+ *
+ * The reason why this function is much faster on FPU-less systems is for
+ * an entirely different reason. All common rounding methods involve multiple
+ * floating-point operations. Each one of these operations has to be
+ * emulated in software, which adds up to be a large performance penalty.
+ * This function doesn't perform any floating-point calculations, and thus
+ * avoids this penalty.
+ */
+int
+_cairo_lround (double d)
+{
+ uint32_t top, shift_amount, output;
+ union {
+ double d;
+ uint64_t ui64;
+ uint32_t ui32[2];
+ } u;
+
+ u.d = d;
+
+ /* If the integer word order doesn't match the float word order, we swap
+ * the words of the input double. This is needed because we will be
+ * treating the whole double as a 64-bit unsigned integer. Notice that we
+ * use WORDS_BIGENDIAN to detect the integer word order, which isn't
+ * exactly correct because WORDS_BIGENDIAN refers to byte order, not word
+ * order. Thus, we are making the assumption that the byte order is the
+ * same as the integer word order which, on the modern machines that we
+ * care about, is OK.
+ */
+#if ( defined(FLOAT_WORDS_BIGENDIAN) && !defined(WORDS_BIGENDIAN)) || \
+ (!defined(FLOAT_WORDS_BIGENDIAN) && defined(WORDS_BIGENDIAN))
+ {
+ uint32_t temp = u.ui32[0];
+ u.ui32[0] = u.ui32[1];
+ u.ui32[1] = temp;
+ }
+#endif
+
+#ifdef WORDS_BIGENDIAN
+ #define MSW (0) /* Most Significant Word */
+ #define LSW (1) /* Least Significant Word */
+#else
+ #define MSW (1)
+ #define LSW (0)
+#endif
+
+ /* By shifting the most significant word of the input double to the
+ * right 20 places, we get the very "top" of the double where the exponent
+ * and sign bit lie.
+ */
+ top = u.ui32[MSW] >> 20;
+
+ /* Here, we calculate how much we have to shift the mantissa to normalize
+ * it to an integer value. We extract the exponent "top" by masking out the
+ * sign bit, then we calculate the shift amount by subtracting the exponent
+ * from the bias. Notice that the correct bias for 64-bit doubles is
+ * actually 1075, but we use 1053 instead for two reasons:
+ *
+ * 1) To perform rounding later on, we will first need the target
+ * value in a 31.1 fixed-point format. Thus, the bias needs to be one
+ * less: (1075 - 1: 1074).
+ *
+ * 2) To avoid shifting the mantissa as a full 64-bit integer (which is
+ * costly on certain architectures), we break the shift into two parts.
+ * First, the upper and lower parts of the mantissa are shifted
+ * individually by a constant amount that all valid inputs will require
+ * at the very least. This amount is chosen to be 21, because this will
+ * allow the two parts of the mantissa to later be combined into a
+ * single 32-bit representation, on which the remainder of the shift
+ * will be performed. Thus, we decrease the bias by an additional 21:
+ * (1074 - 21: 1053).
+ */
+ shift_amount = 1053 - (top & 0x7FF);
+
+ /* We are done with the exponent portion in "top", so here we shift it off
+ * the end.
+ */
+ top >>= 11;
+
+ /* Before we perform any operations on the mantissa, we need to OR in
+ * the implicit 1 at the top (see the IEEE-754 spec). We needn't mask
+ * off the sign bit nor the exponent bits because these higher bits won't
+ * make a bit of difference in the rest of our calculations.
+ */
+ u.ui32[MSW] |= 0x100000;
+
+ /* If the input double is negative, we have to decrease the mantissa
+ * by a hair. This is an important part of performing arithmetic rounding,
+ * as negative numbers must round towards positive infinity in the
+ * halfwase case of -x.5. Since "top" contains only the sign bit at this
+ * point, we can just decrease the mantissa by the value of "top".
+ */
+ u.ui64 -= top;
+
+ /* By decrementing "top", we create a bitmask with a value of either
+ * 0x0 (if the input was negative) or 0xFFFFFFFF (if the input was positive
+ * and thus the unsigned subtraction underflowed) that we'll use later.
+ */
+ top--;
+
+ /* Here, we shift the mantissa by the constant value as described above.
+ * We can emulate a 64-bit shift right by 21 through shifting the top 32
+ * bits left 11 places and ORing in the bottom 32 bits shifted 21 places
+ * to the right. Both parts of the mantissa are now packed into a single
+ * 32-bit integer. Although we severely truncate the lower part in the
+ * process, we still have enough significant bits to perform the conversion
+ * without error (for all valid inputs).
+ */
+ output = (u.ui32[MSW] << 11) | (u.ui32[LSW] >> 21);
+
+ /* Next, we perform the shift that converts the X.Y fixed-point number
+ * currently found in "output" to the desired 31.1 fixed-point format
+ * needed for the following rounding step. It is important to consider
+ * all possible values for "shift_amount" at this point:
+ *
+ * - {shift_amount < 0} Since shift_amount is an unsigned integer, it
+ * really can't have a value less than zero. But, if the shift_amount
+ * calculation above caused underflow (which would happen with
+ * input > INT_MAX or input <= INT_MIN) then shift_amount will now be
+ * a very large number, and so this shift will result in complete
+ * garbage. But that's OK, as the input was out of our range, so our
+ * output is undefined.
+ *
+ * - {shift_amount > 31} If the magnitude of the input was very small
+ * (i.e. |input| << 1.0), shift_amount will have a value greater than
+ * 31. Thus, this shift will also result in garbage. After performing
+ * the shift, we will zero-out "output" if this is the case.
+ *
+ * - {0 <= shift_amount < 32} In this case, the shift will properly convert
+ * the mantissa into a 31.1 fixed-point number.
+ */
+ output >>= shift_amount;
+
+ /* This is where we perform rounding with the 31.1 fixed-point number.
+ * Since what we're after is arithmetic rounding, we simply add the single
+ * fractional bit into the integer part of "output", and just keep the
+ * integer part.
+ */
+ output = (output >> 1) + (output & 1);
+
+ /* Here, we zero-out the result if the magnitude if the input was very small
+ * (as explained in the section above). Notice that all input out of the
+ * valid range is also caught by this condition, which means we produce 0
+ * for all invalid input, which is a nice side effect.
+ *
+ * The most straightforward way to do this would be:
+ *
+ * if (shift_amount > 31)
+ * output = 0;
+ *
+ * But we can use a little trick to avoid the potential branch. The
+ * expression (shift_amount > 31) will be either 1 or 0, which when
+ * decremented will be either 0x0 or 0xFFFFFFFF (unsigned underflow),
+ * which can be used to conditionally mask away all the bits in "output"
+ * (in the 0x0 case), effectively zeroing it out. Certain, compilers would
+ * have done this for us automatically.
+ */
+ output &= ((shift_amount > 31) - 1);
+
+ /* If the input double was a negative number, then we have to negate our
+ * output. The most straightforward way to do this would be:
+ *
+ * if (!top)
+ * output = -output;
+ *
+ * as "top" at this point is either 0x0 (if the input was negative) or
+ * 0xFFFFFFFF (if the input was positive). But, we can use a trick to
+ * avoid the branch. Observe that the following snippet of code has the
+ * same effect as the reference snippet above:
+ *
+ * if (!top)
+ * output = 0 - output;
+ * else
+ * output = output - 0;
+ *
+ * Armed with the bitmask found in "top", we can condense the two statements
+ * into the following:
+ *
+ * output = (output & top) - (output & ~top);
+ *
+ * where, in the case that the input double was negative, "top" will be 0,
+ * and the statement will be equivalent to:
+ *
+ * output = (0) - (output);
+ *
+ * and if the input double was positive, "top" will be 0xFFFFFFFF, and the
+ * statement will be equivalent to:
+ *
+ * output = (output) - (0);
+ *
+ * Which, as pointed out earlier, is equivalent to the original reference
+ * snippet.
+ */
+ output = (output & top) - (output & ~top);
+
+ return output;
+#undef MSW
+#undef LSW
+}
+#endif
+
+/* Convert a 32-bit IEEE single precision floating point number to a
+ * 'half' representation (s10.5)
+ */
+uint16_t
+_cairo_half_from_float (float f)
+{
+ union {
+ uint32_t ui;
+ float f;
+ } u;
+ int s, e, m;
+
+ u.f = f;
+ s = (u.ui >> 16) & 0x00008000;
+ e = ((u.ui >> 23) & 0x000000ff) - (127 - 15);
+ m = u.ui & 0x007fffff;
+ if (e <= 0) {
+ if (e < -10) {
+ /* underflow */
+ return 0;
+ }
+
+ m = (m | 0x00800000) >> (1 - e);
+
+ /* round to nearest, round 0.5 up. */
+ if (m & 0x00001000)
+ m += 0x00002000;
+ return s | (m >> 13);
+ } else if (e == 0xff - (127 - 15)) {
+ if (m == 0) {
+ /* infinity */
+ return s | 0x7c00;
+ } else {
+ /* nan */
+ m >>= 13;
+ return s | 0x7c00 | m | (m == 0);
+ }
+ } else {
+ /* round to nearest, round 0.5 up. */
+ if (m & 0x00001000) {
+ m += 0x00002000;
+
+ if (m & 0x00800000) {
+ m = 0;
+ e += 1;
+ }
+ }
+
+ if (e > 30) {
+ /* overflow -> infinity */
+ return s | 0x7c00;
+ }
+
+ return s | (e << 10) | (m >> 13);
+ }
+}
+
+
+#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/src/cairo-mono-scan-converter.c b/src/cairo-mono-scan-converter.c
new file mode 100755
index 000000000..2a9546cf8
--- /dev/null
+++ b/src/cairo-mono-scan-converter.c
@@ -0,0 +1,612 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/*
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+struct quorem {
+ int32_t quo;
+ int32_t rem;
+};
+
+struct edge {
+ struct edge *next, *prev;
+
+ int32_t height_left;
+ int32_t dir;
+ int32_t vertical;
+
+ int32_t dy;
+ struct quorem x;
+ struct quorem dxdy;
+};
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+ /* The vertical clip extents. */
+ int32_t ymin, ymax;
+
+ int num_edges;
+ struct edge *edges;
+
+ /* Array of edges all starting in the same bucket. An edge is put
+ * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+ * it is added to the polygon. */
+ struct edge **y_buckets;
+
+ struct edge *y_buckets_embedded[64];
+ struct edge edges_embedded[32];
+};
+
+struct mono_scan_converter {
+ struct polygon polygon[1];
+
+ /* Leftmost edge on the current scan line. */
+ struct edge head, tail;
+ int is_vertical;
+
+ cairo_half_open_span_t *spans;
+ cairo_half_open_span_t spans_embedded[64];
+ int num_spans;
+
+ /* Clip box. */
+ int32_t xmin, xmax;
+ int32_t ymin, ymax;
+};
+
+#define I(x) _cairo_fixed_integer_round_down(x)
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+ struct quorem qr;
+ qr.quo = a/b;
+ qr.rem = a%b;
+ if ((a^b)<0 && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+ struct quorem qr;
+ long long xa = (long long)x*a;
+ qr.quo = xa/b;
+ qr.rem = xa%b;
+ if ((xa>=0) != (b>=0) && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+static cairo_status_t
+polygon_init (struct polygon *polygon, int ymin, int ymax)
+{
+ unsigned h = ymax - ymin + 1;
+
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ if (h > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+ polygon->y_buckets = _cairo_malloc_ab (h, sizeof (struct edge *));
+ if (unlikely (NULL == polygon->y_buckets))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ memset (polygon->y_buckets, 0, h * sizeof (struct edge *));
+ polygon->y_buckets[h-1] = (void *)-1;
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ if (polygon->edges != polygon->edges_embedded)
+ free (polygon->edges);
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
+ struct edge *e,
+ int y)
+{
+ struct edge **ptail = &polygon->y_buckets[y - polygon->ymin];
+ if (*ptail)
+ (*ptail)->prev = e;
+ e->next = *ptail;
+ e->prev = NULL;
+ *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+ const cairo_edge_t *edge)
+{
+ struct edge *e;
+ cairo_fixed_t dx;
+ cairo_fixed_t dy;
+ int y, ytop, ybot;
+ int ymin = polygon->ymin;
+ int ymax = polygon->ymax;
+
+ y = I(edge->top);
+ ytop = MAX(y, ymin);
+
+ y = I(edge->bottom);
+ ybot = MIN(y, ymax);
+
+ if (ybot <= ytop)
+ return;
+
+ e = polygon->edges + polygon->num_edges++;
+ e->height_left = ybot - ytop;
+ e->dir = edge->dir;
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+
+ if (dx == 0) {
+ e->vertical = TRUE;
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ e->dxdy.quo = 0;
+ e->dxdy.rem = 0;
+ e->dy = 0;
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_muldivrem (dx, CAIRO_FIXED_ONE, dy);
+ e->dy = dy;
+
+ e->x = floored_muldivrem (ytop * CAIRO_FIXED_ONE + CAIRO_FIXED_FRAC_MASK/2 - edge->line.p1.y,
+ dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+ e->x.rem -= dy;
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e, ytop);
+}
+
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+ struct edge *head, **next, *prev;
+ int32_t x;
+
+ prev = head_a->prev;
+ next = &head;
+ if (head_a->x.quo <= head_b->x.quo) {
+ head = head_a;
+ } else {
+ head = head_b;
+ head_b->prev = prev;
+ goto start_with_b;
+ }
+
+ do {
+ x = head_b->x.quo;
+ while (head_a != NULL && head_a->x.quo <= x) {
+ prev = head_a;
+ next = &head_a->next;
+ head_a = head_a->next;
+ }
+
+ head_b->prev = prev;
+ *next = head_b;
+ if (head_a == NULL)
+ return head;
+
+start_with_b:
+ x = head_a->x.quo;
+ while (head_b != NULL && head_b->x.quo <= x) {
+ prev = head_b;
+ next = &head_b->next;
+ head_b = head_b->next;
+ }
+
+ head_a->prev = prev;
+ *next = head_a;
+ if (head_b == NULL)
+ return head;
+ } while (1);
+}
+
+static struct edge *
+sort_edges (struct edge *list,
+ unsigned int level,
+ struct edge **head_out)
+{
+ struct edge *head_other, *remaining;
+ unsigned int i;
+
+ head_other = list->next;
+
+ if (head_other == NULL) {
+ *head_out = list;
+ return NULL;
+ }
+
+ remaining = head_other->next;
+ if (list->x.quo <= head_other->x.quo) {
+ *head_out = list;
+ head_other->next = NULL;
+ } else {
+ *head_out = head_other;
+ head_other->prev = list->prev;
+ head_other->next = list;
+ list->prev = head_other;
+ list->next = NULL;
+ }
+
+ for (i = 0; i < level && remaining; i++) {
+ remaining = sort_edges (remaining, i, &head_other);
+ *head_out = merge_sorted_edges (*head_out, head_other);
+ }
+
+ return remaining;
+}
+
+static struct edge *
+merge_unsorted_edges (struct edge *head, struct edge *unsorted)
+{
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ return merge_sorted_edges (head, unsorted);
+}
+
+inline static void
+active_list_merge_edges (struct mono_scan_converter *c, struct edge *edges)
+{
+ struct edge *e;
+
+ for (e = edges; c->is_vertical && e; e = e->next)
+ c->is_vertical = e->vertical;
+
+ c->head.next = merge_unsorted_edges (c->head.next, edges);
+}
+
+inline static void
+add_span (struct mono_scan_converter *c, int x1, int x2)
+{
+ int n;
+
+ if (x1 < c->xmin)
+ x1 = c->xmin;
+ if (x2 > c->xmax)
+ x2 = c->xmax;
+ if (x2 <= x1)
+ return;
+
+ n = c->num_spans++;
+ c->spans[n].x = x1;
+ c->spans[n].coverage = 255;
+
+ n = c->num_spans++;
+ c->spans[n].x = x2;
+ c->spans[n].coverage = 0;
+}
+
+inline static void
+row (struct mono_scan_converter *c, unsigned int mask)
+{
+ struct edge *edge = c->head.next;
+ int xstart = INT_MIN, prev_x = INT_MIN;
+ int winding = 0;
+
+ c->num_spans = 0;
+ while (&c->tail != edge) {
+ struct edge *next = edge->next;
+ int xend = I(edge->x.quo);
+
+ if (--edge->height_left) {
+ if (!edge->vertical) {
+ edge->x.quo += edge->dxdy.quo;
+ edge->x.rem += edge->dxdy.rem;
+ if (edge->x.rem >= 0) {
+ ++edge->x.quo;
+ edge->x.rem -= edge->dy;
+ }
+ }
+
+ if (edge->x.quo < prev_x) {
+ struct edge *pos = edge->prev;
+ pos->next = next;
+ next->prev = pos;
+ do {
+ pos = pos->prev;
+ } while (edge->x.quo < pos->x.quo);
+ pos->next->prev = edge;
+ edge->next = pos->next;
+ edge->prev = pos;
+ pos->next = edge;
+ } else
+ prev_x = edge->x.quo;
+ } else {
+ edge->prev->next = next;
+ next->prev = edge->prev;
+ }
+
+ winding += edge->dir;
+ if ((winding & mask) == 0) {
+ if (I(next->x.quo) > xend + 1) {
+ add_span (c, xstart, xend);
+ xstart = INT_MIN;
+ }
+ } else if (xstart == INT_MIN)
+ xstart = xend;
+
+ edge = next;
+ }
+}
+
+inline static void dec (struct edge *e, int h)
+{
+ e->height_left -= h;
+ if (e->height_left == 0) {
+ e->prev->next = e->next;
+ e->next->prev = e->prev;
+ }
+}
+
+static cairo_status_t
+_mono_scan_converter_init(struct mono_scan_converter *c,
+ int xmin, int ymin,
+ int xmax, int ymax)
+{
+ cairo_status_t status;
+ int max_num_spans;
+
+ status = polygon_init (c->polygon, ymin, ymax);
+ if (unlikely (status))
+ return status;
+
+ max_num_spans = xmax - xmin + 1;
+ if (max_num_spans > ARRAY_LENGTH(c->spans_embedded)) {
+ c->spans = _cairo_malloc_ab (max_num_spans,
+ sizeof (cairo_half_open_span_t));
+ if (unlikely (c->spans == NULL)) {
+ polygon_fini (c->polygon);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ } else
+ c->spans = c->spans_embedded;
+
+ c->xmin = xmin;
+ c->xmax = xmax;
+ c->ymin = ymin;
+ c->ymax = ymax;
+
+ c->head.vertical = 1;
+ c->head.height_left = INT_MAX;
+ c->head.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MIN));
+ c->head.prev = NULL;
+ c->head.next = &c->tail;
+ c->tail.prev = &c->head;
+ c->tail.next = NULL;
+ c->tail.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MAX));
+ c->tail.height_left = INT_MAX;
+ c->tail.vertical = 1;
+
+ c->is_vertical = 1;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_mono_scan_converter_fini(struct mono_scan_converter *self)
+{
+ if (self->spans != self->spans_embedded)
+ free (self->spans);
+
+ polygon_fini(self->polygon);
+}
+
+static cairo_status_t
+mono_scan_converter_allocate_edges(struct mono_scan_converter *c,
+ int num_edges)
+
+{
+ c->polygon->num_edges = 0;
+ c->polygon->edges = c->polygon->edges_embedded;
+ if (num_edges > ARRAY_LENGTH (c->polygon->edges_embedded)) {
+ c->polygon->edges = _cairo_malloc_ab (num_edges, sizeof (struct edge));
+ if (unlikely (c->polygon->edges == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+mono_scan_converter_add_edge (struct mono_scan_converter *c,
+ const cairo_edge_t *edge)
+{
+ polygon_add_edge (c->polygon, edge);
+}
+
+static void
+step_edges (struct mono_scan_converter *c, int count)
+{
+ struct edge *edge;
+
+ for (edge = c->head.next; edge != &c->tail; edge = edge->next) {
+ edge->height_left -= count;
+ if (! edge->height_left) {
+ edge->prev->next = edge->next;
+ edge->next->prev = edge->prev;
+ }
+ }
+}
+
+static cairo_status_t
+mono_scan_converter_render(struct mono_scan_converter *c,
+ unsigned int winding_mask,
+ cairo_span_renderer_t *renderer)
+{
+ struct polygon *polygon = c->polygon;
+ int i, j, h = c->ymax - c->ymin;
+ cairo_status_t status;
+
+ for (i = 0; i < h; i = j) {
+ j = i + 1;
+
+ if (polygon->y_buckets[i])
+ active_list_merge_edges (c, polygon->y_buckets[i]);
+
+ if (c->is_vertical) {
+ int min_height;
+ struct edge *e;
+
+ e = c->head.next;
+ min_height = e->height_left;
+ while (e != &c->tail) {
+ if (e->height_left < min_height)
+ min_height = e->height_left;
+ e = e->next;
+ }
+
+ while (--min_height >= 1 && polygon->y_buckets[j] == NULL)
+ j++;
+ if (j != i + 1)
+ step_edges (c, j - (i + 1));
+ }
+
+ row (c, winding_mask);
+ if (c->num_spans) {
+ status = renderer->render_rows (renderer, c->ymin+i, j-i,
+ c->spans, c->num_spans);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* XXX recompute after dropping edges? */
+ if (c->head.next == &c->tail)
+ c->is_vertical = 1;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct _cairo_mono_scan_converter {
+ cairo_scan_converter_t base;
+
+ struct mono_scan_converter converter[1];
+ cairo_fill_rule_t fill_rule;
+};
+
+typedef struct _cairo_mono_scan_converter cairo_mono_scan_converter_t;
+
+static void
+_cairo_mono_scan_converter_destroy (void *converter)
+{
+ cairo_mono_scan_converter_t *self = converter;
+ _mono_scan_converter_fini (self->converter);
+ free(self);
+}
+
+cairo_status_t
+_cairo_mono_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon)
+{
+ cairo_mono_scan_converter_t *self = converter;
+ cairo_status_t status;
+ int i;
+
+#if 0
+ FILE *file = fopen ("polygon.txt", "w");
+ _cairo_debug_print_polygon (file, polygon);
+ fclose (file);
+#endif
+
+ status = mono_scan_converter_allocate_edges (self->converter,
+ polygon->num_edges);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < polygon->num_edges; i++)
+ mono_scan_converter_add_edge (self->converter, &polygon->edges[i]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_mono_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_mono_scan_converter_t *self = converter;
+
+ return mono_scan_converter_render (self->converter,
+ self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
+ renderer);
+}
+
+cairo_scan_converter_t *
+_cairo_mono_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_mono_scan_converter_t *self;
+ cairo_status_t status;
+
+ self = malloc (sizeof(struct _cairo_mono_scan_converter));
+ if (unlikely (self == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail_nomem;
+ }
+
+ self->base.destroy = _cairo_mono_scan_converter_destroy;
+ self->base.generate = _cairo_mono_scan_converter_generate;
+
+ status = _mono_scan_converter_init (self->converter,
+ xmin, ymin, xmax, ymax);
+ if (unlikely (status))
+ goto bail;
+
+ self->fill_rule = fill_rule;
+
+ return &self->base;
+
+ bail:
+ self->base.destroy(&self->base);
+ bail_nomem:
+ return _cairo_scan_converter_create_in_error (status);
+}
diff --git a/src/cairo-mutex-impl-private.h b/src/cairo-mutex-impl-private.h
new file mode 100755
index 000000000..25223f3ea
--- /dev/null
+++ b/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/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
new file mode 100755
index 000000000..d79a059ea
--- /dev/null
+++ b/src/cairo-mutex-list-private.h
@@ -0,0 +1,83 @@
+/* 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)
+#define CAIRO_RECURSIVE_MUTEX_DECLARE(mutex)
+#endif
+
+CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock)
+
+CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex)
+
+CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_glyph_cache_mutex)
+
+#if CAIRO_HAS_TG_SURFACE
+CAIRO_RECURSIVE_MUTEX_DECLARE(_cairo_tg_scaled_glyph_mutex)
+#endif
+
+#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/src/cairo-mutex-private.h b/src/cairo-mutex-private.h
new file mode 100755
index 000000000..0b806c186
--- /dev/null
+++ b/src/cairo-mutex-private.h
@@ -0,0 +1,69 @@
+/* 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;
+#define CAIRO_RECURSIVE_MUTEX_DECLARE(mutex) cairo_private extern cairo_recursive_mutex_t mutex;
+#include "cairo-mutex-list-private.h"
+#undef CAIRO_MUTEX_DECLARE
+#undef CAIRO_RECURSIVE_MUTEX_DECLARE
+
+CAIRO_END_DECLS
+
+#endif
diff --git a/src/cairo-mutex-type-private.h b/src/cairo-mutex-type-private.h
new file mode 100755
index 000000000..e8c493985
--- /dev/null
+++ b/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/src/cairo-mutex.c b/src/cairo-mutex.c
new file mode 100755
index 000000000..41d5c73f4
--- /dev/null
+++ b/src/cairo-mutex.c
@@ -0,0 +1,89 @@
+/* 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;
+#define CAIRO_RECURSIVE_MUTEX_DECLARE(mutex) \
+ cairo_recursive_mutex_t mutex = CAIRO_RECURSIVE_MUTEX_NIL_INITIALIZER;
+#include "cairo-mutex-list-private.h"
+#undef CAIRO_MUTEX_DECLARE
+#undef CAIRO_RECURSIVE_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);
+#define CAIRO_RECURSIVE_MUTEX_DECLARE(mutex) CAIRO_RECURSIVE_MUTEX_INIT (mutex);
+#include "cairo-mutex-list-private.h"
+#undef CAIRO_MUTEX_DECLARE
+#undef CAIRO_RECURSIVE_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);
+#define CAIRO_RECURSIVE_MUTEX_DECLARE(mutex) CAIRO_MUTEX_FINI (mutex);
+#include "cairo-mutex-list-private.h"
+#undef CAIRO_MUTEX_DECLARE
+#undef CAIRO_RECURSIVE_MUTEX_DECLARE
+}
+#endif
diff --git a/src/cairo-no-compositor.c b/src/cairo-no-compositor.c
new file mode 100755
index 000000000..1602a12f6
--- /dev/null
+++ b/src/cairo-no-compositor.c
@@ -0,0 +1,107 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+
+static cairo_int_status_t
+_cairo_no_compositor_paint (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+const cairo_compositor_t __cairo_no_compositor = {
+ NULL,
+ _cairo_no_compositor_paint,
+ _cairo_no_compositor_mask,
+ _cairo_no_compositor_stroke,
+ _cairo_no_compositor_fill,
+ _cairo_no_compositor_glyphs,
+};
diff --git a/src/cairo-observer.c b/src/cairo-observer.c
new file mode 100755
index 000000000..36d6b93bd
--- /dev/null
+++ b/src/cairo-observer.c
@@ -0,0 +1,52 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-list-inline.h"
+
+void
+_cairo_observers_notify (cairo_list_t *observers, void *arg)
+{
+ cairo_observer_t *obs, *next;
+
+ cairo_list_foreach_entry_safe (obs, next,
+ cairo_observer_t,
+ observers, link)
+ {
+ obs->callback (obs, arg);
+ }
+}
diff --git a/src/cairo-os2-private.h b/src/cairo-os2-private.h
new file mode 100755
index 000000000..829dd3c8d
--- /dev/null
+++ b/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/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
new file mode 100755
index 000000000..1ab50f977
--- /dev/null
+++ b/src/cairo-os2-surface.c
@@ -0,0 +1,1415 @@
+/* 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-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-image-surface-private.h"
+
+#if CAIRO_HAS_FC_FONT
+#include <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 *surface = (cairo_os2_surface_t *) abstract_surface;
+
+ DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+ /* Increase lend counter */
+ surface->pixel_array_lend_count++;
+
+ *image_out = surface->image_surface;
+ *image_extra = NULL;
+
+ DosReleaseMutexSem (surface->hmtx_use_private_fields);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_os2_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+ /* Decrease Lend counter! */
+ DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+ if (surface->pixel_array_lend_count > 0)
+ surface->pixel_array_lend_count--;
+ DosPostEventSem (surface->hev_pixel_array_came_back);
+
+ DosReleaseMutexSem (surface->hmtx_use_private_fields);
+}
+
+static cairo_image_surface_t *
+_cairo_os2_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+ DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+ /* Increase lend counter */
+ surface->pixel_array_lend_count++;
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+ /* XXX: BROKEN! */
+ *image_out = _cairo_surface_create_for_rectangle_int (surface->image_surface,
+ extents);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_os2_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+ /* So, we got back the image, and if all goes well, then
+ * something has been changed inside the interest_rect.
+ * So, we blit it to the screen!
+ */
+ if (surface->blit_as_changes) {
+ RECTL rclToBlit;
+
+ /* Get mutex, we'll work with the pixel array! */
+ if (DosRequestMutexSem (surface->hmtx_use_private_fields,
+ SEM_INDEFINITE_WAIT) != NO_ERROR)
+ {
+ /* Could not get mutex! */
+ return;
+ }
+
+ rclToBlit.xLeft = image->base.device_transform_inverse.x0;
+ rclToBlit.xRight = rclToBlit.xLeft + image->width; /* Noninclusive */
+ rclToBlit.yTop = image->base.device_transform_inverse.y0;
+ rclToBlit.yBottom = rclToBlit.yTop + image->height; /* Noninclusive */
+
+ if (surface->hwnd_client_window) {
+ /* We know the HWND, so let's invalidate the window region,
+ * so the application will redraw itself, using the
+ * cairo_os2_surface_refresh_window () API from its own PM thread.
+ *
+ * This is the safe method, which should be preferred every time.
+ */
+ rclToBlit.yTop = surface->bitmap_info.cy - rclToBlit.yTop;
+ rclToBlit.yBottom = surface->bitmap_info.cy - rclToBlit.yTop;
+ WinInvalidateRect (surface->hwnd_client_window,
+ &rclToBlit,
+ FALSE);
+ } else {
+ /* We don't know the HWND, so try to blit the pixels from here!
+ * Please note that it can be problematic if this is not the PM thread!
+ *
+ * It can cause internal PM stuffs to be screwed up, for some reason.
+ * Please always tell the HWND to the surface using the
+ * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
+ * from your WM_PAINT, if it's possible!
+ */
+ _cairo_os2_surface_blit_pixels (surface,
+ surface->hps_client_window,
+ &rclToBlit);
+ }
+
+ DosReleaseMutexSem (surface->hmtx_use_private_fields);
+ }
+ /* Also decrease Lend counter! */
+ DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+ if (surface->pixel_array_lend_count > 0)
+ surface->pixel_array_lend_count--;
+ DosPostEventSem (surface->hev_pixel_array_came_back);
+
+ DosReleaseMutexSem (surface->hmtx_use_private_fields);
+}
+
+static cairo_bool_t
+_cairo_os2_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->bitmap_info.cx;
+ rectangle->height = surface->bitmap_info.cy;
+
+ return TRUE;
+}
+
+/**
+ * cairo_os2_surface_create:
+ * @hps_client_window: the presentation handle to bind the surface to
+ * @width: the width of the surface
+ * @height: the height of the surface
+ *
+ * Create a Cairo surface which is bound to a given presentation space (HPS).
+ * The caller retains ownership of the HPS and must dispose of it after the
+ * the surface has been destroyed. The surface will be created to have the
+ * given size. By default every change to the surface will be made visible
+ * immediately by blitting it into the window. This can be changed with
+ * cairo_os2_surface_set_manual_window_refresh().
+ * Note that the surface will contain garbage when created, so the pixels
+ * have to be initialized by hand first. You can use the Cairo functions to
+ * fill it with black, or use cairo_surface_mark_dirty() to fill the surface
+ * with pixels from the window/HPS.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.4
+ **/
+cairo_surface_t *
+cairo_os2_surface_create (HPS hps_client_window,
+ int width,
+ int height)
+{
+ cairo_os2_surface_t *local_os2_surface = 0;
+ cairo_status_t status;
+ int rc;
+
+ /* Check the size of the window */
+ if ((width <= 0) || (height <= 0)) {
+ status = _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+ goto error_exit;
+ }
+
+ /* Allocate an OS/2 surface structure. */
+ local_os2_surface = (cairo_os2_surface_t *) 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 occurred */
+
+ if (local_os2_surface) {
+ if (local_os2_surface->pixels)
+ _buffer_free (local_os2_surface->pixels);
+ if (local_os2_surface->hev_pixel_array_came_back)
+ DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
+ if (local_os2_surface->hmtx_use_private_fields)
+ DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
+ free (local_os2_surface);
+ }
+
+ return _cairo_surface_create_in_error (status);
+}
+
+/**
+ * cairo_os2_surface_create_for_window:
+ * @hwnd_client_window: the window handle to bind the surface to
+ * @width: the width of the surface
+ * @height: the height of the surface
+ *
+ * Create a Cairo surface which is bound to a given window; the caller retains
+ * ownership of the window. This is a convenience function for use with
+ * windows that will only be updated when cairo_os2_surface_refresh_window()
+ * is called (usually in response to a WM_PAINT message). It avoids the need
+ * to create a persistent HPS for every window and assumes that one will be
+ * supplied by the caller when a cairo function needs one. If it isn't, an
+ * HPS will be created on-the-fly and released before the function which needs
+ * it returns.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_os2_surface_create_for_window (HWND hwnd_client_window,
+ int width,
+ int height)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ /* A window handle must be provided. */
+ if (!hwnd_client_window) {
+ return _cairo_surface_create_in_error (
+ _cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ /* Create the surface. */
+ local_os2_surface = (cairo_os2_surface_t *)
+ cairo_os2_surface_create (0, width, height);
+
+ /* If successful, save the hwnd & turn off automatic repainting. */
+ if (!local_os2_surface->image_surface->base.status) {
+ local_os2_surface->hwnd_client_window = hwnd_client_window;
+ local_os2_surface->blit_as_changes = FALSE;
+ }
+
+ return (cairo_surface_t *)local_os2_surface;
+}
+
+/**
+ * cairo_os2_surface_set_size:
+ * @surface: the cairo surface to resize
+ * @new_width: the new width of the surface
+ * @new_height: the new height of the surface
+ * @timeout: timeout value in milliseconds
+ *
+ * When the client window is resized, call this API to set the new size in the
+ * underlying surface accordingly. This function will reallocate everything,
+ * so you'll have to redraw everything in the surface after this call.
+ * The surface will contain garbage after the resizing. So the notes of
+ * cairo_os2_surface_create() apply here, too.
+ *
+ * The timeout value specifies how long the function should wait on other parts
+ * of the program to release the buffers. It is necessary, because it can happen
+ * that Cairo is just drawing something into the surface while we want to
+ * destroy and recreate it.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
+ * %CAIRO_STATUS_INVALID_SIZE for invalid sizes
+ * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
+ * timeout happened before all the buffers were released
+ *
+ * Since: 1.4
+ **/
+int
+cairo_os2_surface_set_size (cairo_surface_t *surface,
+ int new_width,
+ int new_height,
+ int timeout)
+{
+ cairo_os2_surface_t *local_os2_surface;
+ unsigned char *pchNewPixels;
+ int rc;
+ cairo_image_surface_t *pNewImageSurface;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ }
+
+ if ((new_width <= 0) ||
+ (new_height <= 0))
+ {
+ /* Invalid size! */
+ return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+ }
+
+ /* Allocate memory for new stuffs */
+ pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4);
+ if (!pchNewPixels) {
+ /* Not enough memory for the pixels!
+ * Everything remains the same!
+ */
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* Create image surface from new pixel array */
+ pNewImageSurface = (cairo_image_surface_t *)
+ cairo_image_surface_create_for_data (pchNewPixels,
+ CAIRO_FORMAT_ARGB32,
+ new_width, /* Width */
+ new_height, /* Height */
+ new_width * 4); /* Rowstride */
+
+ if (pNewImageSurface->base.status) {
+ /* Could not create image surface!
+ * Everything remains the same!
+ */
+ _buffer_free (pchNewPixels);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* Okay, new memory allocated, so it's time to swap old buffers
+ * to new ones!
+ */
+ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
+ /* Could not get mutex!
+ * Everything remains the same!
+ */
+ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
+ _buffer_free (pchNewPixels);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* We have to make sure that we won't destroy a surface which
+ * is lent to some other code (Cairo is drawing into it)!
+ */
+ while (local_os2_surface->pixel_array_lend_count > 0) {
+ ULONG ulPostCount;
+ DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount);
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+ /* Wait for somebody to return the pixels! */
+ rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout);
+ if (rc != NO_ERROR) {
+ /* Either timeout or something wrong... Exit. */
+ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
+ _buffer_free (pchNewPixels);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ /* Okay, grab mutex and check counter again! */
+ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+ != NO_ERROR)
+ {
+ /* Could not get mutex!
+ * Everything remains the same!
+ */
+ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
+ _buffer_free (pchNewPixels);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
+ /* Destroy old image surface */
+ cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
+ /* Destroy old pixel buffer */
+ _buffer_free (local_os2_surface->pixels);
+ /* Set new image surface */
+ local_os2_surface->image_surface = pNewImageSurface;
+ /* Set new pixel buffer */
+ local_os2_surface->pixels = pchNewPixels;
+ /* Change bitmap2 structure */
+ local_os2_surface->bitmap_info.cx = new_width;
+ local_os2_surface->bitmap_info.cy = new_height;
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_os2_surface_refresh_window:
+ * @surface: the cairo surface to refresh
+ * @hps_begin_paint: the presentation handle of the window to refresh
+ * @prcl_begin_paint_rect: the rectangle to redraw
+ *
+ * This function can be used to force a repaint of a given area of the client
+ * window. It should usually be called from the WM_PAINT processing of the
+ * window procedure. However, it can be called any time a given part of the
+ * window has to be updated.
+ *
+ * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call
+ * of the window procedure, but you can also get the HPS using WinGetPS, and you
+ * can assemble your own update rectangle by hand.
+ * If hps_begin_paint is %NULL, the function will use the HPS passed into
+ * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function
+ * will query the current window size and repaint the whole window.
+ *
+ * Cairo assumes that if you set the HWND to the surface using
+ * cairo_os2_surface_set_hwnd(), this function will be called by the application
+ * every time it gets a WM_PAINT for that HWND. If the HWND is set in the
+ * surface, Cairo uses this function to handle dirty areas too.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_os2_surface_refresh_window (cairo_surface_t *surface,
+ HPS hps_begin_paint,
+ PRECTL prcl_begin_paint_rect)
+{
+ cairo_os2_surface_t *local_os2_surface;
+ RECTL rclTemp;
+ HPS hpsTemp = 0;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return;
+ }
+
+ /* If an HPS wasn't provided, see if we can get one. */
+ if (!hps_begin_paint) {
+ hps_begin_paint = local_os2_surface->hps_client_window;
+ if (!hps_begin_paint) {
+ if (local_os2_surface->hwnd_client_window) {
+ hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window);
+ hps_begin_paint = hpsTemp;
+ }
+ /* No HPS & no way to get one, so exit */
+ if (!hps_begin_paint)
+ return;
+ }
+ }
+
+ if (prcl_begin_paint_rect == NULL) {
+ /* Update the whole window! */
+ rclTemp.xLeft = 0;
+ rclTemp.xRight = local_os2_surface->bitmap_info.cx;
+ rclTemp.yTop = local_os2_surface->bitmap_info.cy;
+ rclTemp.yBottom = 0;
+ } else {
+ /* Use the rectangle we got passed as parameter! */
+ rclTemp.xLeft = prcl_begin_paint_rect->xLeft;
+ rclTemp.xRight = prcl_begin_paint_rect->xRight;
+ rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
+ rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ;
+ }
+
+ /* Get mutex, we'll work with the pixel array! */
+ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+ != NO_ERROR)
+ {
+ /* Could not get mutex! */
+ if (hpsTemp)
+ WinReleasePS(hpsTemp);
+ return;
+ }
+
+ if ((local_os2_surface->dirty_area_present) &&
+ (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) &&
+ (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) &&
+ (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) &&
+ (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom))
+ {
+ /* Aha, this call was because of a dirty area, so in this case we
+ * have to blit the pixels from the screen to the surface!
+ */
+ local_os2_surface->dirty_area_present = FALSE;
+ _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
+ hps_begin_paint,
+ &rclTemp);
+ } else {
+ /* Okay, we have the surface, have the HPS
+ * (might be from WinBeginPaint () or from WinGetPS () )
+ * Now blit there the stuffs!
+ */
+ _cairo_os2_surface_blit_pixels (local_os2_surface,
+ hps_begin_paint,
+ &rclTemp);
+ }
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+ if (hpsTemp)
+ WinReleasePS(hpsTemp);
+}
+
+static cairo_status_t
+_cairo_os2_surface_finish (void *abstract_surface)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ }
+
+ DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+ /* Destroy old image surface */
+ cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
+ /* Destroy old pixel buffer */
+ _buffer_free (local_os2_surface->pixels);
+ DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
+ DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
+
+ /* The memory itself will be free'd by the cairo_surface_destroy ()
+ * who called us.
+ */
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_os2_surface_set_hwnd:
+ * @surface: the cairo surface to associate with the window handle
+ * @hwnd_client_window: the window handle of the client window
+ *
+ * Sets window handle for surface; the caller retains ownership of the window.
+ * If Cairo wants to blit into the window because it is set to blit as the
+ * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then
+ * there are two ways it can choose:
+ * If it knows the HWND of the surface, then it invalidates that area, so the
+ * application will get a WM_PAINT message and it can call
+ * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself
+ * will use the HPS it got at surface creation time, and blit the pixels itself.
+ * It's also a solution, but experience shows that if this happens from a non-PM
+ * thread, then it can screw up PM internals.
+ *
+ * So, best solution is to set the HWND for the surface after the surface
+ * creation, so every blit will be done from application's message processing
+ * loop, which is the safest way to do.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
+ HWND hwnd_client_window)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return;
+ }
+
+ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+ != NO_ERROR)
+ {
+ /* Could not get mutex! */
+ return;
+ }
+
+ local_os2_surface->hwnd_client_window = hwnd_client_window;
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+}
+
+/**
+ * cairo_os2_surface_set_manual_window_refresh:
+ * @surface: the cairo surface to set the refresh mode for
+ * @manual_refresh: the switch for manual surface refresh
+ *
+ * This API can tell Cairo if it should show every change to this surface
+ * immediately in the window or if it should be cached and will only be visible
+ * once the user calls cairo_os2_surface_refresh_window() explicitly. If the
+ * HWND was not set in the cairo surface, then the HPS will be used to blit the
+ * graphics. Otherwise it will invalidate the given window region so the user
+ * will get the WM_PAINT message to redraw that area of the window.
+ *
+ * So, if you're only interested in displaying the final result after several
+ * drawing operations, you might get better performance if you put the surface
+ * into manual refresh mode by passing a true value to this function. Then call
+ * cairo_os2_surface_refresh() whenever desired.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
+ cairo_bool_t manual_refresh)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return;
+ }
+
+ local_os2_surface->blit_as_changes = !manual_refresh;
+}
+
+/**
+ * cairo_os2_surface_get_manual_window_refresh:
+ * @surface: the cairo surface to query the refresh mode from
+ *
+ * This space left intentionally blank.
+ *
+ * Return value: current refresh mode of the surface (true by default)
+ *
+ * Since: 1.4
+ **/
+cairo_bool_t
+cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return FALSE;
+ }
+
+ return !(local_os2_surface->blit_as_changes);
+}
+
+/**
+ * cairo_os2_surface_get_hps:
+ * @surface: the cairo surface to be querued
+ * @hps: HPS currently associated with the surface (if any)
+ *
+ * This API retrieves the HPS associated with the surface.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved,
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
+ * %CAIRO_STATUS_NULL_POINTER if the hps argument is null
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_os2_surface_get_hps (cairo_surface_t *surface,
+ HPS *hps)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ }
+ if (!hps)
+ {
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ }
+ *hps = local_os2_surface->hps_client_window;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_os2_surface_set_hps:
+ * @surface: the cairo surface to associate with the HPS
+ * @hps: new HPS to be associated with the surface (the HPS may be null)
+ *
+ * This API replaces the HPS associated with the surface with a new one.
+ * The caller retains ownership of the HPS and must dispose of it after
+ * the surface has been destroyed or it has been replaced by another
+ * call to this function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced,
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_os2_surface_set_hps (cairo_surface_t *surface,
+ HPS hps)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ }
+ local_os2_surface->hps_client_window = hps;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_os2_surface_mark_dirty_rectangle (void *surface,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ cairo_os2_surface_t *local_os2_surface;
+ RECTL rclToBlit;
+
+ local_os2_surface = (cairo_os2_surface_t *) surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ }
+
+ /* Get mutex, we'll work with the pixel array! */
+ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+ != NO_ERROR)
+ {
+ /* Could not get mutex! */
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+
+ /* Check for defaults */
+ if (width < 0)
+ width = local_os2_surface->bitmap_info.cx;
+ if (height < 0)
+ height = local_os2_surface->bitmap_info.cy;
+
+ if (local_os2_surface->hwnd_client_window) {
+ /* We know the HWND, so let's invalidate the window region,
+ * so the application will redraw itself, using the
+ * cairo_os2_surface_refresh_window () API from its own PM thread.
+ * From that function we'll note that it's not a redraw but a
+ * dirty-rectangle deal stuff, so we'll handle the things from
+ * there.
+ *
+ * This is the safe method, which should be preferred every time.
+ */
+ rclToBlit.xLeft = x;
+ rclToBlit.xRight = x + width; /* Noninclusive */
+ rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y);
+ rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */
+
+#if 0
+ if (local_os2_surface->dirty_area_present) {
+ /* Yikes, there is already a dirty area which should be
+ * cleaned up, but we'll overwrite it. Sorry.
+ * TODO: Something clever should be done here.
+ */
+ }
+#endif
+
+ /* Set up dirty area reminder stuff */
+ memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL));
+ local_os2_surface->dirty_area_present = TRUE;
+
+ /* Invalidate window area */
+ WinInvalidateRect (local_os2_surface->hwnd_client_window,
+ &rclToBlit,
+ FALSE);
+ } else {
+ /* We don't know the HWND, so try to blit the pixels from here!
+ * Please note that it can be problematic if this is not the PM thread!
+ *
+ * It can cause internal PM stuffs to be scewed up, for some reason.
+ * Please always tell the HWND to the surface using the
+ * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
+ * from your WM_PAINT, if it's possible!
+ */
+
+ rclToBlit.xLeft = x;
+ rclToBlit.xRight = x + width; /* Noninclusive */
+ rclToBlit.yBottom = y;
+ rclToBlit.yTop = y + height; /* Noninclusive */
+ /* Now get the pixels from the screen! */
+ _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
+ local_os2_surface->hps_client_window,
+ &rclToBlit);
+ }
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_os2_surface_backend = {
+ CAIRO_SURFACE_TYPE_OS2,
+ _cairo_os2_surface_finish,
+ _cairo_default_context_create,
+
+ NULL, /* create_similar */
+ NULL, /* create_similar_image */
+ _cairo_os2_surface_map_to_image,
+ _cairo_os2_surface_unmap_image,
+
+ _cairo_surface_default_source,
+ _cairo_os2_surface_acquire_source_image,
+ _cairo_os2_surface_release_source_image,
+ NULL, /* snapshot */
+
+ _cairo_os2_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ _cairo_os2_surface_mark_dirty_rectangle,
+
+ _cairo_surface_fallback_paint,
+ _cairo_surface_fallback_mask,
+ _cairo_surface_fallback_fill,
+ _cairo_surface_fallback_stroke,
+ NULL, /* fill/stroke */
+ _cairo_surface_fallback_glyphs,
+};
diff --git a/src/cairo-os2.h b/src/cairo-os2.h
new file mode 100755
index 000000000..d23f2dec4
--- /dev/null
+++ b/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/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h
new file mode 100755
index 000000000..edaabbe78
--- /dev/null
+++ b/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/src/cairo-output-stream.c b/src/cairo-output-stream.c
new file mode 100755
index 000000000..facc182f6
--- /dev/null
+++ b/src/cairo-output-stream.c
@@ -0,0 +1,774 @@
+/* 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-array-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;
+
+ locale_data = localeconv ();
+ decimal_point = locale_data->decimal_point;
+ decimal_point_len = strlen (decimal_point);
+
+ assert (decimal_point_len != 0);
+
+ if (limited_precision) {
+ snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d);
+ } else {
+ /* Using "%f" to print numbers less than 0.1 will result in
+ * reduced precision due to the default 6 digits after the
+ * decimal point.
+ *
+ * For numbers is < 0.1, we print with maximum precision and count
+ * the number of zeros between the decimal point and the first
+ * significant digit. We then print the number again with the
+ * number of decimal places that gives us the required number of
+ * significant digits. This ensures the number is correctly
+ * rounded.
+ */
+ if (fabs (d) >= 0.1) {
+ snprintf (buffer, size, "%f", d);
+ } else {
+ snprintf (buffer, size, "%.18f", d);
+ p = buffer;
+
+ if (*p == '+' || *p == '-')
+ p++;
+
+ while (_cairo_isdigit (*p))
+ p++;
+
+ if (strncmp (p, decimal_point, decimal_point_len) == 0)
+ p += decimal_point_len;
+
+ num_zeros = 0;
+ while (*p++ == '0')
+ num_zeros++;
+
+ decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL;
+
+ if (decimal_digits < 18)
+ snprintf (buffer, size, "%.*f", decimal_digits, d);
+ }
+ }
+ p = buffer;
+
+ if (*p == '+' || *p == '-')
+ p++;
+
+ while (_cairo_isdigit (*p))
+ p++;
+
+ if (strncmp (p, decimal_point, decimal_point_len) == 0) {
+ *p = '.';
+ decimal_len = strlen (p + decimal_point_len);
+ memmove (p + 1, p + decimal_point_len, decimal_len);
+ p[1 + decimal_len] = 0;
+
+ /* Remove trailing zeros and decimal point if possible. */
+ for (p = p + decimal_len; *p == '0'; p--)
+ *p = 0;
+
+ if (*p == '.') {
+ *p = 0;
+ p--;
+ }
+ }
+}
+
+enum {
+ LENGTH_MODIFIER_LONG = 0x100
+};
+
+/* Here's a limited reimplementation of printf. The reason for doing
+ * this is primarily to special case handling of doubles. We want
+ * locale independent formatting of doubles and we want to trim
+ * trailing zeros. This is handled by dtostr() above, and the code
+ * below handles everything else by calling snprintf() to do the
+ * formatting. This functionality is only for internal use and we
+ * only implement the formats we actually use.
+ */
+void
+_cairo_output_stream_vprintf (cairo_output_stream_t *stream,
+ const char *fmt, va_list ap)
+{
+#define SINGLE_FMT_BUFFER_SIZE 32
+ char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE];
+ int single_fmt_length;
+ char *p;
+ const char *f, *start;
+ int length_modifier, width;
+ cairo_bool_t var_width;
+
+ if (stream->status)
+ return;
+
+ f = fmt;
+ p = buffer;
+ while (*f != '\0') {
+ if (p == buffer + sizeof (buffer)) {
+ _cairo_output_stream_write (stream, buffer, sizeof (buffer));
+ p = buffer;
+ }
+
+ if (*f != '%') {
+ *p++ = *f++;
+ continue;
+ }
+
+ start = f;
+ f++;
+
+ if (*f == '0')
+ f++;
+
+ var_width = FALSE;
+ if (*f == '*') {
+ var_width = TRUE;
+ f++;
+ }
+
+ while (_cairo_isdigit (*f))
+ f++;
+
+ length_modifier = 0;
+ if (*f == 'l') {
+ length_modifier = LENGTH_MODIFIER_LONG;
+ f++;
+ }
+
+ /* The only format strings exist in the cairo implementation
+ * itself. So there's an internal consistency problem if any
+ * of them is larger than our format buffer size. */
+ single_fmt_length = f - start + 1;
+ assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE);
+
+ /* Reuse the format string for this conversion. */
+ memcpy (single_fmt, start, single_fmt_length);
+ single_fmt[single_fmt_length] = '\0';
+
+ /* Flush contents of buffer before snprintf()'ing into it. */
+ _cairo_output_stream_write (stream, buffer, p - buffer);
+
+ /* We group signed and unsigned together in this switch, the
+ * only thing that matters here is the size of the arguments,
+ * since we're just passing the data through to sprintf(). */
+ switch (*f | length_modifier) {
+ case '%':
+ buffer[0] = *f;
+ buffer[1] = 0;
+ break;
+ case 'd':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ if (var_width) {
+ width = va_arg (ap, int);
+ snprintf (buffer, sizeof buffer,
+ single_fmt, width, va_arg (ap, int));
+ } else {
+ snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int));
+ }
+ break;
+ case 'd' | LENGTH_MODIFIER_LONG:
+ case 'u' | LENGTH_MODIFIER_LONG:
+ case 'o' | LENGTH_MODIFIER_LONG:
+ case 'x' | LENGTH_MODIFIER_LONG:
+ case 'X' | LENGTH_MODIFIER_LONG:
+ if (var_width) {
+ width = va_arg (ap, int);
+ snprintf (buffer, sizeof buffer,
+ single_fmt, width, va_arg (ap, long int));
+ } else {
+ snprintf (buffer, sizeof buffer,
+ single_fmt, va_arg (ap, long int));
+ }
+ break;
+ case 's':
+ 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;
+ void *data;
+
+ 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);
+ }
+
+ data = _cairo_array_index (&stream->array, 0);
+ if (data == NULL) {
+ free (*data_out);
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ }
+
+ memcpy (*data_out, data, *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/src/cairo-paginated-private.h b/src/cairo-paginated-private.h
new file mode 100755
index 000000000..b827faba0
--- /dev/null
+++ b/src/cairo-paginated-private.h
@@ -0,0 +1,168 @@
+/* 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_surface_t *
+_cairo_paginated_surface_get_recording (cairo_surface_t *surface);
+
+cairo_private cairo_bool_t
+_cairo_surface_is_paginated (cairo_surface_t *surface);
+
+cairo_private cairo_status_t
+_cairo_paginated_surface_set_size (cairo_surface_t *surface,
+ int width,
+ int height);
+
+#endif /* CAIRO_PAGINATED_H */
diff --git a/src/cairo-paginated-surface-private.h b/src/cairo-paginated-surface-private.h
new file mode 100755
index 000000000..ebf4b3424
--- /dev/null
+++ b/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/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
new file mode 100755
index 000000000..b4580d77b
--- /dev/null
+++ b/src/cairo-paginated-surface.c
@@ -0,0 +1,719 @@
+/* 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"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-subsurface-inline.h"
+
+static const cairo_surface_backend_t cairo_paginated_surface_backend;
+
+static cairo_int_status_t
+_cairo_paginated_surface_show_page (void *abstract_surface);
+
+static cairo_surface_t *
+_cairo_paginated_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_rectangle_t rect;
+ rect.x = rect.y = 0.;
+ rect.width = width;
+ rect.height = height;
+ return cairo_recording_surface_create (content, &rect);
+}
+
+static cairo_surface_t *
+_create_recording_surface_for_target (cairo_surface_t *target,
+ cairo_content_t content)
+{
+ cairo_rectangle_int_t rect;
+
+ if (_cairo_surface_get_extents (target, &rect)) {
+ cairo_rectangle_t recording_extents;
+
+ recording_extents.x = rect.x;
+ recording_extents.y = rect.y;
+ recording_extents.width = rect.width;
+ recording_extents.height = rect.height;
+
+ return cairo_recording_surface_create (content, &recording_extents);
+ } else {
+ return cairo_recording_surface_create (content, NULL);
+ }
+}
+
+cairo_surface_t *
+_cairo_paginated_surface_create (cairo_surface_t *target,
+ cairo_content_t content,
+ const cairo_paginated_surface_backend_t *backend)
+{
+ cairo_paginated_surface_t *surface;
+ cairo_status_t status;
+
+ surface = 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);
+ cairo_surface_destroy (surface->recording_surface);
+ free (surface);
+ FAIL:
+ return _cairo_surface_create_in_error (status);
+}
+
+cairo_bool_t
+_cairo_surface_is_paginated (cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_paginated_surface_backend;
+}
+
+cairo_surface_t *
+_cairo_paginated_surface_get_target (cairo_surface_t *surface)
+{
+ cairo_paginated_surface_t *paginated_surface;
+
+ assert (_cairo_surface_is_paginated (surface));
+
+ paginated_surface = (cairo_paginated_surface_t *) surface;
+ return paginated_surface->target;
+}
+
+cairo_surface_t *
+_cairo_paginated_surface_get_recording (cairo_surface_t *surface)
+{
+ cairo_paginated_surface_t *paginated_surface;
+
+ assert (_cairo_surface_is_paginated (surface));
+
+ paginated_surface = (cairo_paginated_surface_t *) surface;
+ return paginated_surface->recording_surface;
+}
+
+cairo_status_t
+_cairo_paginated_surface_set_size (cairo_surface_t *surface,
+ int width,
+ int height)
+{
+ cairo_paginated_surface_t *paginated_surface;
+ cairo_status_t status;
+ cairo_rectangle_t recording_extents;
+
+ assert (_cairo_surface_is_paginated (surface));
+
+ paginated_surface = (cairo_paginated_surface_t *) surface;
+
+ recording_extents.x = 0;
+ recording_extents.y = 0;
+ recording_extents.width = width;
+ recording_extents.height = height;
+
+ cairo_surface_destroy (paginated_surface->recording_surface);
+ paginated_surface->recording_surface = cairo_recording_surface_create (paginated_surface->content,
+ &recording_extents);
+ status = paginated_surface->recording_surface->status;
+ if (unlikely (status))
+ return _cairo_surface_set_error (surface, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_paginated_surface_finish (void *abstract_surface)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (! surface->base.is_clear || surface->page_num == 1) {
+ /* Bypass some of the sanity checking in cairo-surface.c, as we
+ * know that the surface is finished...
+ */
+ status = _cairo_paginated_surface_show_page (surface);
+ }
+
+ /* XXX We want to propagate any errors from destroy(), but those are not
+ * returned via the api. So we need to explicitly finish the target,
+ * and check the status afterwards. However, we can only call finish()
+ * on the target, if we own it.
+ */
+ if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->target->ref_count) == 1)
+ cairo_surface_finish (surface->target);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = cairo_surface_status (surface->target);
+ cairo_surface_destroy (surface->target);
+
+ cairo_surface_finish (surface->recording_surface);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = cairo_surface_status (surface->recording_surface);
+ cairo_surface_destroy (surface->recording_surface);
+
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_paginated_surface_create_image_surface (void *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+ cairo_font_options_t options;
+
+ image = _cairo_image_surface_create_with_content (surface->content,
+ width,
+ height);
+
+ cairo_surface_get_font_options (&surface->base, &options);
+ _cairo_surface_set_font_options (image, &options);
+
+ return image;
+}
+
+static cairo_surface_t *
+_cairo_paginated_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+ return _cairo_surface_get_source (surface->target, extents);
+}
+
+static cairo_status_t
+_cairo_paginated_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+ cairo_bool_t is_bounded;
+ cairo_surface_t *image;
+ cairo_status_t status;
+ cairo_rectangle_int_t extents;
+
+ is_bounded = _cairo_surface_get_extents (surface->target, &extents);
+ if (! is_bounded)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ image = _cairo_paginated_surface_create_image_surface (surface,
+ extents.width,
+ extents.height);
+
+ status = _cairo_recording_surface_replay (surface->recording_surface, image);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ *image_out = (cairo_image_surface_t*) image;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_paginated_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_int_status_t
+_paint_fallback_image (cairo_paginated_surface_t *surface,
+ cairo_rectangle_int_t *rect)
+{
+ double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution;
+ double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution;
+ int x, y, width, height;
+ cairo_status_t status;
+ cairo_surface_t *image;
+ cairo_surface_pattern_t pattern;
+ cairo_clip_t *clip;
+
+ x = rect->x;
+ y = rect->y;
+ width = rect->width;
+ height = rect->height;
+ image = _cairo_paginated_surface_create_image_surface (surface,
+ ceil (width * x_scale),
+ ceil (height * y_scale));
+ _cairo_surface_set_device_scale (image, x_scale, y_scale);
+ /* set_device_offset just sets the x0/y0 components of the matrix;
+ * so we have to do the scaling manually. */
+ cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale);
+
+ status = _cairo_recording_surface_replay (surface->recording_surface, image);
+ if (unlikely (status))
+ goto CLEANUP_IMAGE;
+
+ _cairo_pattern_init_for_surface (&pattern, image);
+ cairo_matrix_init (&pattern.base.matrix,
+ x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale);
+ /* the fallback should be rendered at native resolution, so disable
+ * filtering (if possible) to avoid introducing potential artifacts. */
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+ clip = _cairo_clip_intersect_rectangle (NULL, rect);
+ status = _cairo_surface_paint (surface->target,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, clip);
+ _cairo_clip_destroy (clip);
+ _cairo_pattern_fini (&pattern.base);
+
+CLEANUP_IMAGE:
+ cairo_surface_destroy (image);
+
+ return status;
+}
+
+static cairo_int_status_t
+_paint_page (cairo_paginated_surface_t *surface)
+{
+ cairo_surface_t *analysis;
+ cairo_int_status_t status;
+ cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback;
+
+ if (unlikely (surface->target->status))
+ return surface->target->status;
+
+ analysis = _cairo_analysis_surface_create (surface->target);
+ if (unlikely (analysis->status)) {
+ status = analysis->status;
+ goto FAIL;
+ }
+
+ 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)
+ goto FAIL;
+
+ assert (analysis->status == CAIRO_STATUS_SUCCESS);
+
+ if (surface->backend->set_bounding_box) {
+ cairo_box_t bbox;
+
+ _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
+ status = surface->backend->set_bounding_box (surface->target, &bbox);
+ if (unlikely (status))
+ goto FAIL;
+ }
+
+ if (surface->backend->set_fallback_images_required) {
+ cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis);
+
+ status = surface->backend->set_fallback_images_required (surface->target,
+ has_fallbacks);
+ if (unlikely (status))
+ goto FAIL;
+ }
+
+ /* Finer grained fallbacks are currently only supported for some
+ * surface types */
+ if (surface->backend->supports_fine_grained_fallbacks != NULL &&
+ surface->backend->supports_fine_grained_fallbacks (surface->target))
+ {
+ has_supported = _cairo_analysis_surface_has_supported (analysis);
+ has_page_fallback = FALSE;
+ has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis);
+ }
+ else
+ {
+ if (_cairo_analysis_surface_has_unsupported (analysis)) {
+ has_supported = FALSE;
+ has_page_fallback = TRUE;
+ } else {
+ has_supported = TRUE;
+ has_page_fallback = FALSE;
+ }
+ has_finegrained_fallback = FALSE;
+ }
+
+ if (has_supported) {
+ 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,
+ const cairo_clip_t *clip)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_paint (surface->recording_surface, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_mask (surface->recording_surface, op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_stroke (surface->recording_surface, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_fill (surface->recording_surface, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_bool_t
+_cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+
+ return cairo_surface_has_show_text_glyphs (surface->target);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_show_text_glyphs (surface->recording_surface, op, source,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
+}
+
+static const char **
+_cairo_paginated_surface_get_supported_mime_types (void *abstract_surface)
+{
+ cairo_paginated_surface_t *surface = abstract_surface;
+
+ if (surface->target->backend->get_supported_mime_types)
+ return surface->target->backend->get_supported_mime_types (surface->target);
+
+ return NULL;
+}
+
+static cairo_surface_t *
+_cairo_paginated_surface_snapshot (void *abstract_other)
+{
+ cairo_paginated_surface_t *other = abstract_other;
+
+ return other->recording_surface->backend->snapshot (other->recording_surface);
+}
+
+static cairo_t *
+_cairo_paginated_context_create (void *target)
+{
+ cairo_paginated_surface_t *surface = target;
+
+ if (_cairo_surface_is_subsurface (&surface->base))
+ surface = (cairo_paginated_surface_t *)
+ _cairo_surface_subsurface_get_target (&surface->base);
+
+ return surface->recording_surface->backend->create_context (target);
+}
+
+static const cairo_surface_backend_t cairo_paginated_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
+ _cairo_paginated_surface_finish,
+
+ _cairo_paginated_context_create,
+
+ _cairo_paginated_surface_create_similar,
+ NULL, /* create simlar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_paginated_surface_source,
+ _cairo_paginated_surface_acquire_source_image,
+ _cairo_paginated_surface_release_source_image,
+ _cairo_paginated_surface_snapshot,
+
+ _cairo_paginated_surface_copy_page,
+ _cairo_paginated_surface_show_page,
+
+ _cairo_paginated_surface_get_extents,
+ _cairo_paginated_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_paginated_surface_paint,
+ _cairo_paginated_surface_mask,
+ _cairo_paginated_surface_stroke,
+ _cairo_paginated_surface_fill,
+ NULL, /* fill_stroke */
+ NULL, /* show_glyphs */
+ _cairo_paginated_surface_has_show_text_glyphs,
+ _cairo_paginated_surface_show_text_glyphs,
+ _cairo_paginated_surface_get_supported_mime_types,
+};
diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
new file mode 100755
index 000000000..77f23c887
--- /dev/null
+++ b/src/cairo-path-bounds.c
@@ -0,0 +1,295 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-box-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+
+typedef struct _cairo_path_bounder {
+ cairo_point_t current_point;
+ cairo_bool_t has_extents;
+ cairo_box_t extents;
+} cairo_path_bounder_t;
+
+static cairo_status_t
+_cairo_path_bounder_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_path_bounder_t *bounder = closure;
+
+ bounder->current_point = *point;
+
+ if (likely (bounder->has_extents)) {
+ _cairo_box_add_point (&bounder->extents, point);
+ } else {
+ bounder->has_extents = TRUE;
+ _cairo_box_set (&bounder->extents, point, point);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_bounder_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_path_bounder_t *bounder = closure;
+
+ bounder->current_point = *point;
+ _cairo_box_add_point (&bounder->extents, point);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_bounder_curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ cairo_path_bounder_t *bounder = closure;
+
+ _cairo_box_add_curve_to (&bounder->extents,
+ &bounder->current_point,
+ b, c, d);
+ bounder->current_point = *d;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_bounder_close_path (void *closure)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
+ cairo_box_t *extents)
+{
+ cairo_path_bounder_t bounder;
+ cairo_status_t status;
+
+ bounder.has_extents = FALSE;
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_path_bounder_move_to,
+ _cairo_path_bounder_line_to,
+ _cairo_path_bounder_curve_to,
+ _cairo_path_bounder_close_path,
+ &bounder);
+ assert (!status);
+
+ if (bounder.has_extents)
+ *extents = bounder.extents;
+
+ return bounder.has_extents;
+}
+
+void
+_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_int_t *extents)
+{
+ _cairo_path_fixed_approximate_fill_extents (path, extents);
+}
+
+void
+_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_int_t *extents)
+{
+ _cairo_path_fixed_fill_extents (path, CAIRO_FILL_RULE_WINDING, 0, extents);
+}
+
+void
+_cairo_path_fixed_approximate_fill_exact_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_t *extents)
+{
+ _cairo_path_fixed_fill_exact_extents (path, CAIRO_FILL_RULE_WINDING, 0, extents);
+}
+
+void
+_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_rectangle_int_t *extents)
+{
+ if (path->extents.p1.x < path->extents.p2.x &&
+ path->extents.p1.y < path->extents.p2.y) {
+ _cairo_box_round_to_rectangle (&path->extents, extents);
+ } else {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ }
+}
+
+void
+_cairo_path_fixed_fill_exact_extents (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_rectangle_t *extents)
+{
+ if (path->extents.p1.x < path->extents.p2.x &&
+ path->extents.p1.y < path->extents.p2.y) {
+ double x1, y1, x2, y2;
+ _cairo_box_to_doubles (&path->extents, &x1, &y1, &x2, &y2);
+
+ extents->x = x1;
+ extents->y = y1;
+ extents->width = x2 - x1;
+ extents->height = y2 - y1;
+ } else {
+ extents->x = extents->y = 0.0;
+ extents->width = extents->height = 0.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)
+{
+ if (path->has_extents) {
+ cairo_box_t box_extents;
+ double dx, dy;
+
+ _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
+
+ box_extents = path->extents;
+ box_extents.p1.x -= _cairo_fixed_from_double (dx);
+ box_extents.p1.y -= _cairo_fixed_from_double (dy);
+ box_extents.p2.x += _cairo_fixed_from_double (dx);
+ box_extents.p2.y += _cairo_fixed_from_double (dy);
+
+ _cairo_box_round_to_rectangle (&box_extents, extents);
+ } else {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ }
+}
+
+void
+_cairo_path_fixed_approximate_stroke_exact_extents (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ cairo_rectangle_t *extents)
+{
+ if (path->has_extents) {
+ cairo_box_t box_extents;
+ double dx, dy;
+ double x1, y1, x2, y2;
+
+ _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
+
+ box_extents = path->extents;
+ box_extents.p1.x -= _cairo_fixed_from_double (dx);
+ box_extents.p1.y -= _cairo_fixed_from_double (dy);
+ box_extents.p2.x += _cairo_fixed_from_double (dx);
+ box_extents.p2.y += _cairo_fixed_from_double (dy);
+
+ _cairo_box_to_doubles (&box_extents, &x1, &y1, &x2, &y2);
+
+ extents->x = x1;
+ extents->y = y1;
+ extents->width = x2 - x1;
+ extents->height = y2 - y1;
+ } else {
+ extents->x = extents->y = 0.0;
+ extents->width = extents->height = 0.0;
+ }
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ _cairo_polygon_init (&polygon, NULL, 0);
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ _cairo_box_round_to_rectangle (&polygon.extents, extents);
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_exact_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_t *extents)
+{
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+ double x1, x2, y1, y2;
+
+ _cairo_polygon_init (&polygon, NULL, 0);
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ _cairo_box_to_doubles (&polygon.extents, &x1, &y1, &x2, &y2);
+ _cairo_polygon_fini (&polygon);
+
+ extents->x = x1;
+ extents->y = y1;
+ extents->width = x2 - x1;
+ extents->height = y2 - y1;
+
+ return status;
+}
+
+cairo_bool_t
+_cairo_path_fixed_extents (const cairo_path_fixed_t *path,
+ cairo_box_t *box)
+{
+ *box = path->extents;
+ return path->has_extents;
+}
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
new file mode 100755
index 000000000..b38c2a8d3
--- /dev/null
+++ b/src/cairo-path-fill.c
@@ -0,0 +1,342 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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"
+#include "cairo-traps-private.h"
+
+typedef struct cairo_filler {
+ cairo_polygon_t *polygon;
+ double tolerance;
+
+ cairo_box_t limit;
+ cairo_bool_t has_limits;
+
+ cairo_point_t current_point;
+ cairo_point_t last_move_to;
+} cairo_filler_t;
+
+static cairo_status_t
+_cairo_filler_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_filler_t *filler = closure;
+ cairo_status_t status;
+
+ status = _cairo_polygon_add_external_edge (filler->polygon,
+ &filler->current_point,
+ point);
+
+ filler->current_point = *point;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_filler_close (void *closure)
+{
+ cairo_filler_t *filler = closure;
+
+ /* close the subpath */
+ return _cairo_filler_line_to (closure, &filler->last_move_to);
+}
+
+static cairo_status_t
+_cairo_filler_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_filler_t *filler = closure;
+ cairo_status_t status;
+
+ /* close current subpath */
+ status = _cairo_filler_close (closure);
+ if (unlikely (status))
+ return status;
+
+ /* make sure that the closure represents a degenerate path */
+ filler->current_point = *point;
+ filler->last_move_to = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_filler_curve_to (void *closure,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ const cairo_point_t *p3)
+{
+ cairo_filler_t *filler = closure;
+ cairo_spline_t spline;
+
+ if (filler->has_limits) {
+ if (! _cairo_spline_intersects (&filler->current_point, p1, p2, p3,
+ &filler->limit))
+ return _cairo_filler_line_to (filler, p3);
+ }
+
+ if (! _cairo_spline_init (&spline,
+ (cairo_spline_add_point_func_t)_cairo_filler_line_to, filler,
+ &filler->current_point, p1, p2, p3))
+ {
+ return _cairo_filler_line_to (closure, p3);
+ }
+
+ return _cairo_spline_decompose (&spline, filler->tolerance);
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
+ double tolerance,
+ cairo_polygon_t *polygon)
+{
+ cairo_filler_t filler;
+ cairo_status_t status;
+
+ filler.polygon = polygon;
+ filler.tolerance = tolerance;
+
+ filler.has_limits = FALSE;
+ if (polygon->num_limits) {
+ filler.has_limits = TRUE;
+ filler.limit = polygon->limit;
+ }
+
+ /* make sure that the closure represents a degenerate path */
+ filler.current_point.x = 0;
+ filler.current_point.y = 0;
+ filler.last_move_to = filler.current_point;
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_filler_move_to,
+ _cairo_filler_line_to,
+ _cairo_filler_curve_to,
+ _cairo_filler_close,
+ &filler);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_filler_close (&filler);
+}
+
+typedef struct cairo_filler_rectilinear_aligned {
+ cairo_polygon_t *polygon;
+
+ cairo_point_t current_point;
+ cairo_point_t last_move_to;
+} cairo_filler_ra_t;
+
+static cairo_status_t
+_cairo_filler_ra_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_filler_ra_t *filler = closure;
+ cairo_status_t status;
+ cairo_point_t p;
+
+ p.x = _cairo_fixed_round_down (point->x);
+ p.y = _cairo_fixed_round_down (point->y);
+
+ status = _cairo_polygon_add_external_edge (filler->polygon,
+ &filler->current_point,
+ &p);
+
+ filler->current_point = p;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_filler_ra_close (void *closure)
+{
+ cairo_filler_ra_t *filler = closure;
+ return _cairo_filler_ra_line_to (closure, &filler->last_move_to);
+}
+
+static cairo_status_t
+_cairo_filler_ra_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_filler_ra_t *filler = closure;
+ cairo_status_t status;
+ cairo_point_t p;
+
+ /* close current subpath */
+ status = _cairo_filler_ra_close (closure);
+ if (unlikely (status))
+ return status;
+
+ p.x = _cairo_fixed_round_down (point->x);
+ p.y = _cairo_fixed_round_down (point->y);
+
+ /* make sure that the closure represents a degenerate path */
+ filler->current_point = p;
+ filler->last_move_to = p;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
+ cairo_antialias_t antialias,
+ cairo_polygon_t *polygon)
+{
+ cairo_filler_ra_t filler;
+ cairo_status_t status;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE)
+ return _cairo_path_fixed_fill_to_polygon (path, 0., polygon);
+
+ filler.polygon = polygon;
+
+ /* make sure that the closure represents a degenerate path */
+ filler.current_point.x = 0;
+ filler.current_point.y = 0;
+ filler.last_move_to = filler.current_point;
+
+ status = _cairo_path_fixed_interpret_flat (path,
+ _cairo_filler_ra_move_to,
+ _cairo_filler_ra_line_to,
+ _cairo_filler_ra_close,
+ &filler,
+ 0.);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_filler_ra_close (&filler);
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_traps_t *traps)
+{
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ if (_cairo_path_fixed_fill_is_empty (path))
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (unlikely (status || polygon.num_edges == 0))
+ goto CLEANUP;
+
+ status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+ &polygon, fill_rule);
+
+ CLEANUP:
+ _cairo_polygon_fini (&polygon);
+ return status;
+}
+
+static cairo_status_t
+_cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ _cairo_polygon_init (&polygon, boxes->limits, boxes->num_limits);
+ boxes->num_limits = 0;
+
+ /* tolerance will be ignored as the path is rectilinear */
+ status = _cairo_path_fixed_fill_rectilinear_to_polygon (path, antialias, &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status =
+ _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (&polygon,
+ fill_rule,
+ boxes);
+ }
+
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ cairo_path_fixed_iter_t iter;
+ cairo_status_t status;
+ cairo_box_t box;
+
+ if (_cairo_path_fixed_is_box (path, &box))
+ return _cairo_boxes_add (boxes, antialias, &box);
+
+ _cairo_path_fixed_iter_init (&iter, path);
+ while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+ if (box.p1.y == box.p2.y || box.p1.x == box.p2.x)
+ continue;
+
+ if (box.p1.y > box.p2.y) {
+ cairo_fixed_t t;
+
+ t = box.p1.y;
+ box.p1.y = box.p2.y;
+ box.p2.y = t;
+
+ t = box.p1.x;
+ box.p1.x = box.p2.x;
+ box.p2.x = t;
+ }
+
+ status = _cairo_boxes_add (boxes, antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (_cairo_path_fixed_iter_at_end (&iter))
+ return _cairo_bentley_ottmann_tessellate_boxes (boxes, fill_rule, boxes);
+
+ /* path is not rectangular, try extracting clipped rectilinear edges */
+ _cairo_boxes_clear (boxes);
+ return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path,
+ fill_rule,
+ antialias,
+ boxes);
+}
+
diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
new file mode 100755
index 000000000..cf7cd0836
--- /dev/null
+++ b/src/cairo-path-fixed-private.h
@@ -0,0 +1,206 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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)))
+
+#define cairo_path_head(path__) (&(path__)->buf.base)
+#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__))
+
+#define cairo_path_buf_next(pos__) \
+ cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link)
+#define cairo_path_buf_prev(pos__) \
+ cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link)
+
+#define cairo_path_foreach_buf_start(pos__, path__) \
+ pos__ = cairo_path_head (path__); do
+#define cairo_path_foreach_buf_end(pos__, path__) \
+ while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__))
+
+
+typedef struct _cairo_path_buf {
+ cairo_list_t link;
+ unsigned int num_ops;
+ unsigned int size_ops;
+ unsigned int num_points;
+ unsigned int size_points;
+
+ cairo_path_op_t *op;
+ cairo_point_t *points;
+} cairo_path_buf_t;
+
+typedef struct _cairo_path_buf_fixed {
+ cairo_path_buf_t base;
+
+ cairo_path_op_t op[CAIRO_PATH_BUF_SIZE];
+ cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE];
+} cairo_path_buf_fixed_t;
+
+/*
+ NOTES:
+ has_curve_to => !stroke_is_rectilinear
+ fill_is_rectilinear => stroke_is_rectilinear
+ fill_is_empty => fill_is_rectilinear
+ fill_maybe_region => fill_is_rectilinear
+*/
+struct _cairo_path_fixed {
+ cairo_point_t last_move_point;
+ cairo_point_t current_point;
+ unsigned int has_current_point : 1;
+ unsigned int needs_move_to : 1;
+ unsigned int has_extents : 1;
+ unsigned int has_curve_to : 1;
+ unsigned int stroke_is_rectilinear : 1;
+ unsigned int fill_is_rectilinear : 1;
+ unsigned int fill_maybe_region : 1;
+ unsigned int fill_is_empty : 1;
+
+ cairo_box_t extents;
+
+ cairo_path_buf_fixed_t buf;
+};
+
+cairo_private void
+_cairo_path_fixed_translate (cairo_path_fixed_t *path,
+ cairo_fixed_t offx,
+ cairo_fixed_t offy);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_append (cairo_path_fixed_t *path,
+ const cairo_path_fixed_t *other,
+ cairo_fixed_t tx,
+ cairo_fixed_t ty);
+
+cairo_private unsigned long
+_cairo_path_fixed_hash (const cairo_path_fixed_t *path);
+
+cairo_private unsigned long
+_cairo_path_fixed_size (const cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_equal (const cairo_path_fixed_t *a,
+ const cairo_path_fixed_t *b);
+
+typedef struct _cairo_path_fixed_iter {
+ const cairo_path_buf_t *first;
+ const cairo_path_buf_t *buf;
+ unsigned int n_op;
+ unsigned int n_point;
+} cairo_path_fixed_iter_t;
+
+cairo_private void
+_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
+ const cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
+ cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter);
+
+static inline cairo_bool_t
+_cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path)
+{
+ return path->fill_is_empty;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path)
+{
+ if (! path->fill_is_rectilinear)
+ return 0;
+
+ if (! path->has_current_point || path->needs_move_to)
+ return 1;
+
+ /* check whether the implicit close preserves the rectilinear property */
+ return path->current_point.x == path->last_move_point.x ||
+ path->current_point.y == path->last_move_point.y;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path)
+{
+ return path->stroke_is_rectilinear;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
+{
+ if (! path->fill_maybe_region)
+ return 0;
+
+ if (! path->has_current_point || path->needs_move_to)
+ return 1;
+
+ /* check whether the implicit close preserves the rectilinear property
+ * (the integer point property is automatically preserved)
+ */
+ return path->current_point.x == path->last_move_point.x ||
+ path->current_point.y == path->last_move_point.y;
+}
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path,
+ cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path);
+
+#endif /* CAIRO_PATH_FIXED_PRIVATE_H */
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
new file mode 100755
index 000000000..2c198815d
--- /dev/null
+++ b/src/cairo-path-fixed.c
@@ -0,0 +1,1588 @@
+/* -*- 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-box-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+
+static cairo_status_t
+_cairo_path_fixed_add (cairo_path_fixed_t *path,
+ cairo_path_op_t op,
+ const cairo_point_t *points,
+ int num_points);
+
+static void
+_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
+ cairo_path_buf_t *buf);
+
+static cairo_path_buf_t *
+_cairo_path_buf_create (int size_ops, int size_points);
+
+static void
+_cairo_path_buf_destroy (cairo_path_buf_t *buf);
+
+static void
+_cairo_path_buf_add_op (cairo_path_buf_t *buf,
+ cairo_path_op_t op);
+
+static void
+_cairo_path_buf_add_points (cairo_path_buf_t *buf,
+ const cairo_point_t *points,
+ int num_points);
+
+void
+_cairo_path_fixed_init (cairo_path_fixed_t *path)
+{
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
+
+ cairo_list_init (&path->buf.base.link);
+
+ path->buf.base.num_ops = 0;
+ path->buf.base.num_points = 0;
+ path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
+ path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
+ path->buf.base.op = path->buf.op;
+ path->buf.base.points = path->buf.points;
+
+ path->current_point.x = 0;
+ path->current_point.y = 0;
+ path->last_move_point = path->current_point;
+
+ path->has_current_point = FALSE;
+ path->needs_move_to = TRUE;
+ path->has_extents = FALSE;
+ path->has_curve_to = FALSE;
+ path->stroke_is_rectilinear = TRUE;
+ path->fill_is_rectilinear = TRUE;
+ path->fill_maybe_region = TRUE;
+ path->fill_is_empty = TRUE;
+
+ path->extents.p1.x = path->extents.p1.y = 0;
+ path->extents.p2.x = path->extents.p2.y = 0;
+}
+
+cairo_status_t
+_cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
+ const cairo_path_fixed_t *other)
+{
+ cairo_path_buf_t *buf, *other_buf;
+ unsigned int num_points, num_ops;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
+
+ cairo_list_init (&path->buf.base.link);
+
+ path->buf.base.op = path->buf.op;
+ path->buf.base.points = path->buf.points;
+ path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
+ path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
+
+ path->current_point = other->current_point;
+ path->last_move_point = other->last_move_point;
+
+ path->has_current_point = other->has_current_point;
+ path->needs_move_to = other->needs_move_to;
+ path->has_extents = other->has_extents;
+ path->has_curve_to = other->has_curve_to;
+ path->stroke_is_rectilinear = other->stroke_is_rectilinear;
+ path->fill_is_rectilinear = other->fill_is_rectilinear;
+ path->fill_maybe_region = other->fill_maybe_region;
+ path->fill_is_empty = other->fill_is_empty;
+
+ path->extents = other->extents;
+
+ path->buf.base.num_ops = other->buf.base.num_ops;
+ path->buf.base.num_points = other->buf.base.num_points;
+ memcpy (path->buf.op, other->buf.base.op,
+ other->buf.base.num_ops * sizeof (other->buf.op[0]));
+ memcpy (path->buf.points, other->buf.points,
+ other->buf.base.num_points * sizeof (other->buf.points[0]));
+
+ num_points = num_ops = 0;
+ for (other_buf = cairo_path_buf_next (cairo_path_head (other));
+ other_buf != cairo_path_head (other);
+ other_buf = cairo_path_buf_next (other_buf))
+ {
+ num_ops += other_buf->num_ops;
+ num_points += other_buf->num_points;
+ }
+
+ if (num_ops) {
+ buf = _cairo_path_buf_create (num_ops, num_points);
+ if (unlikely (buf == NULL)) {
+ _cairo_path_fixed_fini (path);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (other_buf = cairo_path_buf_next (cairo_path_head (other));
+ other_buf != cairo_path_head (other);
+ other_buf = cairo_path_buf_next (other_buf))
+ {
+ memcpy (buf->op + buf->num_ops, other_buf->op,
+ other_buf->num_ops * sizeof (buf->op[0]));
+ buf->num_ops += other_buf->num_ops;
+
+ memcpy (buf->points + buf->num_points, other_buf->points,
+ other_buf->num_points * sizeof (buf->points[0]));
+ buf->num_points += other_buf->num_points;
+ }
+
+ _cairo_path_fixed_add_buf (path, buf);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+unsigned long
+_cairo_path_fixed_hash (const cairo_path_fixed_t *path)
+{
+ unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+ const cairo_path_buf_t *buf;
+ unsigned int count;
+
+ count = 0;
+ cairo_path_foreach_buf_start (buf, path) {
+ hash = _cairo_hash_bytes (hash, buf->op,
+ buf->num_ops * sizeof (buf->op[0]));
+ count += buf->num_ops;
+ } cairo_path_foreach_buf_end (buf, path);
+ hash = _cairo_hash_bytes (hash, &count, sizeof (count));
+
+ count = 0;
+ cairo_path_foreach_buf_start (buf, path) {
+ hash = _cairo_hash_bytes (hash, buf->points,
+ buf->num_points * sizeof (buf->points[0]));
+ count += buf->num_points;
+ } cairo_path_foreach_buf_end (buf, path);
+ hash = _cairo_hash_bytes (hash, &count, sizeof (count));
+
+ return hash;
+}
+
+unsigned long
+_cairo_path_fixed_size (const cairo_path_fixed_t *path)
+{
+ const cairo_path_buf_t *buf;
+ int num_points, num_ops;
+
+ num_ops = num_points = 0;
+ cairo_path_foreach_buf_start (buf, path) {
+ num_ops += buf->num_ops;
+ num_points += buf->num_points;
+ } cairo_path_foreach_buf_end (buf, path);
+
+ return num_ops * sizeof (buf->op[0]) +
+ num_points * sizeof (buf->points[0]);
+}
+
+cairo_bool_t
+_cairo_path_fixed_equal (const cairo_path_fixed_t *a,
+ const cairo_path_fixed_t *b)
+{
+ const cairo_path_buf_t *buf_a, *buf_b;
+ const cairo_path_op_t *ops_a, *ops_b;
+ const cairo_point_t *points_a, *points_b;
+ int num_points_a, num_ops_a;
+ int num_points_b, num_ops_b;
+
+ if (a == b)
+ return TRUE;
+
+ /* use the flags to quickly differentiate based on contents */
+ if (a->has_curve_to != b->has_curve_to)
+ {
+ return FALSE;
+ }
+
+ if (a->extents.p1.x != b->extents.p1.x ||
+ a->extents.p1.y != b->extents.p1.y ||
+ a->extents.p2.x != b->extents.p2.x ||
+ a->extents.p2.y != b->extents.p2.y)
+ {
+ return FALSE;
+ }
+
+ num_ops_a = num_points_a = 0;
+ cairo_path_foreach_buf_start (buf_a, a) {
+ num_ops_a += buf_a->num_ops;
+ num_points_a += buf_a->num_points;
+ } cairo_path_foreach_buf_end (buf_a, a);
+
+ num_ops_b = num_points_b = 0;
+ cairo_path_foreach_buf_start (buf_b, b) {
+ num_ops_b += buf_b->num_ops;
+ num_points_b += buf_b->num_points;
+ } cairo_path_foreach_buf_end (buf_b, b);
+
+ if (num_ops_a == 0 && num_ops_b == 0)
+ return TRUE;
+
+ if (num_ops_a != num_ops_b || num_points_a != num_points_b)
+ return FALSE;
+
+ buf_a = cairo_path_head (a);
+ num_points_a = buf_a->num_points;
+ num_ops_a = buf_a->num_ops;
+ ops_a = buf_a->op;
+ points_a = buf_a->points;
+
+ buf_b = cairo_path_head (b);
+ num_points_b = buf_b->num_points;
+ num_ops_b = buf_b->num_ops;
+ ops_b = buf_b->op;
+ points_b = buf_b->points;
+
+ while (TRUE) {
+ int num_ops = MIN (num_ops_a, num_ops_b);
+ int num_points = MIN (num_points_a, num_points_b);
+
+ if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t)))
+ return FALSE;
+ if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t)))
+ return FALSE;
+
+ num_ops_a -= num_ops;
+ ops_a += num_ops;
+ num_points_a -= num_points;
+ points_a += num_points;
+ if (num_ops_a == 0 || num_points_a == 0) {
+ if (num_ops_a || num_points_a)
+ return FALSE;
+
+ buf_a = cairo_path_buf_next (buf_a);
+ if (buf_a == cairo_path_head (a))
+ break;
+
+ num_points_a = buf_a->num_points;
+ num_ops_a = buf_a->num_ops;
+ ops_a = buf_a->op;
+ points_a = buf_a->points;
+ }
+
+ num_ops_b -= num_ops;
+ ops_b += num_ops;
+ num_points_b -= num_points;
+ points_b += num_points;
+ if (num_ops_b == 0 || num_points_b == 0) {
+ if (num_ops_b || num_points_b)
+ return FALSE;
+
+ buf_b = cairo_path_buf_next (buf_b);
+ if (buf_b == cairo_path_head (b))
+ break;
+
+ num_points_b = buf_b->num_points;
+ num_ops_b = buf_b->num_ops;
+ ops_b = buf_b->op;
+ points_b = buf_b->points;
+ }
+ }
+
+ return TRUE;
+}
+
+cairo_path_fixed_t *
+_cairo_path_fixed_create (void)
+{
+ cairo_path_fixed_t *path;
+
+ path = 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_fixed_last_op (cairo_path_fixed_t *path)
+{
+ cairo_path_buf_t *buf;
+
+ buf = cairo_path_tail (path);
+ assert (buf->num_ops != 0);
+
+ return buf->op[buf->num_ops - 1];
+}
+
+static inline const cairo_point_t *
+_cairo_path_fixed_penultimate_point (cairo_path_fixed_t *path)
+{
+ cairo_path_buf_t *buf;
+
+ buf = cairo_path_tail (path);
+ if (likely (buf->num_points >= 2)) {
+ return &buf->points[buf->num_points - 2];
+ } else {
+ cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
+
+ assert (prev_buf->num_points >= 2 - buf->num_points);
+ return &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+ }
+}
+
+static void
+_cairo_path_fixed_drop_line_to (cairo_path_fixed_t *path)
+{
+ cairo_path_buf_t *buf;
+
+ assert (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO);
+
+ buf = cairo_path_tail (path);
+ buf->num_points--;
+ buf->num_ops--;
+}
+
+cairo_status_t
+_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y)
+{
+ _cairo_path_fixed_new_sub_path (path);
+
+ path->has_current_point = TRUE;
+ path->current_point.x = x;
+ path->current_point.y = y;
+ path->last_move_point = path->current_point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_fixed_move_to_apply (cairo_path_fixed_t *path)
+{
+ if (likely (! path->needs_move_to))
+ return CAIRO_STATUS_SUCCESS;
+
+ path->needs_move_to = FALSE;
+
+ if (path->has_extents) {
+ _cairo_box_add_point (&path->extents, &path->current_point);
+ } else {
+ _cairo_box_set (&path->extents, &path->current_point, &path->current_point);
+ path->has_extents = TRUE;
+ }
+
+ if (path->fill_maybe_region) {
+ path->fill_maybe_region = _cairo_fixed_is_integer (path->current_point.x) &&
+ _cairo_fixed_is_integer (path->current_point.y);
+ }
+
+ path->last_move_point = path->current_point;
+
+ return _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &path->current_point, 1);
+}
+
+void
+_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
+{
+ if (! path->needs_move_to) {
+ /* If the current subpath doesn't need_move_to, it contains at least one command */
+ if (path->fill_is_rectilinear) {
+ /* Implicitly close for fill */
+ path->fill_is_rectilinear = path->current_point.x == path->last_move_point.x ||
+ path->current_point.y == path->last_move_point.y;
+ path->fill_maybe_region &= path->fill_is_rectilinear;
+ }
+ path->needs_move_to = TRUE;
+ }
+
+ path->has_current_point = FALSE;
+}
+
+cairo_status_t
+_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
+ cairo_fixed_t dx,
+ cairo_fixed_t dy)
+{
+ if (unlikely (! path->has_current_point))
+ return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
+
+ return _cairo_path_fixed_move_to (path,
+ path->current_point.x + dx,
+ path->current_point.y + dy);
+
+}
+
+cairo_status_t
+_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y)
+{
+ cairo_status_t status;
+ cairo_point_t point;
+
+ point.x = x;
+ point.y = y;
+
+ /* When there is not yet a current point, the line_to operation
+ * becomes a move_to instead. Note: We have to do this by
+ * explicitly calling into _cairo_path_fixed_move_to to ensure
+ * that the last_move_point state is updated properly.
+ */
+ if (! path->has_current_point)
+ return _cairo_path_fixed_move_to (path, point.x, point.y);
+
+ status = _cairo_path_fixed_move_to_apply (path);
+ if (unlikely (status))
+ return status;
+
+ /* If the previous op was but the initial MOVE_TO and this segment
+ * is degenerate, then we can simply skip this point. Note that
+ * a move-to followed by a degenerate line-to is a valid path for
+ * stroking, but at all other times is simply a degenerate segment.
+ */
+ if (_cairo_path_fixed_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
+ if (x == path->current_point.x && y == path->current_point.y)
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* If the previous op was also a LINE_TO with the same gradient,
+ * then just change its end-point rather than adding a new op.
+ */
+ if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+ const cairo_point_t *p;
+
+ p = _cairo_path_fixed_penultimate_point (path);
+ if (p->x == path->current_point.x && p->y == path->current_point.y) {
+ /* previous line element was degenerate, replace */
+ _cairo_path_fixed_drop_line_to (path);
+ } else {
+ cairo_slope_t prev, self;
+
+ _cairo_slope_init (&prev, p, &path->current_point);
+ _cairo_slope_init (&self, &path->current_point, &point);
+ if (_cairo_slope_equal (&prev, &self) &&
+ /* cannot trim anti-parallel segments whilst stroking */
+ ! _cairo_slope_backwards (&prev, &self))
+ {
+ _cairo_path_fixed_drop_line_to (path);
+ /* In this case the flags might be more restrictive than
+ * what we actually need.
+ * When changing the flags definition we should check if
+ * changing the line_to point can affect them.
+ */
+ }
+ }
+ }
+
+ if (path->stroke_is_rectilinear) {
+ path->stroke_is_rectilinear = path->current_point.x == x ||
+ path->current_point.y == y;
+ path->fill_is_rectilinear &= path->stroke_is_rectilinear;
+ path->fill_maybe_region &= path->fill_is_rectilinear;
+ if (path->fill_maybe_region) {
+ path->fill_maybe_region = _cairo_fixed_is_integer (x) &&
+ _cairo_fixed_is_integer (y);
+ }
+ if (path->fill_is_empty) {
+ path->fill_is_empty = path->current_point.x == x &&
+ path->current_point.y == y;
+ }
+ }
+
+ path->current_point = point;
+
+ _cairo_box_add_point (&path->extents, &point);
+
+ return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
+}
+
+cairo_status_t
+_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
+ cairo_fixed_t dx,
+ cairo_fixed_t dy)
+{
+ if (unlikely (! path->has_current_point))
+ return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
+
+ return _cairo_path_fixed_line_to (path,
+ path->current_point.x + dx,
+ path->current_point.y + dy);
+}
+
+cairo_status_t
+_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
+ cairo_fixed_t x0, cairo_fixed_t y0,
+ cairo_fixed_t x1, cairo_fixed_t y1,
+ cairo_fixed_t x2, cairo_fixed_t y2)
+{
+ cairo_status_t status;
+ cairo_point_t point[3];
+
+ /* If this curves does not move, replace it with a line-to.
+ * This frequently happens with rounded-rectangles and r==0.
+ */
+ if (path->current_point.x == x2 && path->current_point.y == y2) {
+ if (x1 == x2 && x0 == x2 && y1 == y2 && y0 == y2)
+ return _cairo_path_fixed_line_to (path, x2, y2);
+
+ /* We may want to check for the absence of a cusp, in which case
+ * we can also replace the curve-to with a line-to.
+ */
+ }
+
+ /* make sure subpaths are started properly */
+ if (! path->has_current_point) {
+ status = _cairo_path_fixed_move_to (path, x0, y0);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ status = _cairo_path_fixed_move_to_apply (path);
+ if (unlikely (status))
+ return status;
+
+ /* If the previous op was a degenerate LINE_TO, drop it. */
+ if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+ const cairo_point_t *p;
+
+ p = _cairo_path_fixed_penultimate_point (path);
+ if (p->x == path->current_point.x && p->y == path->current_point.y) {
+ /* previous line element was degenerate, replace */
+ _cairo_path_fixed_drop_line_to (path);
+ }
+ }
+
+ point[0].x = x0; point[0].y = y0;
+ point[1].x = x1; point[1].y = y1;
+ point[2].x = x2; point[2].y = y2;
+
+ _cairo_box_add_curve_to (&path->extents, &path->current_point,
+ &point[0], &point[1], &point[2]);
+
+ path->current_point = point[2];
+ path->has_curve_to = TRUE;
+ path->stroke_is_rectilinear = FALSE;
+ path->fill_is_rectilinear = FALSE;
+ path->fill_maybe_region = FALSE;
+ path->fill_is_empty = FALSE;
+
+ return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
+}
+
+cairo_status_t
+_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
+ cairo_fixed_t dx0, cairo_fixed_t dy0,
+ cairo_fixed_t dx1, cairo_fixed_t dy1,
+ cairo_fixed_t dx2, cairo_fixed_t dy2)
+{
+ if (unlikely (! path->has_current_point))
+ return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
+
+ return _cairo_path_fixed_curve_to (path,
+ path->current_point.x + dx0,
+ path->current_point.y + dy0,
+
+ path->current_point.x + dx1,
+ path->current_point.y + dy1,
+
+ path->current_point.x + dx2,
+ path->current_point.y + dy2);
+}
+
+cairo_status_t
+_cairo_path_fixed_close_path (cairo_path_fixed_t *path)
+{
+ cairo_status_t status;
+
+ if (! path->has_current_point)
+ return CAIRO_STATUS_SUCCESS;
+
+ /*
+ * Add a line_to, to compute flags and solve any degeneracy.
+ * It will be removed later (if it was actually added).
+ */
+ status = _cairo_path_fixed_line_to (path,
+ path->last_move_point.x,
+ path->last_move_point.y);
+ if (unlikely (status))
+ return status;
+
+ /*
+ * If the command used to close the path is a line_to, drop it.
+ * We must check that last command is actually a line_to,
+ * because the path could have been closed with a curve_to (and
+ * the previous line_to not added as it would be degenerate).
+ */
+ if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO)
+ _cairo_path_fixed_drop_line_to (path);
+
+ path->needs_move_to = TRUE; /* After close_path, add an implicit move_to */
+
+ return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
+}
+
+cairo_bool_t
+_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
+ cairo_fixed_t *x,
+ cairo_fixed_t *y)
+{
+ if (! path->has_current_point)
+ return FALSE;
+
+ *x = path->current_point.x;
+ *y = path->current_point.y;
+
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_path_fixed_add (cairo_path_fixed_t *path,
+ cairo_path_op_t op,
+ const cairo_point_t *points,
+ int num_points)
+{
+ cairo_path_buf_t *buf = cairo_path_tail (path);
+
+ if (buf->num_ops + 1 > buf->size_ops ||
+ buf->num_points + num_points > buf->size_points)
+ {
+ buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2);
+ if (unlikely (buf == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_path_fixed_add_buf (path, buf);
+ }
+
+ if (WATCH_PATH) {
+ const char *op_str[] = {
+ "move-to",
+ "line-to",
+ "curve-to",
+ "close-path",
+ };
+ char buf[1024];
+ int len = 0;
+ int i;
+
+ len += snprintf (buf + len, sizeof (buf), "[");
+ for (i = 0; i < num_points; i++) {
+ if (i != 0)
+ len += snprintf (buf + len, sizeof (buf), " ");
+ len += snprintf (buf + len, sizeof (buf), "(%f, %f)",
+ _cairo_fixed_to_double (points[i].x),
+ _cairo_fixed_to_double (points[i].y));
+ }
+ len += snprintf (buf + len, sizeof (buf), "]");
+
+#define STRINGIFYFLAG(x) (path->x ? #x " " : "")
+ fprintf (stderr,
+ "_cairo_path_fixed_add (%s, %s) [%s%s%s%s%s%s%s%s]\n",
+ op_str[(int) op], buf,
+ STRINGIFYFLAG(has_current_point),
+ STRINGIFYFLAG(needs_move_to),
+ STRINGIFYFLAG(has_extents),
+ STRINGIFYFLAG(has_curve_to),
+ STRINGIFYFLAG(stroke_is_rectilinear),
+ STRINGIFYFLAG(fill_is_rectilinear),
+ STRINGIFYFLAG(fill_is_empty),
+ STRINGIFYFLAG(fill_maybe_region)
+ );
+#undef STRINGIFYFLAG
+ }
+
+ _cairo_path_buf_add_op (buf, op);
+ _cairo_path_buf_add_points (buf, points, num_points);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
+ cairo_path_buf_t *buf)
+{
+ cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link);
+}
+
+COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1);
+static cairo_path_buf_t *
+_cairo_path_buf_create (int size_ops, int size_points)
+{
+ cairo_path_buf_t *buf;
+
+ /* adjust size_ops to ensure that buf->points is naturally aligned */
+ size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double));
+ buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t));
+ if (buf) {
+ buf->num_ops = 0;
+ buf->num_points = 0;
+ buf->size_ops = size_ops;
+ buf->size_points = size_points;
+
+ buf->op = (cairo_path_op_t *) (buf + 1);
+ buf->points = (cairo_point_t *) (buf->op + size_ops);
+ }
+
+ return buf;
+}
+
+static void
+_cairo_path_buf_destroy (cairo_path_buf_t *buf)
+{
+ free (buf);
+}
+
+static void
+_cairo_path_buf_add_op (cairo_path_buf_t *buf,
+ cairo_path_op_t op)
+{
+ buf->op[buf->num_ops++] = op;
+}
+
+static void
+_cairo_path_buf_add_points (cairo_path_buf_t *buf,
+ const cairo_point_t *points,
+ int num_points)
+{
+ if (num_points == 0)
+ return;
+
+ memcpy (buf->points + buf->num_points,
+ points,
+ sizeof (points[0]) * num_points);
+ buf->num_points += num_points;
+}
+
+cairo_status_t
+_cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_curve_to_func_t *curve_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure)
+{
+ const cairo_path_buf_t *buf;
+ cairo_status_t status;
+
+ cairo_path_foreach_buf_start (buf, path) {
+ const cairo_point_t *points = buf->points;
+ unsigned int i;
+
+ for (i = 0; i < buf->num_ops; i++) {
+ switch (buf->op[i]) {
+ case CAIRO_PATH_OP_MOVE_TO:
+ status = (*move_to) (closure, &points[0]);
+ points += 1;
+ break;
+ case CAIRO_PATH_OP_LINE_TO:
+ status = (*line_to) (closure, &points[0]);
+ points += 1;
+ break;
+ case CAIRO_PATH_OP_CURVE_TO:
+ status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
+ points += 3;
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATH_OP_CLOSE_PATH:
+ status = (*close_path) (closure);
+ break;
+ }
+
+ if (unlikely (status))
+ return status;
+ }
+ } cairo_path_foreach_buf_end (buf, path);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cairo_path_fixed_append_closure {
+ cairo_point_t offset;
+ cairo_path_fixed_t *path;
+} cairo_path_fixed_append_closure_t;
+
+static cairo_status_t
+_append_move_to (void *abstract_closure,
+ const cairo_point_t *point)
+{
+ cairo_path_fixed_append_closure_t *closure = abstract_closure;
+
+ return _cairo_path_fixed_move_to (closure->path,
+ point->x + closure->offset.x,
+ point->y + closure->offset.y);
+}
+
+static cairo_status_t
+_append_line_to (void *abstract_closure,
+ const cairo_point_t *point)
+{
+ cairo_path_fixed_append_closure_t *closure = abstract_closure;
+
+ return _cairo_path_fixed_line_to (closure->path,
+ point->x + closure->offset.x,
+ point->y + closure->offset.y);
+}
+
+static cairo_status_t
+_append_curve_to (void *abstract_closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ cairo_path_fixed_append_closure_t *closure = abstract_closure;
+
+ return _cairo_path_fixed_curve_to (closure->path,
+ p0->x + closure->offset.x,
+ p0->y + closure->offset.y,
+ p1->x + closure->offset.x,
+ p1->y + closure->offset.y,
+ p2->x + closure->offset.x,
+ p2->y + closure->offset.y);
+}
+
+static cairo_status_t
+_append_close_path (void *abstract_closure)
+{
+ cairo_path_fixed_append_closure_t *closure = abstract_closure;
+
+ return _cairo_path_fixed_close_path (closure->path);
+}
+
+cairo_status_t
+_cairo_path_fixed_append (cairo_path_fixed_t *path,
+ const cairo_path_fixed_t *other,
+ cairo_fixed_t tx,
+ cairo_fixed_t ty)
+{
+ cairo_path_fixed_append_closure_t closure;
+
+ closure.path = path;
+ closure.offset.x = tx;
+ closure.offset.y = ty;
+
+ return _cairo_path_fixed_interpret (other,
+ _append_move_to,
+ _append_line_to,
+ _append_curve_to,
+ _append_close_path,
+ &closure);
+}
+
+static void
+_cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
+ cairo_fixed_t offx,
+ cairo_fixed_t offy,
+ cairo_fixed_t scalex,
+ cairo_fixed_t scaley)
+{
+ cairo_path_buf_t *buf;
+ unsigned int i;
+
+ if (scalex == CAIRO_FIXED_ONE && scaley == CAIRO_FIXED_ONE) {
+ _cairo_path_fixed_translate (path, offx, offy);
+ return;
+ }
+
+ path->last_move_point.x = _cairo_fixed_mul (scalex, path->last_move_point.x) + offx;
+ path->last_move_point.y = _cairo_fixed_mul (scaley, path->last_move_point.y) + offy;
+ path->current_point.x = _cairo_fixed_mul (scalex, path->current_point.x) + offx;
+ path->current_point.y = _cairo_fixed_mul (scaley, path->current_point.y) + offy;
+
+ path->fill_maybe_region = TRUE;
+
+ cairo_path_foreach_buf_start (buf, path) {
+ for (i = 0; i < buf->num_points; i++) {
+ if (scalex != CAIRO_FIXED_ONE)
+ buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex);
+ buf->points[i].x += offx;
+
+ if (scaley != CAIRO_FIXED_ONE)
+ buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley);
+ buf->points[i].y += offy;
+
+ if (path->fill_maybe_region) {
+ path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
+ _cairo_fixed_is_integer (buf->points[i].y);
+ }
+ }
+ } cairo_path_foreach_buf_end (buf, path);
+
+ path->fill_maybe_region &= path->fill_is_rectilinear;
+
+ path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx;
+ path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx;
+ 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;
+
+ path->last_move_point.x += offx;
+ path->last_move_point.y += offy;
+ path->current_point.x += offx;
+ path->current_point.y += offy;
+
+ path->fill_maybe_region = TRUE;
+
+ cairo_path_foreach_buf_start (buf, path) {
+ for (i = 0; i < buf->num_points; i++) {
+ buf->points[i].x += offx;
+ buf->points[i].y += offy;
+
+ if (path->fill_maybe_region) {
+ path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
+ _cairo_fixed_is_integer (buf->points[i].y);
+ }
+ }
+ } cairo_path_foreach_buf_end (buf, path);
+
+ path->fill_maybe_region &= path->fill_is_rectilinear;
+
+ path->extents.p1.x += offx;
+ path->extents.p1.y += offy;
+ path->extents.p2.x += offx;
+ path->extents.p2.y += offy;
+}
+
+
+static inline void
+_cairo_path_fixed_transform_point (cairo_point_t *p,
+ const cairo_matrix_t *matrix)
+{
+ double dx, dy;
+
+ dx = _cairo_fixed_to_double (p->x);
+ dy = _cairo_fixed_to_double (p->y);
+ cairo_matrix_transform_point (matrix, &dx, &dy);
+ p->x = _cairo_fixed_from_double (dx);
+ p->y = _cairo_fixed_from_double (dy);
+}
+
+/**
+ * _cairo_path_fixed_transform:
+ * @path: a #cairo_path_fixed_t to be transformed
+ * @matrix: a #cairo_matrix_t
+ *
+ * Transform the fixed-point path according to the given matrix.
+ * There is a fast path for the case where @matrix has no rotation
+ * or shear.
+ **/
+void
+_cairo_path_fixed_transform (cairo_path_fixed_t *path,
+ const cairo_matrix_t *matrix)
+{
+ cairo_box_t extents;
+ cairo_point_t point;
+ cairo_path_buf_t *buf;
+ unsigned int i;
+
+ if (matrix->yx == 0.0 && matrix->xy == 0.0) {
+ /* Fast path for the common case of scale+transform */
+ _cairo_path_fixed_offset_and_scale (path,
+ _cairo_fixed_from_double (matrix->x0),
+ _cairo_fixed_from_double (matrix->y0),
+ _cairo_fixed_from_double (matrix->xx),
+ _cairo_fixed_from_double (matrix->yy));
+ return;
+ }
+
+ _cairo_path_fixed_transform_point (&path->last_move_point, matrix);
+ _cairo_path_fixed_transform_point (&path->current_point, matrix);
+
+ buf = cairo_path_head (path);
+ if (buf->num_points == 0)
+ return;
+
+ extents = path->extents;
+ point = buf->points[0];
+ _cairo_path_fixed_transform_point (&point, matrix);
+ _cairo_box_set (&path->extents, &point, &point);
+
+ cairo_path_foreach_buf_start (buf, path) {
+ for (i = 0; i < buf->num_points; i++) {
+ _cairo_path_fixed_transform_point (&buf->points[i], matrix);
+ _cairo_box_add_point (&path->extents, &buf->points[i]);
+ }
+ } cairo_path_foreach_buf_end (buf, path);
+
+ if (path->has_curve_to) {
+ cairo_bool_t is_tight;
+
+ _cairo_matrix_transform_bounding_box_fixed (matrix, &extents, &is_tight);
+ if (!is_tight) {
+ cairo_bool_t has_extents;
+
+ has_extents = _cairo_path_bounder_extents (path, &extents);
+ assert (has_extents);
+ }
+ path->extents = extents;
+ }
+
+ /* flags might become more strict than needed */
+ path->stroke_is_rectilinear = FALSE;
+ path->fill_is_rectilinear = FALSE;
+ path->fill_is_empty = FALSE;
+ path->fill_maybe_region = FALSE;
+}
+
+/* Closure for path flattening */
+typedef struct cairo_path_flattener {
+ double tolerance;
+ cairo_point_t current_point;
+ cairo_path_fixed_move_to_func_t *move_to;
+ cairo_path_fixed_line_to_func_t *line_to;
+ cairo_path_fixed_close_path_func_t *close_path;
+ void *closure;
+} cpf_t;
+
+static cairo_status_t
+_cpf_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cpf_t *cpf = closure;
+
+ cpf->current_point = *point;
+
+ return cpf->move_to (cpf->closure, point);
+}
+
+static cairo_status_t
+_cpf_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cpf_t *cpf = closure;
+
+ cpf->current_point = *point;
+
+ return cpf->line_to (cpf->closure, point);
+}
+
+static cairo_status_t
+_cpf_curve_to (void *closure,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ const cairo_point_t *p3)
+{
+ cpf_t *cpf = closure;
+ cairo_spline_t spline;
+
+ cairo_point_t *p0 = &cpf->current_point;
+
+ if (! _cairo_spline_init (&spline,
+ (cairo_spline_add_point_func_t)cpf->line_to,
+ cpf->closure,
+ p0, p1, p2, p3))
+ {
+ return _cpf_line_to (closure, p3);
+ }
+
+ cpf->current_point = *p3;
+
+ return _cairo_spline_decompose (&spline, cpf->tolerance);
+}
+
+static cairo_status_t
+_cpf_close_path (void *closure)
+{
+ cpf_t *cpf = closure;
+
+ return cpf->close_path (cpf->closure);
+}
+
+cairo_status_t
+_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure,
+ double tolerance)
+{
+ cpf_t flattener;
+
+ if (! path->has_curve_to) {
+ return _cairo_path_fixed_interpret (path,
+ move_to,
+ line_to,
+ NULL,
+ close_path,
+ closure);
+ }
+
+ flattener.tolerance = tolerance;
+ flattener.move_to = move_to;
+ flattener.line_to = line_to;
+ flattener.close_path = close_path;
+ flattener.closure = closure;
+ return _cairo_path_fixed_interpret (path,
+ _cpf_move_to,
+ _cpf_line_to,
+ _cpf_curve_to,
+ _cpf_close_path,
+ &flattener);
+}
+
+static inline void
+_canonical_box (cairo_box_t *box,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ if (p1->x <= p2->x) {
+ box->p1.x = p1->x;
+ box->p2.x = p2->x;
+ } else {
+ box->p1.x = p2->x;
+ box->p2.x = p1->x;
+ }
+
+ if (p1->y <= p2->y) {
+ box->p1.y = p1->y;
+ box->p2.y = p2->y;
+ } else {
+ box->p1.y = p2->y;
+ box->p2.y = p1->y;
+ }
+}
+
+static inline cairo_bool_t
+_path_is_quad (const cairo_path_fixed_t *path)
+{
+ const cairo_path_buf_t *buf = cairo_path_head (path);
+
+ /* Do we have the right number of ops? */
+ if (buf->num_ops < 4 || buf->num_ops > 6)
+ return FALSE;
+
+ /* Check whether the ops are those that would be used for a rectangle */
+ if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
+ buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
+ buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
+ buf->op[3] != CAIRO_PATH_OP_LINE_TO)
+ {
+ return FALSE;
+ }
+
+ /* we accept an implicit close for filled paths */
+ if (buf->num_ops > 4) {
+ /* Now, there are choices. The rectangle might end with a LINE_TO
+ * (to the original point), but this isn't required. If it
+ * doesn't, then it must end with a CLOSE_PATH. */
+ if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) {
+ if (buf->points[4].x != buf->points[0].x ||
+ buf->points[4].y != buf->points[0].y)
+ return FALSE;
+ } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) {
+ return FALSE;
+ }
+
+ if (buf->num_ops == 6) {
+ /* A trailing CLOSE_PATH or MOVE_TO is ok */
+ if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO &&
+ buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static inline cairo_bool_t
+_points_form_rect (const cairo_point_t *points)
+{
+ if (points[0].y == points[1].y &&
+ points[1].x == points[2].x &&
+ points[2].y == points[3].y &&
+ points[3].x == points[0].x)
+ return TRUE;
+ if (points[0].x == points[1].x &&
+ points[1].y == points[2].y &&
+ points[2].x == points[3].x &&
+ points[3].y == points[0].y)
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Check whether the given path contains a single rectangle.
+ */
+cairo_bool_t
+_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
+ cairo_box_t *box)
+{
+ const cairo_path_buf_t *buf;
+
+ if (! path->fill_is_rectilinear)
+ return FALSE;
+
+ if (! _path_is_quad (path))
+ return FALSE;
+
+ buf = cairo_path_head (path);
+ if (_points_form_rect (buf->points)) {
+ _canonical_box (box, &buf->points[0], &buf->points[2]);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Determine whether two lines A->B and C->D intersect based on the
+ * algorithm described here: http://paulbourke.net/geometry/lineline2d/ */
+static inline cairo_bool_t
+_lines_intersect_or_are_coincident (cairo_point_t a,
+ cairo_point_t b,
+ cairo_point_t c,
+ cairo_point_t d)
+{
+ cairo_int64_t numerator_a, numerator_b, denominator;
+
+ denominator = _cairo_int64_sub (_cairo_int32x32_64_mul (d.y - c.y, b.x - a.x),
+ _cairo_int32x32_64_mul (d.x - c.x, b.y - a.y));
+ numerator_a = _cairo_int64_sub (_cairo_int32x32_64_mul (d.x - c.x, a.y - c.y),
+ _cairo_int32x32_64_mul (d.y - c.y, a.x - c.x));
+ numerator_b = _cairo_int64_sub (_cairo_int32x32_64_mul (b.x - a.x, a.y - c.y),
+ _cairo_int32x32_64_mul (b.y - a.y, a.x - c.x));
+
+ if (_cairo_int64_is_zero (denominator)) {
+ /* If the denominator and numerators are both zero,
+ * the lines are coincident. */
+ if (_cairo_int64_is_zero (numerator_a) && _cairo_int64_is_zero (numerator_b))
+ return TRUE;
+
+ /* Otherwise, a zero denominator indicates the lines are
+ * parallel and never intersect. */
+ return FALSE;
+ }
+
+ /* If either division would produce a number between 0 and 1, i.e.
+ * the numerator is smaller than the denominator and their signs are
+ * the same, then the lines intersect. */
+ if (_cairo_int64_lt (numerator_a, denominator) &&
+ ! (_cairo_int64_negative (numerator_a) ^ _cairo_int64_negative(denominator))) {
+ return TRUE;
+ }
+
+ if (_cairo_int64_lt (numerator_b, denominator) &&
+ ! (_cairo_int64_negative (numerator_b) ^ _cairo_int64_negative(denominator))) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+cairo_bool_t
+_cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path)
+{
+ const cairo_point_t *points;
+
+ if (! _path_is_quad (path))
+ return FALSE;
+
+ points = cairo_path_head (path)->points;
+ if (_points_form_rect (points))
+ return TRUE;
+
+ if (_lines_intersect_or_are_coincident (points[0], points[1],
+ points[3], points[2]))
+ return FALSE;
+
+ if (_lines_intersect_or_are_coincident (points[0], points[3],
+ points[1], points[2]))
+ return FALSE;
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path,
+ cairo_box_t *box)
+{
+ const cairo_path_buf_t *buf = cairo_path_head (path);
+
+ if (! path->fill_is_rectilinear)
+ return FALSE;
+
+ /* Do we have the right number of ops? */
+ if (buf->num_ops != 5)
+ return FALSE;
+
+ /* Check whether the ops are those that would be used for a rectangle */
+ if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
+ buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
+ buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
+ buf->op[3] != CAIRO_PATH_OP_LINE_TO ||
+ buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH)
+ {
+ return FALSE;
+ }
+
+ /* Ok, we may have a box, if the points line up */
+ if (buf->points[0].y == buf->points[1].y &&
+ buf->points[1].x == buf->points[2].x &&
+ buf->points[2].y == buf->points[3].y &&
+ buf->points[3].x == buf->points[0].x)
+ {
+ _canonical_box (box, &buf->points[0], &buf->points[2]);
+ return TRUE;
+ }
+
+ if (buf->points[0].x == buf->points[1].x &&
+ buf->points[1].y == buf->points[2].y &&
+ buf->points[2].x == buf->points[3].x &&
+ buf->points[3].y == buf->points[0].y)
+ {
+ _canonical_box (box, &buf->points[0], &buf->points[2]);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Check whether the given path contains a single rectangle
+ * that is logically equivalent to:
+ * <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;
+
+ /* This check is valid because the current implementation of
+ * _cairo_path_fixed_is_box () only accepts rectangles like:
+ * move,line,line,line[,line|close[,close|move]]. */
+ buf = cairo_path_head (path);
+ if (buf->num_ops > 4)
+ return TRUE;
+
+ return FALSE;
+}
+
+void
+_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
+ const cairo_path_fixed_t *path)
+{
+ iter->first = iter->buf = cairo_path_head (path);
+ iter->n_op = 0;
+ iter->n_point = 0;
+}
+
+cairo_bool_t
+_cairo_path_fixed_is_single_line (const cairo_path_fixed_t *path)
+{
+ const cairo_path_buf_t *buf = cairo_path_head (path);
+
+ if (buf->num_ops > 2)
+ return FALSE;
+ if (buf->num_ops <= 1)
+ return TRUE;
+
+ return buf->op[0] == CAIRO_PATH_OP_MOVE_TO &&
+ buf->op[1] == CAIRO_PATH_OP_LINE_TO;
+}
+
+cairo_bool_t
+_cairo_path_fixed_is_single_arc (const cairo_path_fixed_t *path)
+{
+ const cairo_path_buf_t *buf = cairo_path_head (path);
+ if (buf->num_ops > 2)
+ return FALSE;
+
+ if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
+ buf->op[1] != CAIRO_PATH_OP_CURVE_TO)
+ return FALSE;
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter)
+{
+ if (++iter->n_op >= iter->buf->num_ops) {
+ iter->buf = cairo_path_buf_next (iter->buf);
+ if (iter->buf == iter->first) {
+ iter->buf = NULL;
+ return FALSE;
+ }
+
+ iter->n_op = 0;
+ iter->n_point = 0;
+ }
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
+ cairo_box_t *box)
+{
+ cairo_point_t points[5];
+ cairo_path_fixed_iter_t iter;
+
+ if (_iter->buf == NULL)
+ return FALSE;
+
+ iter = *_iter;
+
+ if (iter.n_op == iter.buf->num_ops && ! _cairo_path_fixed_iter_next_op (&iter))
+ return FALSE;
+
+ /* Check whether the ops are those that would be used for a rectangle */
+ if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO)
+ return FALSE;
+ points[0] = iter.buf->points[iter.n_point++];
+ if (! _cairo_path_fixed_iter_next_op (&iter))
+ return FALSE;
+
+ if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
+ return FALSE;
+ points[1] = iter.buf->points[iter.n_point++];
+ if (! _cairo_path_fixed_iter_next_op (&iter))
+ return FALSE;
+
+ /* a horizontal/vertical closed line is also a degenerate rectangle */
+ switch (iter.buf->op[iter.n_op]) {
+ case CAIRO_PATH_OP_CLOSE_PATH:
+ _cairo_path_fixed_iter_next_op (&iter);
+ case CAIRO_PATH_OP_MOVE_TO: /* implicit close */
+ box->p1 = box->p2 = points[0];
+ *_iter = iter;
+ return TRUE;
+ default:
+ return FALSE;
+ case CAIRO_PATH_OP_LINE_TO:
+ break;
+ }
+
+ points[2] = iter.buf->points[iter.n_point++];
+ if (! _cairo_path_fixed_iter_next_op (&iter))
+ return FALSE;
+
+ if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
+ return FALSE;
+ points[3] = iter.buf->points[iter.n_point++];
+
+ /* Now, there are choices. The rectangle might end with a LINE_TO
+ * (to the original point), but this isn't required. If it
+ * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */
+ if (! _cairo_path_fixed_iter_next_op (&iter)) {
+ /* implicit close due to fill */
+ } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) {
+ points[4] = iter.buf->points[iter.n_point++];
+ if (points[4].x != points[0].x || points[4].y != points[0].y)
+ return FALSE;
+ _cairo_path_fixed_iter_next_op (&iter);
+ } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH) {
+ _cairo_path_fixed_iter_next_op (&iter);
+ } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO) {
+ /* implicit close-path due to new-sub-path */
+ } else {
+ return FALSE;
+ }
+
+ /* Ok, we may have a box, if the points line up */
+ if (points[0].y == points[1].y &&
+ points[1].x == points[2].x &&
+ points[2].y == points[3].y &&
+ points[3].x == points[0].x)
+ {
+ box->p1 = points[0];
+ box->p2 = points[2];
+ *_iter = iter;
+ return TRUE;
+ }
+
+ if (points[0].x == points[1].x &&
+ points[1].y == points[2].y &&
+ points[2].x == points[3].x &&
+ points[3].y == points[0].y)
+ {
+ box->p1 = points[1];
+ box->p2 = points[3];
+ *_iter = iter;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+cairo_bool_t
+_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
+{
+ if (iter->buf == NULL)
+ return TRUE;
+
+ return iter->n_op == iter->buf->num_ops;
+}
diff --git a/src/cairo-path-in-fill.c b/src/cairo-path-in-fill.c
new file mode 100755
index 000000000..1787fb1a3
--- /dev/null
+++ b/src/cairo-path-in-fill.c
@@ -0,0 +1,290 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <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_spline_add_point_func_t)_cairo_in_fill_line_to,
+ in_fill,
+ &in_fill->current_point, b, c, d))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_spline_decompose (&spline, in_fill->tolerance);
+}
+
+static cairo_status_t
+_cairo_in_fill_close_path (void *closure)
+{
+ cairo_in_fill_t *in_fill = closure;
+
+ if (in_fill->has_current_point) {
+ _cairo_in_fill_add_edge (in_fill,
+ &in_fill->current_point,
+ &in_fill->first_point);
+
+ in_fill->has_current_point = FALSE;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ double x,
+ double y)
+{
+ cairo_in_fill_t in_fill;
+ cairo_status_t status;
+ cairo_bool_t is_inside;
+
+ if (_cairo_path_fixed_fill_is_empty (path))
+ return FALSE;
+
+ _cairo_in_fill_init (&in_fill, tolerance, x, y);
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_in_fill_move_to,
+ _cairo_in_fill_line_to,
+ _cairo_in_fill_curve_to,
+ _cairo_in_fill_close_path,
+ &in_fill);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _cairo_in_fill_close_path (&in_fill);
+
+ if (in_fill.on_edge) {
+ is_inside = TRUE;
+ } else switch (fill_rule) {
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ is_inside = in_fill.winding & 1;
+ break;
+ case CAIRO_FILL_RULE_WINDING:
+ is_inside = in_fill.winding != 0;
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ is_inside = FALSE;
+ break;
+ }
+
+ _cairo_in_fill_fini (&in_fill);
+
+ return is_inside;
+}
diff --git a/src/cairo-path-private.h b/src/cairo-path-private.h
new file mode 100755
index 000000000..7b54317e2
--- /dev/null
+++ b/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_t *cr);
+
+cairo_private cairo_path_t *
+_cairo_path_create_flat (cairo_path_fixed_t *path,
+ cairo_t *cr);
+
+cairo_private cairo_path_t *
+_cairo_path_create_in_error (cairo_status_t status);
+
+cairo_private cairo_status_t
+_cairo_path_append_to_context (const cairo_path_t *path,
+ cairo_t *cr);
+
+#endif /* CAIRO_PATH_DATA_PRIVATE_H */
diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c
new file mode 100755
index 000000000..7f25bf76c
--- /dev/null
+++ b/src/cairo-path-stroke-boxes.c
@@ -0,0 +1,711 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-inline.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+
+typedef struct _segment_t {
+ cairo_point_t p1, p2;
+ unsigned flags;
+#define HORIZONTAL 0x1
+#define FORWARDS 0x2
+#define JOIN 0x4
+} segment_t;
+
+typedef struct _cairo_rectilinear_stroker {
+ const cairo_stroke_style_t *stroke_style;
+ const cairo_matrix_t *ctm;
+ cairo_antialias_t antialias;
+
+ cairo_fixed_t half_line_x, half_line_y;
+ cairo_boxes_t *boxes;
+ cairo_point_t current_point;
+ cairo_point_t first_point;
+ cairo_bool_t open_sub_path;
+
+ cairo_stroker_dash_t dash;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t bounds;
+
+ int num_segments;
+ int segments_size;
+ segment_t *segments;
+ segment_t segments_embedded[8]; /* common case is a single rectangle */
+} cairo_rectilinear_stroker_t;
+
+static void
+_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
+ const cairo_box_t *boxes,
+ int num_boxes)
+{
+ stroker->has_bounds = TRUE;
+ _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+ stroker->bounds.p1.x -= stroker->half_line_x;
+ stroker->bounds.p2.x += stroker->half_line_x;
+
+ stroker->bounds.p1.y -= stroker->half_line_y;
+ stroker->bounds.p2.y += stroker->half_line_y;
+}
+
+static cairo_bool_t
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ /* This special-case rectilinear stroker only supports
+ * miter-joined lines (not curves) and a translation-only matrix
+ * (though it could probably be extended to support a matrix with
+ * uniform, integer scaling).
+ *
+ * It also only supports horizontal and vertical line_to
+ * elements. But we don't catch that here, but instead return
+ * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
+ * non-rectilinear line_to is encountered.
+ */
+ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
+ return FALSE;
+
+ /* If the miter limit turns right angles into bevels, then we
+ * can't use this optimization. Remember, the ratio is
+ * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
+ * which we round for safety. */
+ if (stroke_style->miter_limit < M_SQRT2)
+ return FALSE;
+
+ if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
+ stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
+ {
+ return FALSE;
+ }
+
+ if (! _cairo_matrix_is_scale (ctm))
+ return FALSE;
+
+ stroker->stroke_style = stroke_style;
+ stroker->ctm = ctm;
+ stroker->antialias = antialias;
+
+ stroker->half_line_x =
+ _cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0);
+ stroker->half_line_y =
+ _cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0);
+
+ stroker->open_sub_path = FALSE;
+ stroker->segments = stroker->segments_embedded;
+ stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
+ stroker->num_segments = 0;
+
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+ stroker->has_bounds = FALSE;
+
+ stroker->boxes = boxes;
+
+ return TRUE;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
+{
+ if (stroker->segments != stroker->segments_embedded)
+ free (stroker->segments);
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ unsigned flags)
+{
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (stroker->num_segments == stroker->segments_size) {
+ int new_size = stroker->segments_size * 2;
+ segment_t *new_segments;
+
+ if (stroker->segments == stroker->segments_embedded) {
+ new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (new_segments, stroker->segments,
+ stroker->num_segments * sizeof (segment_t));
+ } else {
+ new_segments = _cairo_realloc_ab (stroker->segments,
+ new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ stroker->segments_size = new_size;
+ stroker->segments = new_segments;
+ }
+
+ stroker->segments[stroker->num_segments].p1 = *p1;
+ stroker->segments[stroker->num_segments].p2 = *p2;
+ stroker->segments[stroker->num_segments].flags = flags;
+ stroker->num_segments++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_x = stroker->half_line_x;
+ cairo_fixed_t half_line_y = stroker->half_line_y;
+ cairo_status_t status;
+ int i, j;
+
+ /* For each segment we generate a single rectangle.
+ * This rectangle is based on a perpendicular extension (by half the
+ * line width) of the segment endpoints * after some adjustments of the
+ * endpoints to account for caps and joins.
+ */
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_bool_t lengthen_initial, lengthen_final;
+ cairo_point_t *a, *b;
+ cairo_box_t box;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ /* We adjust the initial point of the segment to extend the
+ * rectangle to include the previous cap or join, (this
+ * adjustment applies to all segments except for the first
+ * segment of open, butt-capped paths). However, we must be
+ * careful not to emit a miter join across a degenerate segment
+ * which has been elided.
+ *
+ * Overlapping segments will be eliminated by the tessellation.
+ * Ideally, we would not emit these self-intersections at all,
+ * but that is tricky with segments shorter than half_line_width.
+ */
+ j = i == 0 ? stroker->num_segments - 1 : i-1;
+ lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
+ j = i == stroker->num_segments - 1 ? 0 : i+1;
+ lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
+ if (stroker->open_sub_path) {
+ if (i == 0)
+ lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT;
+
+ if (i == stroker->num_segments - 1)
+ lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (lengthen_initial | lengthen_final) {
+ if (a->y == b->y) {
+ if (a->x < b->x) {
+ if (lengthen_initial)
+ a->x -= half_line_x;
+ if (lengthen_final)
+ b->x += half_line_x;
+ } else {
+ if (lengthen_initial)
+ a->x += half_line_x;
+ if (lengthen_final)
+ b->x -= half_line_x;
+ }
+ } else {
+ if (a->y < b->y) {
+ if (lengthen_initial)
+ a->y -= half_line_y;
+ if (lengthen_final)
+ b->y += half_line_y;
+ } else {
+ if (lengthen_initial)
+ a->y += half_line_y;
+ if (lengthen_final)
+ b->y -= half_line_y;
+ }
+ }
+ }
+
+ /* Form the rectangle by expanding by half the line width in
+ * either perpendicular direction. */
+ if (a->y == b->y) {
+ a->y -= half_line_y;
+ b->y += half_line_y;
+ } else {
+ a->x -= half_line_x;
+ b->x += half_line_x;
+ }
+
+ if (a->x < b->x) {
+ box.p1.x = a->x;
+ box.p2.x = b->x;
+ } else {
+ box.p1.x = b->x;
+ box.p2.x = a->x;
+ }
+ if (a->y < b->y) {
+ box.p1.y = a->y;
+ box.p2.y = b->y;
+ } else {
+ box.p1.y = b->y;
+ box.p2.y = a->y;
+ }
+
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_x = stroker->half_line_x;
+ cairo_fixed_t half_line_y = stroker->half_line_y;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t is_horizontal;
+ cairo_box_t box;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ is_horizontal = stroker->segments[i].flags & HORIZONTAL;
+
+ /* Handle the joins for a potentially degenerate segment. */
+ if (line_cap == CAIRO_LINE_CAP_BUTT &&
+ stroker->segments[i].flags & JOIN &&
+ (i != stroker->num_segments - 1 ||
+ (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
+ {
+ cairo_slope_t out_slope;
+ int j = (i + 1) % stroker->num_segments;
+ cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS);
+
+ _cairo_slope_init (&out_slope,
+ &stroker->segments[j].p1,
+ &stroker->segments[j].p2);
+ box.p2 = box.p1 = stroker->segments[i].p2;
+
+ if (is_horizontal) {
+ if (forwards)
+ box.p2.x += half_line_x;
+ else
+ box.p1.x -= half_line_x;
+
+ if (out_slope.dy > 0)
+ box.p1.y -= half_line_y;
+ else
+ box.p2.y += half_line_y;
+ } else {
+ if (forwards)
+ box.p2.y += half_line_y;
+ else
+ box.p1.y -= half_line_y;
+
+ if (out_slope.dx > 0)
+ box.p1.x -= half_line_x;
+ else
+ box.p2.x += half_line_x;
+ }
+
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (is_horizontal) {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->x <= b->x) {
+ a->x -= half_line_x;
+ b->x += half_line_x;
+ } else {
+ a->x += half_line_x;
+ b->x -= half_line_x;
+ }
+ }
+
+ a->y += half_line_y;
+ b->y -= half_line_y;
+ } else {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->y <= b->y) {
+ a->y -= half_line_y;
+ b->y += half_line_y;
+ } else {
+ a->y += half_line_y;
+ b->y -= half_line_y;
+ }
+ }
+
+ a->x += half_line_x;
+ b->x -= half_line_x;
+ }
+
+ if (a->x == b->x && a->y == b->y)
+ continue;
+
+ if (a->x < b->x) {
+ box.p1.x = a->x;
+ box.p2.x = b->x;
+ } else {
+ box.p1.x = b->x;
+ box.p2.x = a->x;
+ }
+ if (a->y < b->y) {
+ box.p1.y = a->y;
+ box.p2.y = b->y;
+ } else {
+ box.p1.y = b->y;
+ box.p2.y = a->y;
+ }
+
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ /* reset the dash pattern for new sub paths */
+ _cairo_stroker_dash_start (&stroker->dash);
+
+ stroker->current_point = *point;
+ stroker->first_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void *closure,
+ const cairo_point_t *b)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_point_t *a = &stroker->current_point;
+ cairo_status_t status;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+ (a->y == b->y) | JOIN);
+
+ stroker->current_point = *b;
+ stroker->open_sub_path = TRUE;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ const cairo_point_t *a = &stroker->current_point;
+ const cairo_point_t *b = point;
+ cairo_bool_t fully_in_bounds;
+ double sf, sign, remain;
+ cairo_fixed_t mag;
+ cairo_status_t status;
+ cairo_line_t segment;
+ cairo_bool_t dash_on = FALSE;
+ unsigned is_horizontal;
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ fully_in_bounds = TRUE;
+ if (stroker->has_bounds &&
+ (! _cairo_box_contains_point (&stroker->bounds, a) ||
+ ! _cairo_box_contains_point (&stroker->bounds, b)))
+ {
+ fully_in_bounds = FALSE;
+ }
+
+ is_horizontal = a->y == b->y;
+ if (is_horizontal) {
+ mag = b->x - a->x;
+ sf = fabs (stroker->ctm->xx);
+ } else {
+ mag = b->y - a->y;
+ sf = fabs (stroker->ctm->yy);
+ }
+ if (mag < 0) {
+ remain = _cairo_fixed_to_double (-mag);
+ sign = 1.;
+ } else {
+ remain = _cairo_fixed_to_double (mag);
+ is_horizontal |= FORWARDS;
+ sign = -1.;
+ }
+
+ segment.p2 = segment.p1 = *a;
+ while (remain > 0.) {
+ double step_length;
+
+ step_length = MIN (sf * stroker->dash.dash_remain, remain);
+ remain -= step_length;
+
+ mag = _cairo_fixed_from_double (sign*remain);
+ if (is_horizontal & 0x1)
+ segment.p2.x = b->x + mag;
+ else
+ segment.p2.y = b->y + mag;
+
+ if (stroker->dash.dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p2,
+ is_horizontal | (remain <= 0.) << 2);
+ if (unlikely (status))
+ return status;
+
+ dash_on = TRUE;
+ }
+ else
+ {
+ dash_on = FALSE;
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length / sf);
+ segment.p1 = segment.p2;
+ }
+
+ if (stroker->dash.dash_on && ! dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+
+ /* This segment ends on a transition to dash_on, compute a new face
+ * and add cap for the beginning of the next dash_on step.
+ */
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p1,
+ is_horizontal | JOIN);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->current_point = *point;
+ stroker->open_sub_path = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_close_path (void *closure)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ /* We don't draw anything for degenerate paths. */
+ if (! stroker->open_sub_path)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (stroker->dash.dashed) {
+ status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
+ &stroker->first_point);
+ } else {
+ status = _cairo_rectilinear_stroker_line_to (stroker,
+ &stroker->first_point);
+ }
+ if (unlikely (status))
+ return status;
+
+ stroker->open_sub_path = FALSE;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ cairo_rectilinear_stroker_t rectilinear_stroker;
+ cairo_int_status_t status;
+ cairo_box_t box;
+
+ assert (_cairo_path_fixed_stroke_is_rectilinear (path));
+
+ if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+ stroke_style, ctm, antialias,
+ boxes))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (! rectilinear_stroker.dash.dashed &&
+ _cairo_path_fixed_is_stroke_box (path, &box) &&
+ /* if the segments overlap we need to feed them into the tessellator */
+ box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x &&
+ box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y)
+ {
+ cairo_box_t b;
+
+ /* top */
+ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
+ b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
+ b.p1.y = box.p1.y - rectilinear_stroker.half_line_y;
+ b.p2.y = box.p1.y + rectilinear_stroker.half_line_y;
+ status = _cairo_boxes_add (boxes, antialias, &b);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ /* left (excluding top/bottom) */
+ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
+ b.p2.x = box.p1.x + rectilinear_stroker.half_line_x;
+ b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
+ b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
+ status = _cairo_boxes_add (boxes, antialias, &b);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ /* right (excluding top/bottom) */
+ b.p1.x = box.p2.x - rectilinear_stroker.half_line_x;
+ b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
+ b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
+ b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
+ status = _cairo_boxes_add (boxes, antialias, &b);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ /* bottom */
+ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
+ b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
+ b.p1.y = box.p2.y - rectilinear_stroker.half_line_y;
+ b.p2.y = box.p2.y + rectilinear_stroker.half_line_y;
+ status = _cairo_boxes_add (boxes, antialias, &b);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ goto done;
+ }
+
+ if (boxes->num_limits) {
+ _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+ boxes->limits,
+ boxes->num_limits);
+ }
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_rectilinear_stroker_move_to,
+ rectilinear_stroker.dash.dashed ?
+ _cairo_rectilinear_stroker_line_to_dashed :
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (rectilinear_stroker.dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ /* As we incrementally tessellate, we do not eliminate self-intersections */
+ status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
+ CAIRO_FILL_RULE_WINDING,
+ boxes);
+ if (unlikely (status))
+ goto BAIL;
+
+done:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+ _cairo_boxes_clear (boxes);
+ return status;
+}
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
new file mode 100755
index 000000000..b62ddfb8f
--- /dev/null
+++ b/src/cairo-path-stroke-polygon.c
@@ -0,0 +1,1377 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-inline.h"
+#include "cairo-boxes-private.h"
+#include "cairo-contour-inline.h"
+#include "cairo-contour-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+
+#define DEBUG 0
+
+struct stroker {
+ cairo_stroke_style_t style;
+
+#if DEBUG
+ cairo_contour_t path;
+#endif
+
+ struct stroke_contour {
+ /* Note that these are not strictly contours as they may intersect */
+ cairo_contour_t contour;
+ } cw, ccw;
+ cairo_uint64_t contour_tolerance;
+ cairo_polygon_t *polygon;
+
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
+ double tolerance;
+ double spline_cusp_tolerance;
+ double half_line_width;
+ cairo_bool_t ctm_det_positive;
+
+ cairo_pen_t pen;
+
+ cairo_point_t first_point;
+
+ cairo_bool_t has_initial_sub_path;
+
+ cairo_bool_t has_current_face;
+ cairo_stroke_face_t current_face;
+
+ cairo_bool_t has_first_face;
+ cairo_stroke_face_t first_face;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t bounds;
+};
+
+static inline double
+normalize_slope (double *dx, double *dy);
+
+static void
+compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ struct stroker *stroker,
+ cairo_stroke_face_t *face);
+
+static cairo_uint64_t
+point_distance_sq (const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ int32_t dx = p1->x - p2->x;
+ int32_t dy = p1->y - p2->y;
+ return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
+}
+
+static cairo_bool_t
+within_tolerance (const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_uint64_t tolerance)
+{
+ return FALSE;
+ return _cairo_int64_lt (point_distance_sq (p1, p2), tolerance);
+}
+
+static void
+contour_add_point (struct stroker *stroker,
+ struct stroke_contour *c,
+ const cairo_point_t *point)
+{
+ if (! within_tolerance (point, _cairo_contour_last_point (&c->contour),
+ stroker->contour_tolerance))
+ _cairo_contour_add_point (&c->contour, point);
+ //*_cairo_contour_last_point (&c->contour) = *point;
+}
+
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+ point->x += offset->x;
+ point->y += offset->y;
+}
+
+static int
+slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+ double c = (dx1 * dy2 - dx2 * dy1);
+
+ if (c > 0) return 1;
+ if (c < 0) return -1;
+ return 0;
+}
+
+static inline int
+range_step (int i, int step, int max)
+{
+ i += step;
+ if (i < 0)
+ i = max - 1;
+ if (i >= max)
+ i = 0;
+ return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static void
+add_fan (struct stroker *stroker,
+ const cairo_slope_t *in_vector,
+ const cairo_slope_t *out_vector,
+ const cairo_point_t *midpt,
+ cairo_bool_t clockwise,
+ struct stroke_contour *c)
+{
+ cairo_pen_t *pen = &stroker->pen;
+ int start, stop;
+
+ if (stroker->has_bounds &&
+ ! _cairo_box_contains_point (&stroker->bounds, midpt))
+ return;
+
+ assert (stroker->pen.num_vertices);
+
+ if (clockwise) {
+ _cairo_pen_find_active_cw_vertices (pen,
+ in_vector, out_vector,
+ &start, &stop);
+ while (start != stop) {
+ cairo_point_t p = *midpt;
+ translate_point (&p, &pen->vertices[start].point);
+ contour_add_point (stroker, c, &p);
+
+ if (++start == pen->num_vertices)
+ start = 0;
+ }
+ } else {
+ _cairo_pen_find_active_ccw_vertices (pen,
+ in_vector, out_vector,
+ &start, &stop);
+ while (start != stop) {
+ cairo_point_t p = *midpt;
+ translate_point (&p, &pen->vertices[start].point);
+ contour_add_point (stroker, c, &p);
+
+ if (start-- == 0)
+ start += pen->num_vertices;
+ }
+ }
+}
+
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
+}
+
+static void
+inner_join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out,
+ int clockwise)
+{
+#if 0
+ cairo_point_t last;
+ const cairo_point_t *p, *outpt;
+ struct stroke_contour *inner;
+ cairo_int64_t d_p, d_last;
+ cairo_int64_t half_line_width;
+ cairo_bool_t negate;
+
+ /* XXX line segments shorter than line-width */
+
+ if (clockwise) {
+ inner = &stroker->ccw;
+ outpt = &out->ccw;
+ negate = 1;
+ } else {
+ inner = &stroker->cw;
+ outpt = &out->cw;
+ negate = 0;
+ }
+
+ half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5;
+
+ /* On the inside, the previous end-point is always
+ * closer to the new face by definition.
+ */
+ last = *_cairo_contour_last_point (&inner->contour);
+ d_last = distance_from_face (out, &last, negate);
+ _cairo_contour_remove_last_point (&inner->contour);
+
+prev:
+ if (inner->contour.chain.num_points == 0) {
+ contour_add_point (stroker, inner, outpt);
+ return;
+ }
+ p = _cairo_contour_last_point (&inner->contour);
+ d_p = distance_from_face (out, p, negate);
+ if (_cairo_int64_lt (d_p, half_line_width) &&
+ !_cairo_int64_negative (distance_along_face (out, p)))
+ {
+ last = *p;
+ d_last = d_p;
+ _cairo_contour_remove_last_point (&inner->contour);
+ goto prev;
+ }
+
+ compute_inner_joint (&last, d_last, p, d_p, half_line_width);
+ contour_add_point (stroker, inner, &last);
+#else
+ const cairo_point_t *outpt;
+ struct stroke_contour *inner;
+
+ if (clockwise) {
+ inner = &stroker->ccw;
+ outpt = &out->ccw;
+ } else {
+ inner = &stroker->cw;
+ outpt = &out->cw;
+ }
+ contour_add_point (stroker, inner, &in->point);
+ contour_add_point (stroker, inner, outpt);
+#endif
+}
+
+static void
+inner_close (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ cairo_stroke_face_t *out)
+{
+#if 0
+ cairo_point_t last;
+ const cairo_point_t *p, *outpt, *inpt;
+ struct stroke_contour *inner;
+ struct _cairo_contour_chain *chain;
+
+ /* XXX line segments shorter than line-width */
+
+ if (join_is_clockwise (in, out)) {
+ inner = &stroker->ccw;
+ outpt = &in->ccw;
+ inpt = &out->ccw;
+ } else {
+ inner = &stroker->cw;
+ outpt = &in->cw;
+ inpt = &out->cw;
+ }
+
+ if (inner->contour.chain.num_points == 0) {
+ contour_add_point (stroker, inner, &in->point);
+ contour_add_point (stroker, inner, inpt);
+ *_cairo_contour_first_point (&inner->contour) =
+ *_cairo_contour_last_point (&inner->contour);
+ return;
+ }
+
+ line_width = stroker->style.line_width/2;
+ line_width *= CAIRO_FIXED_ONE;
+
+ d_last = sign * distance_from_face (out, outpt);
+ last = *outpt;
+
+ for (chain = &inner->contour.chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ p = &chain->points[i];
+ if ((d_p = sign * distance_from_face (in, p)) >= line_width &&
+ distance_from_edge (stroker, inpt, &last, p) < line_width)
+ {
+ goto out;
+ }
+
+ if (p->x != last.x || p->y != last.y) {
+ last = *p;
+ d_last = d_p;
+ }
+ }
+ }
+out:
+
+ if (d_p != d_last) {
+ double dot = (line_width - d_last) / (d_p - d_last);
+ last.x += dot * (p->x - last.x);
+ last.y += dot * (p->y - last.y);
+ }
+ *_cairo_contour_last_point (&inner->contour) = last;
+
+ for (chain = &inner->contour.chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ cairo_point_t *pp = &chain->points[i];
+ if (pp == p)
+ return;
+ *pp = last;
+ }
+ }
+#else
+ const cairo_point_t *inpt;
+ struct stroke_contour *inner;
+
+ if (join_is_clockwise (in, out)) {
+ inner = &stroker->ccw;
+ inpt = &out->ccw;
+ } else {
+ inner = &stroker->cw;
+ inpt = &out->cw;
+ }
+
+ contour_add_point (stroker, inner, &in->point);
+ contour_add_point (stroker, inner, inpt);
+ *_cairo_contour_first_point (&inner->contour) =
+ *_cairo_contour_last_point (&inner->contour);
+#endif
+}
+
+static void
+outer_close (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ const cairo_point_t *inpt, *outpt;
+ struct stroke_contour *outer;
+ int clockwise;
+
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+ {
+ return;
+ }
+
+ clockwise = join_is_clockwise (in, out);
+ if (clockwise) {
+ inpt = &in->cw;
+ outpt = &out->cw;
+ outer = &stroker->cw;
+ } else {
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ outer = &stroker->ccw;
+ }
+
+ if (within_tolerance (inpt, outpt, stroker->contour_tolerance)) {
+ *_cairo_contour_first_point (&outer->contour) =
+ *_cairo_contour_last_point (&outer->contour);
+ return;
+ }
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ if ((in->dev_slope.x * out->dev_slope.x +
+ in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance)
+ {
+ add_fan (stroker,
+ &in->dev_vector, &out->dev_vector, &in->point,
+ clockwise, outer);
+ break;
+ }
+
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = in->dev_slope.x * out->dev_slope.x +
+ in->dev_slope.y * out->dev_slope.y;
+ double ml = stroker->style.miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 + in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->dev_slope.x;
+ dy1 = in->dev_slope.y;
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->dev_slope.x;
+ dy2 = out->dev_slope.y;
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ cairo_point_t p;
+
+ p.x = _cairo_fixed_from_double (mx);
+ p.y = _cairo_fixed_from_double (my);
+
+ *_cairo_contour_last_point (&outer->contour) = p;
+ *_cairo_contour_first_point (&outer->contour) = p;
+ return;
+ }
+ }
+ break;
+ }
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ break;
+ }
+ contour_add_point (stroker, outer, outpt);
+}
+
+static void
+outer_join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out,
+ int clockwise)
+{
+ const cairo_point_t *inpt, *outpt;
+ struct stroke_contour *outer;
+
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+ {
+ return;
+ }
+ if (clockwise) {
+ inpt = &in->cw;
+ outpt = &out->cw;
+ outer = &stroker->cw;
+ } else {
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ outer = &stroker->ccw;
+ }
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ add_fan (stroker,
+ &in->dev_vector, &out->dev_vector, &in->point,
+ clockwise, outer);
+ break;
+
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = in->dev_slope.x * out->dev_slope.x +
+ in->dev_slope.y * out->dev_slope.y;
+ double ml = stroker->style.miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 + in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->dev_slope.x;
+ dy1 = in->dev_slope.y;
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->dev_slope.x;
+ dy2 = out->dev_slope.y;
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ cairo_point_t p;
+
+ p.x = _cairo_fixed_from_double (mx);
+ p.y = _cairo_fixed_from_double (my);
+
+ *_cairo_contour_last_point (&outer->contour) = p;
+ return;
+ }
+ }
+ break;
+ }
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ break;
+ }
+ contour_add_point (stroker,outer, outpt);
+}
+
+static void
+add_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *f,
+ struct stroke_contour *c)
+{
+ switch (stroker->style.line_cap) {
+ case CAIRO_LINE_CAP_ROUND: {
+ cairo_slope_t slope;
+
+ slope.dx = -f->dev_vector.dx;
+ slope.dy = -f->dev_vector.dy;
+
+ add_fan (stroker, &f->dev_vector, &slope, &f->point, FALSE, c);
+ break;
+ }
+
+ case CAIRO_LINE_CAP_SQUARE: {
+ cairo_slope_t fvector;
+ cairo_point_t p;
+ double dx, dy;
+
+ dx = f->usr_vector.x;
+ dy = f->usr_vector.y;
+ dx *= stroker->half_line_width;
+ dy *= stroker->half_line_width;
+ cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+ fvector.dx = _cairo_fixed_from_double (dx);
+ fvector.dy = _cairo_fixed_from_double (dy);
+
+ p.x = f->ccw.x + fvector.dx;
+ p.y = f->ccw.y + fvector.dy;
+ contour_add_point (stroker, c, &p);
+
+ p.x = f->cw.x + fvector.dx;
+ p.y = f->cw.y + fvector.dy;
+ contour_add_point (stroker, c, &p);
+ }
+
+ case CAIRO_LINE_CAP_BUTT:
+ default:
+ break;
+ }
+ contour_add_point (stroker, c, &f->cw);
+}
+
+static void
+add_leading_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face,
+ struct stroke_contour *c)
+{
+ cairo_stroke_face_t reversed;
+ cairo_point_t t;
+
+ reversed = *face;
+
+ /* The initial cap needs an outward facing vector. Reverse everything */
+ reversed.usr_vector.x = -reversed.usr_vector.x;
+ reversed.usr_vector.y = -reversed.usr_vector.y;
+ reversed.dev_vector.dx = -reversed.dev_vector.dx;
+ reversed.dev_vector.dy = -reversed.dev_vector.dy;
+
+ t = reversed.cw;
+ reversed.cw = reversed.ccw;
+ reversed.ccw = t;
+
+ add_cap (stroker, &reversed, c);
+}
+
+static void
+add_trailing_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face,
+ struct stroke_contour *c)
+{
+ add_cap (stroker, face, c);
+}
+
+static inline double
+normalize_slope (double *dx, double *dy)
+{
+ double dx0 = *dx, dy0 = *dy;
+ double mag;
+
+ assert (dx0 != 0.0 || dy0 != 0.0);
+
+ if (dx0 == 0.0) {
+ *dx = 0.0;
+ if (dy0 > 0.0) {
+ mag = dy0;
+ *dy = 1.0;
+ } else {
+ mag = -dy0;
+ *dy = -1.0;
+ }
+ } else if (dy0 == 0.0) {
+ *dy = 0.0;
+ if (dx0 > 0.0) {
+ mag = dx0;
+ *dx = 1.0;
+ } else {
+ mag = -dx0;
+ *dx = -1.0;
+ }
+ } else {
+ mag = hypot (dx0, dy0);
+ *dx = dx0 / mag;
+ *dy = dy0 / mag;
+ }
+
+ return mag;
+}
+
+static void
+compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ struct stroker *stroker,
+ cairo_stroke_face_t *face)
+{
+ double face_dx, face_dy;
+ cairo_point_t offset_ccw, offset_cw;
+ double slope_dx, slope_dy;
+
+ slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+ slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+ face->length = normalize_slope (&slope_dx, &slope_dy);
+ face->dev_slope.x = slope_dx;
+ face->dev_slope.y = slope_dy;
+
+ /*
+ * rotate to get a line_width/2 vector along the face, note that
+ * the vector must be rotated the right direction in device space,
+ * but by 90° in user space. So, the rotation depends on
+ * whether the ctm reflects or not, and that can be determined
+ * by looking at the determinant of the matrix.
+ */
+ if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
+ /* Normalize the matrix! */
+ cairo_matrix_transform_distance (stroker->ctm_inverse,
+ &slope_dx, &slope_dy);
+ normalize_slope (&slope_dx, &slope_dy);
+
+ if (stroker->ctm_det_positive) {
+ face_dx = - slope_dy * stroker->half_line_width;
+ face_dy = slope_dx * stroker->half_line_width;
+ } else {
+ face_dx = slope_dy * stroker->half_line_width;
+ face_dy = - slope_dx * stroker->half_line_width;
+ }
+
+ /* back to device space */
+ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+ } else {
+ face_dx = - slope_dy * stroker->half_line_width;
+ face_dy = slope_dx * stroker->half_line_width;
+ }
+
+ offset_ccw.x = _cairo_fixed_from_double (face_dx);
+ offset_ccw.y = _cairo_fixed_from_double (face_dy);
+ offset_cw.x = -offset_ccw.x;
+ offset_cw.y = -offset_ccw.y;
+
+ face->ccw = *point;
+ translate_point (&face->ccw, &offset_ccw);
+
+ face->point = *point;
+
+ face->cw = *point;
+ translate_point (&face->cw, &offset_cw);
+
+ face->usr_vector.x = slope_dx;
+ face->usr_vector.y = slope_dy;
+
+ face->dev_vector = *dev_slope;
+}
+
+static void
+add_caps (struct stroker *stroker)
+{
+ /* check for a degenerative sub_path */
+ if (stroker->has_initial_sub_path &&
+ ! stroker->has_first_face &&
+ ! stroker->has_current_face &&
+ stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+ {
+ /* pick an arbitrary slope to use */
+ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+ cairo_stroke_face_t face;
+
+ /* arbitrarily choose first_point */
+ compute_face (&stroker->first_point, &slope, stroker, &face);
+
+ add_leading_cap (stroker, &face, &stroker->ccw);
+ add_trailing_cap (stroker, &face, &stroker->ccw);
+
+ /* ensure the circle is complete */
+ _cairo_contour_add_point (&stroker->ccw.contour,
+ _cairo_contour_first_point (&stroker->ccw.contour));
+
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
+ } else {
+ if (stroker->has_current_face)
+ add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
+
+#if DEBUG
+ {
+ FILE *file = fopen ("contours.txt", "a");
+ _cairo_debug_print_contour (file, &stroker->path);
+ _cairo_debug_print_contour (file, &stroker->cw.contour);
+ _cairo_debug_print_contour (file, &stroker->ccw.contour);
+ fclose (file);
+ _cairo_contour_reset (&stroker->path);
+ }
+#endif
+
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
+
+ if (stroker->has_first_face) {
+ _cairo_contour_add_point (&stroker->ccw.contour,
+ &stroker->first_face.cw);
+ add_leading_cap (stroker, &stroker->first_face, &stroker->ccw);
+#if DEBUG
+ {
+ FILE *file = fopen ("contours.txt", "a");
+ _cairo_debug_print_contour (file, &stroker->ccw.contour);
+ fclose (file);
+ }
+#endif
+
+ _cairo_polygon_add_contour (stroker->polygon,
+ &stroker->ccw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
+ }
+
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+ _cairo_contour_reset (&stroker->cw.contour);
+ }
+}
+
+static cairo_status_t
+close_path (void *closure);
+
+static cairo_status_t
+move_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+
+ /* Cap the start and end of the previous sub path as needed */
+ add_caps (stroker);
+
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ stroker->first_point = *point;
+
+#if DEBUG
+ _cairo_contour_add_point (&stroker->path, point);
+#endif
+
+ stroker->current_face.point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+line_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+ cairo_stroke_face_t start;
+ cairo_point_t *p1 = &stroker->current_face.point;
+ cairo_slope_t dev_slope;
+
+ stroker->has_initial_sub_path = TRUE;
+
+ if (p1->x == point->x && p1->y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG
+ _cairo_contour_add_point (&stroker->path, point);
+#endif
+
+ _cairo_slope_init (&dev_slope, p1, point);
+ compute_face (p1, &dev_slope, stroker, &start);
+
+ if (stroker->has_current_face) {
+ int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector,
+ &start.dev_vector);
+ if (clockwise) {
+ clockwise = clockwise < 0;
+ /* Join with final face from previous segment */
+ if (! within_tolerance (&stroker->current_face.ccw, &start.ccw,
+ stroker->contour_tolerance) ||
+ ! within_tolerance (&stroker->current_face.cw, &start.cw,
+ stroker->contour_tolerance))
+ {
+ outer_join (stroker, &stroker->current_face, &start, clockwise);
+ inner_join (stroker, &stroker->current_face, &start, clockwise);
+ }
+ }
+ } else {
+ if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = start;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->has_current_face = TRUE;
+
+ contour_add_point (stroker, &stroker->cw, &start.cw);
+ contour_add_point (stroker, &stroker->ccw, &start.ccw);
+ }
+
+ stroker->current_face = start;
+ stroker->current_face.point = *point;
+ stroker->current_face.ccw.x += dev_slope.dx;
+ stroker->current_face.ccw.y += dev_slope.dy;
+ stroker->current_face.cw.x += dev_slope.dx;
+ stroker->current_face.cw.y += dev_slope.dy;
+
+ contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+ contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent)
+{
+ struct stroker *stroker = closure;
+ cairo_stroke_face_t face;
+
+#if DEBUG
+ _cairo_contour_add_point (&stroker->path, point);
+#endif
+ if ((tangent->dx | tangent->dy) == 0) {
+ const cairo_point_t *inpt, *outpt;
+ struct stroke_contour *outer;
+ cairo_point_t t;
+ int clockwise;
+
+ face = stroker->current_face;
+
+ face.usr_vector.x = -face.usr_vector.x;
+ face.usr_vector.y = -face.usr_vector.y;
+ face.dev_vector.dx = -face.dev_vector.dx;
+ face.dev_vector.dy = -face.dev_vector.dy;
+
+ t = face.cw;
+ face.cw = face.ccw;
+ face.ccw = t;
+
+ clockwise = join_is_clockwise (&stroker->current_face, &face);
+ if (clockwise) {
+ inpt = &stroker->current_face.cw;
+ outpt = &face.cw;
+ outer = &stroker->cw;
+ } else {
+ inpt = &stroker->current_face.ccw;
+ outpt = &face.ccw;
+ outer = &stroker->ccw;
+ }
+
+ add_fan (stroker,
+ &stroker->current_face.dev_vector,
+ &face.dev_vector,
+ &stroker->current_face.point,
+ clockwise, outer);
+ } else {
+ compute_face (point, tangent, stroker, &face);
+
+ if ((face.dev_slope.x * stroker->current_face.dev_slope.x +
+ face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance)
+ {
+ const cairo_point_t *inpt, *outpt;
+ struct stroke_contour *outer;
+ int clockwise = join_is_clockwise (&stroker->current_face, &face);
+
+ stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x;
+ stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y;
+ contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+
+ stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x;
+ stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y;
+ contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
+
+ if (clockwise) {
+ inpt = &stroker->current_face.cw;
+ outpt = &face.cw;
+ outer = &stroker->cw;
+ } else {
+ inpt = &stroker->current_face.ccw;
+ outpt = &face.ccw;
+ outer = &stroker->ccw;
+ }
+ add_fan (stroker,
+ &stroker->current_face.dev_vector,
+ &face.dev_vector,
+ &stroker->current_face.point,
+ clockwise, outer);
+ }
+
+ contour_add_point (stroker, &stroker->cw, &face.cw);
+ contour_add_point (stroker, &stroker->ccw, &face.ccw);
+ }
+
+ stroker->current_face = face;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ struct stroker *stroker = closure;
+ cairo_spline_t spline;
+ cairo_stroke_face_t face;
+
+ if (stroker->has_bounds &&
+ ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
+ &stroker->bounds))
+ return line_to (closure, d);
+
+ if (! _cairo_spline_init (&spline, spline_to, stroker,
+ &stroker->current_face.point, b, c, d))
+ return line_to (closure, d);
+
+ compute_face (&stroker->current_face.point, &spline.initial_slope,
+ stroker, &face);
+
+ if (stroker->has_current_face) {
+ int clockwise = join_is_clockwise (&stroker->current_face, &face);
+ /* Join with final face from previous segment */
+ outer_join (stroker, &stroker->current_face, &face, clockwise);
+ inner_join (stroker, &stroker->current_face, &face, clockwise);
+ } else {
+ if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = face;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->has_current_face = TRUE;
+
+ contour_add_point (stroker, &stroker->cw, &face.cw);
+ contour_add_point (stroker, &stroker->ccw, &face.ccw);
+ }
+ stroker->current_face = face;
+
+ return _cairo_spline_decompose (&spline, stroker->tolerance);
+}
+
+static cairo_status_t
+close_path (void *closure)
+{
+ struct stroker *stroker = closure;
+ cairo_status_t status;
+
+ status = line_to (stroker, &stroker->first_point);
+ if (unlikely (status))
+ return status;
+
+ if (stroker->has_first_face && stroker->has_current_face) {
+ /* Join first and final faces of sub path */
+ outer_close (stroker, &stroker->current_face, &stroker->first_face);
+ inner_close (stroker, &stroker->current_face, &stroker->first_face);
+#if 0
+ *_cairo_contour_first_point (&stroker->ccw.contour) =
+ *_cairo_contour_last_point (&stroker->ccw.contour);
+
+ *_cairo_contour_first_point (&stroker->cw.contour) =
+ *_cairo_contour_last_point (&stroker->cw.contour);
+#endif
+
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+
+#if DEBUG
+ {
+ FILE *file = fopen ("contours.txt", "a");
+ _cairo_debug_print_contour (file, &stroker->path);
+ _cairo_debug_print_contour (file, &stroker->cw.contour);
+ _cairo_debug_print_contour (file, &stroker->ccw.contour);
+ fclose (file);
+
+ _cairo_contour_reset (&stroker->path);
+ }
+#endif
+ _cairo_contour_reset (&stroker->cw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
+ } else {
+ /* Cap the start and end of the sub path as needed */
+ add_caps (stroker);
+ }
+
+ stroker->has_initial_sub_path = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon)
+{
+ struct stroker stroker;
+ cairo_status_t status;
+
+ if (style->num_dashes) {
+ return _cairo_path_fixed_stroke_dashed_to_polygon (path,
+ style,
+ ctm,
+ ctm_inverse,
+ tolerance,
+ polygon);
+ }
+
+ stroker.has_bounds = polygon->num_limits;
+ if (stroker.has_bounds) {
+ /* Extend the bounds in each direction to account for the maximum area
+ * we might generate trapezoids, to capture line segments that are
+ * outside of the bounds but which might generate rendering that's
+ * within bounds.
+ */
+ double dx, dy;
+ cairo_fixed_t fdx, fdy;
+ int i;
+
+ stroker.bounds = polygon->limits[0];
+ for (i = 1; i < polygon->num_limits; i++)
+ _cairo_box_add_box (&stroker.bounds, &polygon->limits[i]);
+
+ _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
+ fdx = _cairo_fixed_from_double (dx);
+ fdy = _cairo_fixed_from_double (dy);
+
+ stroker.bounds.p1.x -= fdx;
+ stroker.bounds.p2.x += fdx;
+ stroker.bounds.p1.y -= fdy;
+ stroker.bounds.p2.y += fdy;
+ }
+
+ stroker.style = *style;
+ stroker.ctm = ctm;
+ stroker.ctm_inverse = ctm_inverse;
+ stroker.tolerance = tolerance;
+ stroker.half_line_width = style->line_width / 2.;
+ /* To test whether we need to join two segments of a spline using
+ * a round-join or a bevel-join, we can inspect the angle between the
+ * two segments. If the difference between the chord distance
+ * (half-line-width times the cosine of the bisection angle) and the
+ * half-line-width itself is greater than tolerance then we need to
+ * inject a point.
+ */
+ stroker.spline_cusp_tolerance = 1 - tolerance / stroker.half_line_width;
+ stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance;
+ stroker.spline_cusp_tolerance *= 2;
+ stroker.spline_cusp_tolerance -= 1;
+ stroker.ctm_det_positive =
+ _cairo_matrix_compute_determinant (ctm) >= 0.0;
+
+ stroker.pen.num_vertices = 0;
+ if (path->has_curve_to ||
+ style->line_join == CAIRO_LINE_JOIN_ROUND ||
+ style->line_cap == CAIRO_LINE_CAP_ROUND) {
+ status = _cairo_pen_init (&stroker.pen,
+ stroker.half_line_width,
+ tolerance, ctm);
+ if (unlikely (status))
+ return status;
+
+ /* If the line width is so small that the pen is reduced to a
+ single point, then we have nothing to do. */
+ if (stroker.pen.num_vertices <= 1) {
+ if (stroker.pen.num_vertices)
+ _cairo_pen_fini (&stroker.pen);
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ stroker.has_current_face = FALSE;
+ stroker.has_first_face = FALSE;
+ stroker.has_initial_sub_path = FALSE;
+
+#if DEBUG
+ remove ("contours.txt");
+ remove ("polygons.txt");
+ _cairo_contour_init (&stroker.path, 0);
+#endif
+ _cairo_contour_init (&stroker.cw.contour, 1);
+ _cairo_contour_init (&stroker.ccw.contour, -1);
+ tolerance *= CAIRO_FIXED_ONE;
+ tolerance *= tolerance;
+ stroker.contour_tolerance = tolerance;
+ stroker.polygon = polygon;
+
+ status = _cairo_path_fixed_interpret (path,
+ move_to,
+ line_to,
+ curve_to,
+ close_path,
+ &stroker);
+ /* Cap the start and end of the final sub path as needed */
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ add_caps (&stroker);
+
+ _cairo_contour_fini (&stroker.cw.contour);
+ _cairo_contour_fini (&stroker.ccw.contour);
+ if (stroker.pen.num_vertices)
+ _cairo_pen_fini (&stroker.pen);
+
+#if DEBUG
+ {
+ FILE *file = fopen ("polygons.txt", "a");
+ _cairo_debug_print_polygon (file, polygon);
+ fclose (file);
+ }
+#endif
+
+ return status;
+}
diff --git a/src/cairo-path-stroke-traps.c b/src/cairo-path-stroke-traps.c
new file mode 100755
index 000000000..f95321491
--- /dev/null
+++ b/src/cairo-path-stroke-traps.c
@@ -0,0 +1,1127 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2013 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-inline.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+#include "cairo-traps-private.h"
+
+#include <float.h>
+
+struct stroker {
+ const cairo_stroke_style_t *style;
+
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
+ double spline_cusp_tolerance;
+ double half_line_width;
+ double tolerance;
+ double ctm_determinant;
+ cairo_bool_t ctm_det_positive;
+ cairo_line_join_t line_join;
+
+ cairo_traps_t *traps;
+
+ cairo_pen_t pen;
+
+ cairo_point_t first_point;
+
+ cairo_bool_t has_initial_sub_path;
+
+ cairo_bool_t has_current_face;
+ cairo_stroke_face_t current_face;
+
+ cairo_bool_t has_first_face;
+ cairo_stroke_face_t first_face;
+
+ cairo_stroker_dash_t dash;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t tight_bounds;
+ cairo_box_t line_bounds;
+ cairo_box_t join_bounds;
+};
+
+static cairo_status_t
+stroker_init (struct stroker *stroker,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps)
+{
+ cairo_status_t status;
+
+ stroker->style = style;
+ stroker->ctm = ctm;
+ stroker->ctm_inverse = NULL;
+ if (! _cairo_matrix_is_identity (ctm_inverse))
+ stroker->ctm_inverse = ctm_inverse;
+ stroker->line_join = style->line_join;
+ stroker->half_line_width = style->line_width / 2.0;
+ stroker->tolerance = tolerance;
+ stroker->traps = traps;
+
+ /* To test whether we need to join two segments of a spline using
+ * a round-join or a bevel-join, we can inspect the angle between the
+ * two segments. If the difference between the chord distance
+ * (half-line-width times the cosine of the bisection angle) and the
+ * half-line-width itself is greater than tolerance then we need to
+ * inject a point.
+ */
+ stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width;
+ stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance;
+ stroker->spline_cusp_tolerance *= 2;
+ stroker->spline_cusp_tolerance -= 1;
+
+ stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
+ stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
+
+ status = _cairo_pen_init (&stroker->pen,
+ stroker->half_line_width,
+ tolerance, ctm);
+ if (unlikely (status))
+ return status;
+
+ stroker->has_current_face = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ _cairo_stroker_dash_init (&stroker->dash, style);
+
+ stroker->has_bounds = traps->num_limits;
+ if (stroker->has_bounds) {
+ /* Extend the bounds in each direction to account for the maximum area
+ * we might generate trapezoids, to capture line segments that are outside
+ * of the bounds but which might generate rendering that's within bounds.
+ */
+ double dx, dy;
+ cairo_fixed_t fdx, fdy;
+
+ stroker->tight_bounds = traps->bounds;
+
+ _cairo_stroke_style_max_distance_from_path (stroker->style, path,
+ stroker->ctm, &dx, &dy);
+
+ _cairo_stroke_style_max_line_distance_from_path (stroker->style, path,
+ stroker->ctm, &dx, &dy);
+
+ fdx = _cairo_fixed_from_double (dx);
+ fdy = _cairo_fixed_from_double (dy);
+
+ stroker->line_bounds = stroker->tight_bounds;
+ stroker->line_bounds.p1.x -= fdx;
+ stroker->line_bounds.p2.x += fdx;
+ stroker->line_bounds.p1.y -= fdy;
+ stroker->line_bounds.p2.y += fdy;
+
+ _cairo_stroke_style_max_join_distance_from_path (stroker->style, path,
+ stroker->ctm, &dx, &dy);
+
+ fdx = _cairo_fixed_from_double (dx);
+ fdy = _cairo_fixed_from_double (dy);
+
+ stroker->join_bounds = stroker->tight_bounds;
+ stroker->join_bounds.p1.x -= fdx;
+ stroker->join_bounds.p2.x += fdx;
+ stroker->join_bounds.p1.y -= fdy;
+ stroker->join_bounds.p2.y += fdy;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+stroker_fini (struct stroker *stroker)
+{
+ _cairo_pen_fini (&stroker->pen);
+}
+
+static void
+translate_point (cairo_point_t *point, cairo_point_t *offset)
+{
+ point->x += offset->x;
+ point->y += offset->y;
+}
+
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
+}
+
+static int
+slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+ double c = dx1 * dy2 - dx2 * dy1;
+ if (c > 0) return 1;
+ if (c < 0) return -1;
+ return 0;
+}
+
+static cairo_bool_t
+stroker_intersects_join (const struct stroker *stroker,
+ const cairo_point_t *in,
+ const cairo_point_t *out)
+{
+ cairo_line_t segment;
+
+ if (! stroker->has_bounds)
+ return TRUE;
+
+ segment.p1 = *in;
+ segment.p2 = *out;
+ return _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment);
+}
+
+static void
+join (struct stroker *stroker,
+ cairo_stroke_face_t *in,
+ cairo_stroke_face_t *out)
+{
+ int clockwise = join_is_clockwise (out, in);
+ cairo_point_t *inpt, *outpt;
+
+ if (in->cw.x == out->cw.x &&
+ in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x &&
+ in->ccw.y == out->ccw.y)
+ {
+ return;
+ }
+
+ if (clockwise) {
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ } else {
+ inpt = &in->cw;
+ outpt = &out->cw;
+ }
+
+ if (! stroker_intersects_join (stroker, inpt, outpt))
+ return;
+
+ switch (stroker->line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ if ((in->dev_slope.x * out->dev_slope.x +
+ in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance)
+ {
+ int start, stop;
+ cairo_point_t tri[3];
+ cairo_pen_t *pen = &stroker->pen;
+
+ tri[0] = in->point;
+ tri[1] = *inpt;
+ if (clockwise) {
+ _cairo_pen_find_active_ccw_vertices (pen,
+ &in->dev_vector, &out->dev_vector,
+ &start, &stop);
+ while (start != stop) {
+ tri[2] = in->point;
+ translate_point (&tri[2], &pen->vertices[start].point);
+ _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ tri[1] = tri[2];
+
+ if (start-- == 0)
+ start += pen->num_vertices;
+ }
+ } else {
+ _cairo_pen_find_active_cw_vertices (pen,
+ &in->dev_vector, &out->dev_vector,
+ &start, &stop);
+ while (start != stop) {
+ tri[2] = in->point;
+ translate_point (&tri[2], &pen->vertices[start].point);
+ _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ tri[1] = tri[2];
+
+ if (++start == pen->num_vertices)
+ start = 0;
+ }
+ }
+ tri[2] = *outpt;
+ _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ break;
+ }
+
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = (-in->usr_vector.x * out->usr_vector.x +
+ -in->usr_vector.y * out->usr_vector.y);
+ double ml = stroker->style->miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 - in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ cairo_point_t outer;
+ cairo_point_t quad[4];
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->usr_vector.x;
+ dy1 = in->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->usr_vector.x;
+ dy2 = out->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ /*
+ * Draw the quadrilateral
+ */
+ outer.x = _cairo_fixed_from_double (mx);
+ outer.y = _cairo_fixed_from_double (my);
+
+ quad[0] = in->point;
+ quad[1] = *inpt;
+ quad[2] = outer;
+ quad[3] = *outpt;
+
+ _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+ break;
+ }
+ }
+ /* fall through ... */
+ }
+
+ case CAIRO_LINE_JOIN_BEVEL: {
+ cairo_point_t tri[3];
+ tri[0] = in->point;
+ tri[1] = *inpt;
+ tri[2] = *outpt;
+
+ _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ break;
+ }
+ }
+}
+
+static void
+add_cap (struct stroker *stroker, cairo_stroke_face_t *f)
+{
+ switch (stroker->style->line_cap) {
+ case CAIRO_LINE_CAP_ROUND: {
+ int start, stop;
+ cairo_slope_t in_slope, out_slope;
+ cairo_point_t tri[3];
+ cairo_pen_t *pen = &stroker->pen;
+
+ in_slope = f->dev_vector;
+ out_slope.dx = -in_slope.dx;
+ out_slope.dy = -in_slope.dy;
+ _cairo_pen_find_active_cw_vertices (pen, &in_slope, &out_slope,
+ &start, &stop);
+ tri[0] = f->point;
+ tri[1] = f->cw;
+ while (start != stop) {
+ tri[2] = f->point;
+ translate_point (&tri[2], &pen->vertices[start].point);
+ _cairo_traps_tessellate_triangle (stroker->traps, tri);
+
+ tri[1] = tri[2];
+ if (++start == pen->num_vertices)
+ start = 0;
+ }
+ tri[2] = f->ccw;
+ _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ break;
+ }
+
+ case CAIRO_LINE_CAP_SQUARE: {
+ double dx, dy;
+ cairo_slope_t fvector;
+ cairo_point_t quad[4];
+
+ dx = f->usr_vector.x;
+ dy = f->usr_vector.y;
+ dx *= stroker->half_line_width;
+ dy *= stroker->half_line_width;
+ cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+ fvector.dx = _cairo_fixed_from_double (dx);
+ fvector.dy = _cairo_fixed_from_double (dy);
+
+ quad[0] = f->cw;
+ quad[1].x = f->cw.x + fvector.dx;
+ quad[1].y = f->cw.y + fvector.dy;
+ quad[2].x = f->ccw.x + fvector.dx;
+ quad[2].y = f->ccw.y + fvector.dy;
+ quad[3] = f->ccw;
+
+ _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+ break;
+ }
+
+ case CAIRO_LINE_CAP_BUTT:
+ default:
+ break;
+ }
+}
+
+static void
+add_leading_cap (struct stroker *stroker,
+ cairo_stroke_face_t *face)
+{
+ cairo_stroke_face_t reversed;
+ cairo_point_t t;
+
+ reversed = *face;
+
+ /* The initial cap needs an outward facing vector. Reverse everything */
+ reversed.usr_vector.x = -reversed.usr_vector.x;
+ reversed.usr_vector.y = -reversed.usr_vector.y;
+ reversed.dev_vector.dx = -reversed.dev_vector.dx;
+ reversed.dev_vector.dy = -reversed.dev_vector.dy;
+ t = reversed.cw;
+ reversed.cw = reversed.ccw;
+ reversed.ccw = t;
+
+ add_cap (stroker, &reversed);
+}
+
+static void
+add_trailing_cap (struct stroker *stroker, cairo_stroke_face_t *face)
+{
+ add_cap (stroker, face);
+}
+
+static inline double
+normalize_slope (double *dx, double *dy)
+{
+ double dx0 = *dx, dy0 = *dy;
+
+ if (dx0 == 0.0 && dy0 == 0.0)
+ return 0;
+
+ if (dx0 == 0.0) {
+ *dx = 0.0;
+ if (dy0 > 0.0) {
+ *dy = 1.0;
+ return dy0;
+ } else {
+ *dy = -1.0;
+ return -dy0;
+ }
+ } else if (dy0 == 0.0) {
+ *dy = 0.0;
+ if (dx0 > 0.0) {
+ *dx = 1.0;
+ return dx0;
+ } else {
+ *dx = -1.0;
+ return -dx0;
+ }
+ } else {
+ double mag = hypot (dx0, dy0);
+ *dx = dx0 / mag;
+ *dy = dy0 / mag;
+ return mag;
+ }
+}
+
+static void
+compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ struct stroker *stroker,
+ cairo_stroke_face_t *face)
+{
+ double face_dx, face_dy;
+ cairo_point_t offset_ccw, offset_cw;
+ double slope_dx, slope_dy;
+
+ slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+ slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+ face->length = normalize_slope (&slope_dx, &slope_dy);
+ face->dev_slope.x = slope_dx;
+ face->dev_slope.y = slope_dy;
+
+ /*
+ * rotate to get a line_width/2 vector along the face, note that
+ * the vector must be rotated the right direction in device space,
+ * but by 90° in user space. So, the rotation depends on
+ * whether the ctm reflects or not, and that can be determined
+ * by looking at the determinant of the matrix.
+ */
+ if (stroker->ctm_inverse) {
+ cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy);
+ normalize_slope (&slope_dx, &slope_dy);
+
+ if (stroker->ctm_det_positive) {
+ face_dx = - slope_dy * stroker->half_line_width;
+ face_dy = slope_dx * stroker->half_line_width;
+ } else {
+ face_dx = slope_dy * stroker->half_line_width;
+ face_dy = - slope_dx * stroker->half_line_width;
+ }
+
+ /* back to device space */
+ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+ } else {
+ face_dx = - slope_dy * stroker->half_line_width;
+ face_dy = slope_dx * stroker->half_line_width;
+ }
+
+ offset_ccw.x = _cairo_fixed_from_double (face_dx);
+ offset_ccw.y = _cairo_fixed_from_double (face_dy);
+ offset_cw.x = -offset_ccw.x;
+ offset_cw.y = -offset_ccw.y;
+
+ face->ccw = *point;
+ translate_point (&face->ccw, &offset_ccw);
+
+ face->point = *point;
+
+ face->cw = *point;
+ translate_point (&face->cw, &offset_cw);
+
+ face->usr_vector.x = slope_dx;
+ face->usr_vector.y = slope_dy;
+
+ face->dev_vector = *dev_slope;
+}
+
+static void
+add_caps (struct stroker *stroker)
+{
+ /* check for a degenerative sub_path */
+ if (stroker->has_initial_sub_path &&
+ !stroker->has_first_face &&
+ !stroker->has_current_face &&
+ stroker->style->line_cap == CAIRO_LINE_CAP_ROUND)
+ {
+ /* pick an arbitrary slope to use */
+ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+ cairo_stroke_face_t face;
+
+ /* arbitrarily choose first_point
+ * first_point and current_point should be the same */
+ compute_face (&stroker->first_point, &slope, stroker, &face);
+
+ add_leading_cap (stroker, &face);
+ add_trailing_cap (stroker, &face);
+ }
+
+ if (stroker->has_first_face)
+ add_leading_cap (stroker, &stroker->first_face);
+
+ if (stroker->has_current_face)
+ add_trailing_cap (stroker, &stroker->current_face);
+}
+
+static cairo_bool_t
+stroker_intersects_edge (const struct stroker *stroker,
+ const cairo_stroke_face_t *start,
+ const cairo_stroke_face_t *end)
+{
+ cairo_box_t box;
+
+ if (! stroker->has_bounds)
+ return TRUE;
+
+ if (_cairo_box_contains_point (&stroker->tight_bounds, &start->cw))
+ return TRUE;
+ box.p2 = box.p1 = start->cw;
+
+ if (_cairo_box_contains_point (&stroker->tight_bounds, &start->ccw))
+ return TRUE;
+ _cairo_box_add_point (&box, &start->ccw);
+
+ if (_cairo_box_contains_point (&stroker->tight_bounds, &end->cw))
+ return TRUE;
+ _cairo_box_add_point (&box, &end->cw);
+
+ if (_cairo_box_contains_point (&stroker->tight_bounds, &end->ccw))
+ return TRUE;
+ _cairo_box_add_point (&box, &end->ccw);
+
+ return (box.p2.x > stroker->tight_bounds.p1.x &&
+ box.p1.x < stroker->tight_bounds.p2.x &&
+ box.p2.y > stroker->tight_bounds.p1.y &&
+ box.p1.y < stroker->tight_bounds.p2.y);
+}
+
+static void
+add_sub_edge (struct stroker *stroker,
+ const cairo_point_t *p1, const cairo_point_t *p2,
+ const cairo_slope_t *dev_slope,
+ cairo_stroke_face_t *start, cairo_stroke_face_t *end)
+{
+ cairo_point_t rectangle[4];
+
+ compute_face (p1, dev_slope, stroker, start);
+
+ *end = *start;
+ end->point = *p2;
+ rectangle[0].x = p2->x - p1->x;
+ rectangle[0].y = p2->y - p1->y;
+ translate_point (&end->ccw, &rectangle[0]);
+ translate_point (&end->cw, &rectangle[0]);
+
+ if (p1->x == p2->x && p1->y == p2->y)
+ return;
+
+ if (! stroker_intersects_edge (stroker, start, end))
+ return;
+
+ rectangle[0] = start->cw;
+ rectangle[1] = start->ccw;
+ rectangle[2] = end->ccw;
+ rectangle[3] = end->cw;
+
+ _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
+}
+
+static cairo_status_t
+move_to (void *closure, const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+
+ /* Cap the start and end of the previous sub path as needed */
+ add_caps (stroker);
+
+ stroker->first_point = *point;
+ stroker->current_face.point = *point;
+
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+move_to_dashed (void *closure, const cairo_point_t *point)
+{
+ /* reset the dash pattern for new sub paths */
+ struct stroker *stroker = closure;
+
+ _cairo_stroker_dash_start (&stroker->dash);
+ return move_to (closure, point);
+}
+
+static cairo_status_t
+line_to (void *closure, const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+ cairo_stroke_face_t start, end;
+ const cairo_point_t *p1 = &stroker->current_face.point;
+ const cairo_point_t *p2 = point;
+ cairo_slope_t dev_slope;
+
+ stroker->has_initial_sub_path = TRUE;
+ memset (&start, 0, sizeof (cairo_stroke_face_t));
+ memset (&end, 0, sizeof (cairo_stroke_face_t));
+
+ if (p1->x == p2->x && p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_slope_init (&dev_slope, p1, p2);
+ add_sub_edge (stroker, p1, p2, &dev_slope, &start, &end);
+
+ if (stroker->has_current_face) {
+ /* Join with final face from previous segment */
+ join (stroker, &stroker->current_face, &start);
+ } else if (!stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = start;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->current_face = end;
+ stroker->has_current_face = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Dashed lines. Cap each dash end, join around turns when on
+ */
+static cairo_status_t
+line_to_dashed (void *closure, const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+ double mag, remain, step_length = 0;
+ double slope_dx, slope_dy;
+ double dx2, dy2;
+ cairo_stroke_face_t sub_start, sub_end;
+ const cairo_point_t *p1 = &stroker->current_face.point;
+ const cairo_point_t *p2 = point;
+ cairo_slope_t dev_slope;
+ cairo_line_t segment;
+ cairo_bool_t fully_in_bounds;
+
+ memset (&sub_start, 0, sizeof (cairo_stroke_face_t));
+ memset (&sub_end, 0, sizeof (cairo_stroke_face_t));
+
+ stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
+
+ if (p1->x == p2->x && p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ fully_in_bounds = TRUE;
+ if (stroker->has_bounds &&
+ (! _cairo_box_contains_point (&stroker->join_bounds, p1) ||
+ ! _cairo_box_contains_point (&stroker->join_bounds, p2)))
+ {
+ fully_in_bounds = FALSE;
+ }
+
+ _cairo_slope_init (&dev_slope, p1, p2);
+
+ slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
+ slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
+
+ if (stroker->ctm_inverse)
+ cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy);
+ mag = normalize_slope (&slope_dx, &slope_dy);
+ if (mag <= DBL_EPSILON)
+ return CAIRO_STATUS_SUCCESS;
+
+ remain = mag;
+ segment.p1 = *p1;
+ while (remain) {
+ step_length = MIN (stroker->dash.dash_remain, remain);
+ remain -= step_length;
+ dx2 = slope_dx * (mag - remain);
+ dy2 = slope_dy * (mag - remain);
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+ segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
+ segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
+
+ if (stroker->dash.dash_on &&
+ (fully_in_bounds ||
+ (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
+ _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment)))
+ {
+ add_sub_edge (stroker,
+ &segment.p1, &segment.p2,
+ &dev_slope,
+ &sub_start, &sub_end);
+
+ if (stroker->has_current_face) {
+ /* Join with final face from previous segment */
+ join (stroker, &stroker->current_face, &sub_start);
+
+ stroker->has_current_face = FALSE;
+ } else if (! stroker->has_first_face && stroker->dash.dash_starts_on) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = sub_start;
+ stroker->has_first_face = TRUE;
+ } else {
+ /* Cap dash start if not connecting to a previous segment */
+ add_leading_cap (stroker, &sub_start);
+ }
+
+ if (remain) {
+ /* Cap dash end if not at end of segment */
+ add_trailing_cap (stroker, &sub_end);
+ } else {
+ stroker->current_face = sub_end;
+ stroker->has_current_face = TRUE;
+ }
+ } else {
+ if (stroker->has_current_face) {
+ /* Cap final face from previous segment */
+ add_trailing_cap (stroker, &stroker->current_face);
+
+ stroker->has_current_face = FALSE;
+ }
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ if (stroker->dash.dash_on && ! stroker->has_current_face) {
+ /* This segment ends on a transition to dash_on, compute a new face
+ * and add cap for the beginning of the next dash_on step.
+ *
+ * Note: this will create a degenerate cap if this is not the last line
+ * in the path. Whether this behaviour is desirable or not is debatable.
+ * On one side these degenerate caps can not be reproduced with regular
+ * path stroking.
+ * On the other hand, Acroread 7 also produces the degenerate caps.
+ */
+ compute_face (point, &dev_slope, stroker, &stroker->current_face);
+
+ add_leading_cap (stroker, &stroker->current_face);
+
+ stroker->has_current_face = TRUE;
+ } else
+ stroker->current_face.point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent)
+{
+ struct stroker *stroker = closure;
+ cairo_stroke_face_t face;
+
+ if ((tangent->dx | tangent->dy) == 0) {
+ cairo_point_t t;
+
+ face = stroker->current_face;
+
+ face.usr_vector.x = -face.usr_vector.x;
+ face.usr_vector.y = -face.usr_vector.y;
+ face.dev_slope.x = -face.dev_slope.x;
+ face.dev_slope.y = -face.dev_slope.y;
+ face.dev_vector.dx = -face.dev_vector.dx;
+ face.dev_vector.dy = -face.dev_vector.dy;
+
+ t = face.cw;
+ face.cw = face.ccw;
+ face.ccw = t;
+
+ join (stroker, &stroker->current_face, &face);
+ } else {
+ cairo_point_t rectangle[4];
+
+ compute_face (&stroker->current_face.point, tangent, stroker, &face);
+
+ join (stroker, &stroker->current_face, &face);
+
+ rectangle[0] = face.cw;
+ rectangle[1] = face.ccw;
+
+ rectangle[2].x = point->x - face.point.x;
+ rectangle[2].y = point->y - face.point.y;
+ face.point = *point;
+ translate_point (&face.ccw, &rectangle[2]);
+ translate_point (&face.cw, &rectangle[2]);
+
+ rectangle[2] = face.ccw;
+ rectangle[3] = face.cw;
+
+ _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
+ }
+
+ stroker->current_face = face;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ struct stroker *stroker = closure;
+ cairo_line_join_t line_join_save;
+ cairo_spline_t spline;
+ cairo_stroke_face_t face;
+ cairo_status_t status;
+
+ if (stroker->has_bounds &&
+ ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
+ &stroker->line_bounds))
+ return line_to (closure, d);
+
+ if (! _cairo_spline_init (&spline, spline_to, stroker,
+ &stroker->current_face.point, b, c, d))
+ return line_to (closure, d);
+
+ compute_face (&stroker->current_face.point, &spline.initial_slope,
+ stroker, &face);
+
+ if (stroker->has_current_face) {
+ /* Join with final face from previous segment */
+ join (stroker, &stroker->current_face, &face);
+ } else {
+ if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = face;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->has_current_face = TRUE;
+ }
+ stroker->current_face = face;
+
+ /* Temporarily modify the stroker to use round joins to guarantee
+ * smooth stroked curves. */
+ line_join_save = stroker->line_join;
+ stroker->line_join = CAIRO_LINE_JOIN_ROUND;
+
+ status = _cairo_spline_decompose (&spline, stroker->tolerance);
+
+ stroker->line_join = line_join_save;
+
+ return status;
+}
+
+static cairo_status_t
+curve_to_dashed (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ struct stroker *stroker = closure;
+ cairo_spline_t spline;
+ cairo_line_join_t line_join_save;
+ cairo_spline_add_point_func_t func;
+ cairo_status_t status;
+
+ func = (cairo_spline_add_point_func_t)line_to_dashed;
+
+ if (stroker->has_bounds &&
+ ! _cairo_spline_intersects (&stroker->current_face.point, b, c, b,
+ &stroker->line_bounds))
+ return func (closure, d, NULL);
+
+ if (! _cairo_spline_init (&spline, func, stroker,
+ &stroker->current_face.point, b, c, d))
+ return func (closure, d, NULL);
+
+ /* Temporarily modify the stroker to use round joins to guarantee
+ * smooth stroked curves. */
+ line_join_save = stroker->line_join;
+ stroker->line_join = CAIRO_LINE_JOIN_ROUND;
+
+ status = _cairo_spline_decompose (&spline, stroker->tolerance);
+
+ stroker->line_join = line_join_save;
+
+ return status;
+}
+
+static cairo_status_t
+_close_path (struct stroker *stroker)
+{
+ if (stroker->has_first_face && stroker->has_current_face) {
+ /* Join first and final faces of sub path */
+ join (stroker, &stroker->current_face, &stroker->first_face);
+ } else {
+ /* Cap the start and end of the sub path as needed */
+ add_caps (stroker);
+ }
+
+ stroker->has_initial_sub_path = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+close_path (void *closure)
+{
+ struct stroker *stroker = closure;
+ cairo_status_t status;
+
+ status = line_to (stroker, &stroker->first_point);
+ if (unlikely (status))
+ return status;
+
+ return _close_path (stroker);
+}
+
+static cairo_status_t
+close_path_dashed (void *closure)
+{
+ struct stroker *stroker = closure;
+ cairo_status_t status;
+
+ status = line_to_dashed (stroker, &stroker->first_point);
+ if (unlikely (status))
+ return status;
+
+ return _close_path (stroker);
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps)
+{
+ struct stroker stroker;
+ cairo_status_t status;
+
+ status = stroker_init (&stroker, path, style,
+ ctm, ctm_inverse, tolerance,
+ traps);
+ if (unlikely (status))
+ return status;
+
+ if (stroker.dash.dashed)
+ status = _cairo_path_fixed_interpret (path,
+ move_to_dashed,
+ line_to_dashed,
+ curve_to_dashed,
+ close_path_dashed,
+ &stroker);
+ else
+ status = _cairo_path_fixed_interpret (path,
+ move_to,
+ line_to,
+ curve_to,
+ close_path,
+ &stroker);
+ assert(status == CAIRO_STATUS_SUCCESS);
+ add_caps (&stroker);
+
+ stroker_fini (&stroker);
+
+ return traps->status;
+}
diff --git a/src/cairo-path-stroke-tristrip.c b/src/cairo-path-stroke-tristrip.c
new file mode 100755
index 000000000..3c232986b
--- /dev/null
+++ b/src/cairo-path-stroke-tristrip.c
@@ -0,0 +1,1091 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-inline.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-tristrip-private.h"
+
+struct stroker {
+ cairo_stroke_style_t style;
+
+ cairo_tristrip_t *strip;
+
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
+ double tolerance;
+ cairo_bool_t ctm_det_positive;
+
+ cairo_pen_t pen;
+
+ cairo_bool_t has_sub_path;
+
+ cairo_point_t first_point;
+
+ cairo_bool_t has_current_face;
+ cairo_stroke_face_t current_face;
+
+ cairo_bool_t has_first_face;
+ cairo_stroke_face_t first_face;
+
+ cairo_box_t limit;
+ cairo_bool_t has_limits;
+};
+
+static inline double
+normalize_slope (double *dx, double *dy);
+
+static void
+compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ struct stroker *stroker,
+ cairo_stroke_face_t *face);
+
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+ point->x += offset->x;
+ point->y += offset->y;
+}
+
+static int
+slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+ double c = (dx1 * dy2 - dx2 * dy1);
+
+ if (c > 0) return 1;
+ if (c < 0) return -1;
+ return 0;
+}
+
+static inline int
+range_step (int i, int step, int max)
+{
+ i += step;
+ if (i < 0)
+ i = max - 1;
+ if (i >= max)
+ i = 0;
+ return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static void
+add_fan (struct stroker *stroker,
+ const cairo_slope_t *in_vector,
+ const cairo_slope_t *out_vector,
+ const cairo_point_t *midpt,
+ const cairo_point_t *inpt,
+ const cairo_point_t *outpt,
+ cairo_bool_t clockwise)
+{
+ int start, stop, step, i, npoints;
+
+ if (clockwise) {
+ step = 1;
+
+ start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+ in_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
+ in_vector) < 0)
+ start = range_step (start, 1, stroker->pen.num_vertices);
+
+ stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+ out_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+ out_vector) > 0)
+ {
+ stop = range_step (stop, -1, stroker->pen.num_vertices);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+ in_vector) < 0)
+ return;
+ }
+
+ npoints = stop - start;
+ } else {
+ step = -1;
+
+ start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ in_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+ in_vector) < 0)
+ start = range_step (start, -1, stroker->pen.num_vertices);
+
+ stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ out_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+ out_vector) > 0)
+ {
+ stop = range_step (stop, 1, stroker->pen.num_vertices);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+ in_vector) < 0)
+ return;
+ }
+
+ npoints = start - stop;
+ }
+ stop = range_step (stop, step, stroker->pen.num_vertices);
+ if (npoints < 0)
+ npoints += stroker->pen.num_vertices;
+ if (npoints <= 1)
+ return;
+
+ for (i = start;
+ i != stop;
+ i = range_step (i, step, stroker->pen.num_vertices))
+ {
+ cairo_point_t p = *midpt;
+ translate_point (&p, &stroker->pen.vertices[i].point);
+ //contour_add_point (stroker, c, &p);
+ }
+}
+
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
+}
+
+static void
+inner_join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out,
+ int clockwise)
+{
+ const cairo_point_t *outpt;
+
+ if (clockwise) {
+ outpt = &out->ccw;
+ } else {
+ outpt = &out->cw;
+ }
+ //contour_add_point (stroker, inner, &in->point);
+ //contour_add_point (stroker, inner, outpt);
+}
+
+static void
+inner_close (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ cairo_stroke_face_t *out)
+{
+ const cairo_point_t *inpt;
+
+ if (join_is_clockwise (in, out)) {
+ inpt = &out->ccw;
+ } else {
+ inpt = &out->cw;
+ }
+
+ //contour_add_point (stroker, inner, &in->point);
+ //contour_add_point (stroker, inner, inpt);
+ //*_cairo_contour_first_point (&inner->contour) =
+ //*_cairo_contour_last_point (&inner->contour);
+}
+
+static void
+outer_close (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ const cairo_point_t *inpt, *outpt;
+ int clockwise;
+
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+ {
+ return;
+ }
+ clockwise = join_is_clockwise (in, out);
+ if (clockwise) {
+ inpt = &in->cw;
+ outpt = &out->cw;
+ } else {
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ }
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ add_fan (stroker,
+ &in->dev_vector,
+ &out->dev_vector,
+ &in->point, inpt, outpt,
+ clockwise);
+ break;
+
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+ -in->usr_vector.y * out->usr_vector.y;
+ double ml = stroker->style.miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 - in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->usr_vector.x;
+ dy1 = in->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->usr_vector.x;
+ dy2 = out->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ cairo_point_t p;
+
+ p.x = _cairo_fixed_from_double (mx);
+ p.y = _cairo_fixed_from_double (my);
+
+ //*_cairo_contour_last_point (&outer->contour) = p;
+ //*_cairo_contour_first_point (&outer->contour) = p;
+ return;
+ }
+ }
+ break;
+ }
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ break;
+ }
+ //contour_add_point (stroker, outer, outpt);
+}
+
+static void
+outer_join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out,
+ int clockwise)
+{
+ const cairo_point_t *inpt, *outpt;
+
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+ {
+ return;
+ }
+ if (clockwise) {
+ inpt = &in->cw;
+ outpt = &out->cw;
+ } else {
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ }
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ add_fan (stroker,
+ &in->dev_vector,
+ &out->dev_vector,
+ &in->point, inpt, outpt,
+ clockwise);
+ break;
+
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+ -in->usr_vector.y * out->usr_vector.y;
+ double ml = stroker->style.miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 - in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->usr_vector.x;
+ dy1 = in->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->usr_vector.x;
+ dy2 = out->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ cairo_point_t p;
+
+ p.x = _cairo_fixed_from_double (mx);
+ p.y = _cairo_fixed_from_double (my);
+
+ //*_cairo_contour_last_point (&outer->contour) = p;
+ return;
+ }
+ }
+ break;
+ }
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ break;
+ }
+ //contour_add_point (stroker,outer, outpt);
+}
+
+static void
+add_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *f)
+{
+ switch (stroker->style.line_cap) {
+ case CAIRO_LINE_CAP_ROUND: {
+ cairo_slope_t slope;
+
+ slope.dx = -f->dev_vector.dx;
+ slope.dy = -f->dev_vector.dy;
+
+ add_fan (stroker, &f->dev_vector, &slope,
+ &f->point, &f->ccw, &f->cw,
+ FALSE);
+ break;
+ }
+
+ case CAIRO_LINE_CAP_SQUARE: {
+ double dx, dy;
+ cairo_slope_t fvector;
+ cairo_point_t quad[4];
+
+ dx = f->usr_vector.x;
+ dy = f->usr_vector.y;
+ dx *= stroker->style.line_width / 2.0;
+ dy *= stroker->style.line_width / 2.0;
+ cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+ fvector.dx = _cairo_fixed_from_double (dx);
+ fvector.dy = _cairo_fixed_from_double (dy);
+
+ quad[0] = f->ccw;
+ quad[1].x = f->ccw.x + fvector.dx;
+ quad[1].y = f->ccw.y + fvector.dy;
+ quad[2].x = f->cw.x + fvector.dx;
+ quad[2].y = f->cw.y + fvector.dy;
+ quad[3] = f->cw;
+
+ //contour_add_point (stroker, c, &quad[1]);
+ //contour_add_point (stroker, c, &quad[2]);
+ }
+
+ case CAIRO_LINE_CAP_BUTT:
+ default:
+ break;
+ }
+ //contour_add_point (stroker, c, &f->cw);
+}
+
+static void
+add_leading_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face)
+{
+ cairo_stroke_face_t reversed;
+ cairo_point_t t;
+
+ reversed = *face;
+
+ /* The initial cap needs an outward facing vector. Reverse everything */
+ reversed.usr_vector.x = -reversed.usr_vector.x;
+ reversed.usr_vector.y = -reversed.usr_vector.y;
+ reversed.dev_vector.dx = -reversed.dev_vector.dx;
+ reversed.dev_vector.dy = -reversed.dev_vector.dy;
+
+ t = reversed.cw;
+ reversed.cw = reversed.ccw;
+ reversed.ccw = t;
+
+ add_cap (stroker, &reversed);
+}
+
+static void
+add_trailing_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face)
+{
+ add_cap (stroker, face);
+}
+
+static inline double
+normalize_slope (double *dx, double *dy)
+{
+ double dx0 = *dx, dy0 = *dy;
+ double mag;
+
+ assert (dx0 != 0.0 || dy0 != 0.0);
+
+ if (dx0 == 0.0) {
+ *dx = 0.0;
+ if (dy0 > 0.0) {
+ mag = dy0;
+ *dy = 1.0;
+ } else {
+ mag = -dy0;
+ *dy = -1.0;
+ }
+ } else if (dy0 == 0.0) {
+ *dy = 0.0;
+ if (dx0 > 0.0) {
+ mag = dx0;
+ *dx = 1.0;
+ } else {
+ mag = -dx0;
+ *dx = -1.0;
+ }
+ } else {
+ mag = hypot (dx0, dy0);
+ *dx = dx0 / mag;
+ *dy = dy0 / mag;
+ }
+
+ return mag;
+}
+
+static void
+compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ struct stroker *stroker,
+ cairo_stroke_face_t *face)
+{
+ double face_dx, face_dy;
+ cairo_point_t offset_ccw, offset_cw;
+ double slope_dx, slope_dy;
+
+ slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+ slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+ face->length = normalize_slope (&slope_dx, &slope_dy);
+ face->dev_slope.x = slope_dx;
+ face->dev_slope.y = slope_dy;
+
+ /*
+ * rotate to get a line_width/2 vector along the face, note that
+ * the vector must be rotated the right direction in device space,
+ * but by 90° in user space. So, the rotation depends on
+ * whether the ctm reflects or not, and that can be determined
+ * by looking at the determinant of the matrix.
+ */
+ if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
+ /* Normalize the matrix! */
+ cairo_matrix_transform_distance (stroker->ctm_inverse,
+ &slope_dx, &slope_dy);
+ normalize_slope (&slope_dx, &slope_dy);
+
+ if (stroker->ctm_det_positive) {
+ face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = slope_dx * (stroker->style.line_width / 2.0);
+ } else {
+ face_dx = slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+ }
+
+ /* back to device space */
+ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+ } else {
+ face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = slope_dx * (stroker->style.line_width / 2.0);
+ }
+
+ offset_ccw.x = _cairo_fixed_from_double (face_dx);
+ offset_ccw.y = _cairo_fixed_from_double (face_dy);
+ offset_cw.x = -offset_ccw.x;
+ offset_cw.y = -offset_ccw.y;
+
+ face->ccw = *point;
+ translate_point (&face->ccw, &offset_ccw);
+
+ face->point = *point;
+
+ face->cw = *point;
+ translate_point (&face->cw, &offset_cw);
+
+ face->usr_vector.x = slope_dx;
+ face->usr_vector.y = slope_dy;
+
+ face->dev_vector = *dev_slope;
+}
+
+static void
+add_caps (struct stroker *stroker)
+{
+ /* check for a degenerative sub_path */
+ if (stroker->has_sub_path &&
+ ! stroker->has_first_face &&
+ ! stroker->has_current_face &&
+ stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+ {
+ /* pick an arbitrary slope to use */
+ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+ cairo_stroke_face_t face;
+
+ /* arbitrarily choose first_point */
+ compute_face (&stroker->first_point, &slope, stroker, &face);
+
+ add_leading_cap (stroker, &face);
+ add_trailing_cap (stroker, &face);
+
+ /* ensure the circle is complete */
+ //_cairo_contour_add_point (&stroker->ccw.contour,
+ //_cairo_contour_first_point (&stroker->ccw.contour));
+ } else {
+ if (stroker->has_current_face)
+ add_trailing_cap (stroker, &stroker->current_face);
+
+ //_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+ //_cairo_contour_reset (&stroker->ccw.contour);
+
+ if (stroker->has_first_face) {
+ //_cairo_contour_add_point (&stroker->ccw.contour,
+ //&stroker->first_face.cw);
+ add_leading_cap (stroker, &stroker->first_face);
+ //_cairo_polygon_add_contour (stroker->polygon,
+ //&stroker->ccw.contour);
+ //_cairo_contour_reset (&stroker->ccw.contour);
+ }
+ }
+}
+
+static cairo_status_t
+move_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+
+ /* Cap the start and end of the previous sub path as needed */
+ add_caps (stroker);
+
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+ stroker->has_sub_path = FALSE;
+
+ stroker->first_point = *point;
+
+ stroker->current_face.point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+line_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+ cairo_stroke_face_t start;
+ cairo_point_t *p1 = &stroker->current_face.point;
+ cairo_slope_t dev_slope;
+
+ stroker->has_sub_path = TRUE;
+
+ if (p1->x == point->x && p1->y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_slope_init (&dev_slope, p1, point);
+ compute_face (p1, &dev_slope, stroker, &start);
+
+ if (stroker->has_current_face) {
+ int clockwise = join_is_clockwise (&stroker->current_face, &start);
+ /* Join with final face from previous segment */
+ outer_join (stroker, &stroker->current_face, &start, clockwise);
+ inner_join (stroker, &stroker->current_face, &start, clockwise);
+ } else {
+ if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = start;
+ _cairo_tristrip_move_to (stroker->strip, &start.cw);
+ stroker->has_first_face = TRUE;
+ }
+ stroker->has_current_face = TRUE;
+
+ _cairo_tristrip_add_point (stroker->strip, &start.cw);
+ _cairo_tristrip_add_point (stroker->strip, &start.ccw);
+ }
+
+ stroker->current_face = start;
+ stroker->current_face.point = *point;
+ stroker->current_face.ccw.x += dev_slope.dx;
+ stroker->current_face.ccw.y += dev_slope.dy;
+ stroker->current_face.cw.x += dev_slope.dx;
+ stroker->current_face.cw.y += dev_slope.dy;
+
+ _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.cw);
+ _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.ccw);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent)
+{
+ struct stroker *stroker = closure;
+ cairo_stroke_face_t face;
+
+ if (tangent->dx == 0 && tangent->dy == 0) {
+ const cairo_point_t *inpt, *outpt;
+ cairo_point_t t;
+ int clockwise;
+
+ face = stroker->current_face;
+
+ face.usr_vector.x = -face.usr_vector.x;
+ face.usr_vector.y = -face.usr_vector.y;
+ face.dev_vector.dx = -face.dev_vector.dx;
+ face.dev_vector.dy = -face.dev_vector.dy;
+
+ t = face.cw;
+ face.cw = face.ccw;
+ face.ccw = t;
+
+ clockwise = join_is_clockwise (&stroker->current_face, &face);
+ if (clockwise) {
+ inpt = &stroker->current_face.cw;
+ outpt = &face.cw;
+ } else {
+ inpt = &stroker->current_face.ccw;
+ outpt = &face.ccw;
+ }
+
+ add_fan (stroker,
+ &stroker->current_face.dev_vector,
+ &face.dev_vector,
+ &stroker->current_face.point, inpt, outpt,
+ clockwise);
+ } else {
+ compute_face (point, tangent, stroker, &face);
+
+ if (face.dev_slope.x * stroker->current_face.dev_slope.x +
+ face.dev_slope.y * stroker->current_face.dev_slope.y < 0)
+ {
+ const cairo_point_t *inpt, *outpt;
+ int clockwise = join_is_clockwise (&stroker->current_face, &face);
+
+ stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x;
+ stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y;
+ //contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+
+ stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x;
+ stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y;
+ //contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
+
+ if (clockwise) {
+ inpt = &stroker->current_face.cw;
+ outpt = &face.cw;
+ } else {
+ inpt = &stroker->current_face.ccw;
+ outpt = &face.ccw;
+ }
+ add_fan (stroker,
+ &stroker->current_face.dev_vector,
+ &face.dev_vector,
+ &stroker->current_face.point, inpt, outpt,
+ clockwise);
+ }
+
+ _cairo_tristrip_add_point (stroker->strip, &face.cw);
+ _cairo_tristrip_add_point (stroker->strip, &face.ccw);
+ }
+
+ stroker->current_face = face;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ struct stroker *stroker = closure;
+ cairo_spline_t spline;
+ cairo_stroke_face_t face;
+
+ if (stroker->has_limits) {
+ if (! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
+ &stroker->limit))
+ return line_to (closure, d);
+ }
+
+ if (! _cairo_spline_init (&spline, spline_to, stroker,
+ &stroker->current_face.point, b, c, d))
+ return line_to (closure, d);
+
+ compute_face (&stroker->current_face.point, &spline.initial_slope,
+ stroker, &face);
+
+ if (stroker->has_current_face) {
+ int clockwise = join_is_clockwise (&stroker->current_face, &face);
+ /* Join with final face from previous segment */
+ outer_join (stroker, &stroker->current_face, &face, clockwise);
+ inner_join (stroker, &stroker->current_face, &face, clockwise);
+ } else {
+ if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = face;
+ _cairo_tristrip_move_to (stroker->strip, &face.cw);
+ stroker->has_first_face = TRUE;
+ }
+ stroker->has_current_face = TRUE;
+
+ _cairo_tristrip_add_point (stroker->strip, &face.cw);
+ _cairo_tristrip_add_point (stroker->strip, &face.ccw);
+ }
+ stroker->current_face = face;
+
+ return _cairo_spline_decompose (&spline, stroker->tolerance);
+}
+
+static cairo_status_t
+close_path (void *closure)
+{
+ struct stroker *stroker = closure;
+ cairo_status_t status;
+
+ status = line_to (stroker, &stroker->first_point);
+ if (unlikely (status))
+ return status;
+
+ if (stroker->has_first_face && stroker->has_current_face) {
+ /* Join first and final faces of sub path */
+ outer_close (stroker, &stroker->current_face, &stroker->first_face);
+ inner_close (stroker, &stroker->current_face, &stroker->first_face);
+ } else {
+ /* Cap the start and end of the sub path as needed */
+ add_caps (stroker);
+ }
+
+ stroker->has_sub_path = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_tristrip_t *strip)
+{
+ struct stroker stroker;
+ cairo_int_status_t status;
+ int i;
+
+ if (style->num_dashes)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ stroker.style = *style;
+ stroker.ctm = ctm;
+ stroker.ctm_inverse = ctm_inverse;
+ stroker.tolerance = tolerance;
+
+ stroker.ctm_det_positive =
+ _cairo_matrix_compute_determinant (ctm) >= 0.0;
+
+ status = _cairo_pen_init (&stroker.pen,
+ style->line_width / 2.0,
+ tolerance, ctm);
+ if (unlikely (status))
+ return status;
+
+ if (stroker.pen.num_vertices <= 1) {
+ if (stroker.pen.num_vertices)
+ _cairo_pen_fini (&stroker.pen);
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ stroker.has_current_face = FALSE;
+ stroker.has_first_face = FALSE;
+ stroker.has_sub_path = FALSE;
+
+ stroker.has_limits = strip->num_limits > 0;
+ stroker.limit = strip->limits[0];
+ for (i = 1; i < strip->num_limits; i++)
+ _cairo_box_add_box (&stroker.limit, &strip->limits[i]);
+
+ stroker.strip = strip;
+
+ status = _cairo_path_fixed_interpret (path,
+ move_to,
+ line_to,
+ curve_to,
+ close_path,
+ &stroker);
+ /* Cap the start and end of the final sub path as needed */
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ add_caps (&stroker);
+
+ _cairo_pen_fini (&stroker.pen);
+
+ return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
new file mode 100755
index 000000000..73c554bd8
--- /dev/null
+++ b/src/cairo-path-stroke.c
@@ -0,0 +1,1517 @@
+/* -*- 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-box-inline.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+#include "cairo-traps-private.h"
+
+typedef struct cairo_stroker {
+ cairo_stroke_style_t style;
+
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
+ double half_line_width;
+ double tolerance;
+ double 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 cairo_bool_t
+_cairo_stroke_segment_intersect (cairo_point_t *p1, cairo_point_t *p2,
+ cairo_point_t *p3, cairo_point_t *p4,
+ cairo_point_t *p)
+{
+ double x1, y1, x2, y2, x3, y3, x4, y4;
+ double pre, post;
+ double x, y, d;
+
+ 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);
+ x4 = _cairo_fixed_to_double (p4->x);
+ y4 = _cairo_fixed_to_double (p4->y);
+
+ d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+ if (d == 0)
+ return FALSE;
+
+ pre = x1 * y2 - y1 * x2;
+ post = x3 * y4 - y3 * x4;
+ x = (pre * (x3 - x4) - (x1 - x2) * post) / d;
+ y = (pre * (y3 - y4) - (y1 - y2) * post) / d;
+
+ /* check if x, y are within both segments */
+ if (x < MIN (x1, x2) || x > MAX (x1, x2) ||
+ x < MIN (x3, x4) || x > MAX (x3, x4))
+ return FALSE;
+ if (y < MIN (y1, y2) || y > MAX (y1, y2) ||
+ y < MIN (y1, y2) || y > MAX (y3, y4))
+ return FALSE;
+
+ p->x = _cairo_fixed_from_double (x);
+ p->y = _cairo_fixed_from_double (y);
+ return TRUE;
+}
+
+static void
+_cairo_stroker_limit (cairo_stroker_t *stroker,
+ const cairo_path_fixed_t *path,
+ const cairo_box_t *boxes,
+ int num_boxes)
+{
+ double dx, dy;
+ cairo_fixed_t fdx, fdy;
+
+ stroker->has_bounds = TRUE;
+ _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+ /* Extend the bounds in each direction to account for the maximum area
+ * we might generate trapezoids, to capture line segments that are outside
+ * of the bounds but which might generate rendering that's within bounds.
+ */
+
+ _cairo_stroke_style_max_distance_from_path (&stroker->style, path,
+ stroker->ctm, &dx, &dy);
+
+ fdx = _cairo_fixed_from_double (dx);
+ fdy = _cairo_fixed_from_double (dy);
+
+ stroker->bounds.p1.x -= fdx;
+ stroker->bounds.p2.x += fdx;
+
+ stroker->bounds.p1.y -= fdy;
+ stroker->bounds.p2.y += fdy;
+}
+
+static cairo_status_t
+_cairo_stroker_init (cairo_stroker_t *stroker,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ const cairo_box_t *limits,
+ int num_limits)
+{
+ cairo_status_t status;
+
+ stroker->style = *stroke_style;
+ stroker->ctm = ctm;
+ stroker->ctm_inverse = ctm_inverse;
+ stroker->tolerance = tolerance;
+ stroker->half_line_width = stroke_style->line_width / 2.0;
+
+ stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
+ stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
+
+ status = _cairo_pen_init (&stroker->pen,
+ stroker->half_line_width, tolerance, ctm);
+ if (unlikely (status))
+ return status;
+
+ stroker->has_current_face = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+ stroker->add_external_edge = NULL;
+
+ stroker->has_bounds = FALSE;
+ if (num_limits)
+ _cairo_stroker_limit (stroker, path, limits, num_limits);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_stroker_fini (cairo_stroker_t *stroker)
+{
+ _cairo_pen_fini (&stroker->pen);
+}
+
+static void
+_translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+ point->x += offset->x;
+ point->y += offset->y;
+}
+
+static int
+_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ cairo_slope_t in_slope, out_slope;
+
+ _cairo_slope_init (&in_slope, &in->point, &in->cw);
+ _cairo_slope_init (&out_slope, &out->point, &out->cw);
+
+ return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+}
+
+/**
+ * _cairo_slope_compare_sgn:
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ **/
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+ double c = (dx1 * dy2 - dx2 * dy1);
+
+ if (c > 0) return 1;
+ if (c < 0) return -1;
+ return 0;
+}
+
+static inline int
+_range_step (int i, int step, int max)
+{
+ i += step;
+ if (i < 0)
+ i = max - 1;
+ if (i >= max)
+ i = 0;
+ return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static cairo_status_t
+_tessellate_fan (cairo_stroker_t *stroker,
+ const cairo_slope_t *in_vector,
+ const cairo_slope_t *out_vector,
+ const cairo_point_t *midpt,
+ const cairo_point_t *inpt,
+ const cairo_point_t *outpt,
+ cairo_bool_t clockwise)
+{
+ cairo_point_t stack_points[64], *points = stack_points;
+ cairo_pen_t *pen = &stroker->pen;
+ int start, stop, num_points = 0;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (stroker->has_bounds &&
+ ! _cairo_box_contains_point (&stroker->bounds, midpt))
+ goto BEVEL;
+
+ assert (stroker->pen.num_vertices);
+
+ if (clockwise) {
+ _cairo_pen_find_active_ccw_vertices (pen,
+ in_vector, out_vector,
+ &start, &stop);
+ if (stroker->add_external_edge) {
+ cairo_point_t last;
+ last = *inpt;
+ while (start != stop) {
+ cairo_point_t p = *midpt;
+ _translate_point (&p, &pen->vertices[start].point);
+
+ status = stroker->add_external_edge (stroker->closure,
+ &last, &p);
+ if (unlikely (status))
+ return status;
+ last = p;
+
+ if (start-- == 0)
+ start += pen->num_vertices;
+ }
+ status = stroker->add_external_edge (stroker->closure,
+ &last, outpt);
+ } else {
+ if (start == stop)
+ goto BEVEL;
+
+ num_points = stop - start;
+ if (num_points < 0)
+ num_points += pen->num_vertices;
+ else
+ num_points = pen->num_vertices - stop + start;
+ num_points += 2;
+ if (num_points > ARRAY_LENGTH(stack_points)) {
+ points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
+ if (unlikely (points == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ points[0] = *inpt;
+ num_points = 1;
+ while (start != stop) {
+ points[num_points] = *midpt;
+ _translate_point (&points[num_points], &pen->vertices[start].point);
+ num_points++;
+
+ if (start-- == 0)
+ start += pen->num_vertices;
+ }
+ points[num_points++] = *outpt;
+ }
+ } else {
+ _cairo_pen_find_active_cw_vertices (pen,
+ in_vector, out_vector,
+ &start, &stop);
+ if (stroker->add_external_edge) {
+ cairo_point_t last;
+ last = *inpt;
+ while (start != stop) {
+ cairo_point_t p = *midpt;
+ _translate_point (&p, &pen->vertices[start].point);
+
+ status = stroker->add_external_edge (stroker->closure,
+ &p, &last);
+ if (unlikely (status))
+ return status;
+ last = p;
+
+ if (++start == pen->num_vertices)
+ start = 0;
+ }
+ status = stroker->add_external_edge (stroker->closure,
+ outpt, &last);
+ } else {
+ if (start == stop)
+ goto BEVEL;
+
+ num_points = stop - start;
+ if (num_points < 0)
+ num_points += pen->num_vertices;
+ else
+ num_points = pen->num_vertices - stop + start;
+ num_points += 2;
+ if (num_points > ARRAY_LENGTH(stack_points)) {
+ points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
+ if (unlikely (points == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ points[0] = *inpt;
+ num_points = 1;
+ while (start != stop) {
+ points[num_points] = *midpt;
+ _translate_point (&points[num_points], &pen->vertices[start].point);
+ num_points++;
+
+ if (++start == pen->num_vertices)
+ start = 0;
+ }
+ points[num_points++] = *outpt;
+ }
+ }
+
+ if (num_points) {
+ status = stroker->add_triangle_fan (stroker->closure,
+ midpt, points, num_points);
+ }
+
+ if (points != stack_points)
+ free (points);
+
+ return status;
+
+BEVEL:
+ /* Ensure a leak free connection... */
+ if (stroker->add_external_edge != NULL) {
+ if (clockwise)
+ return stroker->add_external_edge (stroker->closure, inpt, outpt);
+ else
+ return stroker->add_external_edge (stroker->closure, outpt, inpt);
+ } else {
+ stack_points[0] = *midpt;
+ stack_points[1] = *inpt;
+ stack_points[2] = *outpt;
+ return stroker->add_triangle (stroker->closure, stack_points);
+ }
+}
+
+static cairo_status_t
+_cairo_stroker_join (cairo_stroker_t *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ int clockwise = _cairo_stroker_join_is_clockwise (out, in);
+ const cairo_point_t *inpt, *outpt;
+ cairo_point_t points[4];
+ cairo_status_t status;
+
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (clockwise) {
+ if (stroker->add_external_edge != NULL) {
+ status = stroker->add_external_edge (stroker->closure,
+ &out->cw, &in->point);
+ if (unlikely (status))
+ return status;
+
+ status = stroker->add_external_edge (stroker->closure,
+ &in->point, &in->cw);
+ if (unlikely (status))
+ return status;
+ }
+
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ } else {
+ if (stroker->add_external_edge != NULL) {
+ status = stroker->add_external_edge (stroker->closure,
+ &in->ccw, &in->point);
+ if (unlikely (status))
+ return status;
+
+ status = stroker->add_external_edge (stroker->closure,
+ &in->point, &out->ccw);
+ if (unlikely (status))
+ return status;
+ }
+
+ inpt = &in->cw;
+ outpt = &out->cw;
+ }
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ return _tessellate_fan (stroker,
+ &in->dev_vector,
+ &out->dev_vector,
+ &in->point, inpt, outpt,
+ clockwise);
+
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+ -in->usr_vector.y * out->usr_vector.y;
+ double ml = stroker->style.miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 - in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->usr_vector.x;
+ dy1 = in->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->usr_vector.x;
+ dy2 = out->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ if (stroker->add_external_edge != NULL) {
+ points[0].x = _cairo_fixed_from_double (mx);
+ points[0].y = _cairo_fixed_from_double (my);
+
+ if (clockwise) {
+ status = stroker->add_external_edge (stroker->closure,
+ inpt, &points[0]);
+ if (unlikely (status))
+ return status;
+
+ status = stroker->add_external_edge (stroker->closure,
+ &points[0], outpt);
+ if (unlikely (status))
+ return status;
+ } else {
+ status = stroker->add_external_edge (stroker->closure,
+ outpt, &points[0]);
+ if (unlikely (status))
+ return status;
+
+ status = stroker->add_external_edge (stroker->closure,
+ &points[0], inpt);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ } else {
+ points[0] = in->point;
+ points[1] = *inpt;
+ points[2].x = _cairo_fixed_from_double (mx);
+ points[2].y = _cairo_fixed_from_double (my);
+ points[3] = *outpt;
+
+ return stroker->add_convex_quad (stroker->closure, points);
+ }
+ }
+ }
+ }
+
+ /* fall through ... */
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ if (stroker->add_external_edge != NULL) {
+ if (clockwise) {
+ return stroker->add_external_edge (stroker->closure,
+ inpt, outpt);
+ } else {
+ return stroker->add_external_edge (stroker->closure,
+ outpt, inpt);
+ }
+ } else {
+ points[0] = in->point;
+ points[1] = *inpt;
+ points[2] = *outpt;
+
+ return stroker->add_triangle (stroker->closure, points);
+ }
+ }
+}
+
+static cairo_status_t
+_cairo_stroker_add_cap (cairo_stroker_t *stroker,
+ const cairo_stroke_face_t *f)
+{
+ switch (stroker->style.line_cap) {
+ case CAIRO_LINE_CAP_ROUND: {
+ cairo_slope_t slope;
+
+ slope.dx = -f->dev_vector.dx;
+ slope.dy = -f->dev_vector.dy;
+
+ return _tessellate_fan (stroker,
+ &f->dev_vector,
+ &slope,
+ &f->point, &f->cw, &f->ccw,
+ FALSE);
+
+ }
+
+ case CAIRO_LINE_CAP_SQUARE: {
+ double dx, dy;
+ cairo_slope_t fvector;
+ cairo_point_t quad[4];
+
+ dx = f->usr_vector.x;
+ dy = f->usr_vector.y;
+ dx *= stroker->half_line_width;
+ dy *= stroker->half_line_width;
+ cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+ fvector.dx = _cairo_fixed_from_double (dx);
+ fvector.dy = _cairo_fixed_from_double (dy);
+
+ quad[0] = f->ccw;
+ quad[1].x = f->ccw.x + fvector.dx;
+ quad[1].y = f->ccw.y + fvector.dy;
+ quad[2].x = f->cw.x + fvector.dx;
+ quad[2].y = f->cw.y + fvector.dy;
+ quad[3] = f->cw;
+
+ if (stroker->add_external_edge != NULL) {
+ cairo_status_t status;
+
+ status = stroker->add_external_edge (stroker->closure,
+ &quad[0], &quad[1]);
+ if (unlikely (status))
+ return status;
+
+ status = stroker->add_external_edge (stroker->closure,
+ &quad[1], &quad[2]);
+ if (unlikely (status))
+ return status;
+
+ status = stroker->add_external_edge (stroker->closure,
+ &quad[2], &quad[3]);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+ } else {
+ return stroker->add_convex_quad (stroker->closure, quad);
+ }
+ }
+
+ case CAIRO_LINE_CAP_BUTT:
+ default:
+ if (stroker->add_external_edge != NULL) {
+ return stroker->add_external_edge (stroker->closure,
+ &f->ccw, &f->cw);
+ } else {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+}
+
+static cairo_status_t
+_cairo_stroker_add_leading_cap (cairo_stroker_t *stroker,
+ const cairo_stroke_face_t *face)
+{
+ cairo_stroke_face_t reversed;
+ cairo_point_t t;
+
+ reversed = *face;
+
+ /* The initial cap needs an outward facing vector. Reverse everything */
+ reversed.usr_vector.x = -reversed.usr_vector.x;
+ reversed.usr_vector.y = -reversed.usr_vector.y;
+ reversed.dev_vector.dx = -reversed.dev_vector.dx;
+ reversed.dev_vector.dy = -reversed.dev_vector.dy;
+ t = reversed.cw;
+ reversed.cw = reversed.ccw;
+ reversed.ccw = t;
+
+ return _cairo_stroker_add_cap (stroker, &reversed);
+}
+
+static cairo_status_t
+_cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker,
+ const cairo_stroke_face_t *face)
+{
+ return _cairo_stroker_add_cap (stroker, face);
+}
+
+static inline cairo_bool_t
+_compute_normalized_device_slope (double *dx, double *dy,
+ const cairo_matrix_t *ctm_inverse,
+ double *mag_out)
+{
+ double dx0 = *dx, dy0 = *dy;
+ double mag;
+
+ cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
+
+ if (dx0 == 0.0 && dy0 == 0.0) {
+ if (mag_out)
+ *mag_out = 0.0;
+ return FALSE;
+ }
+
+ if (dx0 == 0.0) {
+ *dx = 0.0;
+ if (dy0 > 0.0) {
+ mag = dy0;
+ *dy = 1.0;
+ } else {
+ mag = -dy0;
+ *dy = -1.0;
+ }
+ } else if (dy0 == 0.0) {
+ *dy = 0.0;
+ if (dx0 > 0.0) {
+ mag = dx0;
+ *dx = 1.0;
+ } else {
+ mag = -dx0;
+ *dx = -1.0;
+ }
+ } else {
+ mag = hypot (dx0, dy0);
+ *dx = dx0 / mag;
+ *dy = dy0 / mag;
+ }
+
+ if (mag_out)
+ *mag_out = mag;
+
+ return TRUE;
+}
+
+static void
+_compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ double slope_dx,
+ double slope_dy,
+ cairo_stroker_t *stroker,
+ cairo_stroke_face_t *face)
+{
+ double face_dx, face_dy;
+ cairo_point_t offset_ccw, offset_cw;
+
+ /*
+ * rotate to get a line_width/2 vector along the face, note that
+ * the vector must be rotated the right direction in device space,
+ * but by 90° in user space. So, the rotation depends on
+ * whether the ctm reflects or not, and that can be determined
+ * by looking at the determinant of the matrix.
+ */
+ if (stroker->ctm_det_positive)
+ {
+ face_dx = - slope_dy * stroker->half_line_width;
+ face_dy = slope_dx * stroker->half_line_width;
+ }
+ else
+ {
+ face_dx = slope_dy * stroker->half_line_width;
+ face_dy = - slope_dx * stroker->half_line_width;
+ }
+
+ /* back to device space */
+ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+
+ offset_ccw.x = _cairo_fixed_from_double (face_dx);
+ offset_ccw.y = _cairo_fixed_from_double (face_dy);
+ offset_cw.x = -offset_ccw.x;
+ offset_cw.y = -offset_ccw.y;
+
+ face->ccw = *point;
+ _translate_point (&face->ccw, &offset_ccw);
+
+ face->point = *point;
+
+ face->cw = *point;
+ _translate_point (&face->cw, &offset_cw);
+
+ face->usr_vector.x = slope_dx;
+ face->usr_vector.y = slope_dy;
+
+ face->dev_vector = *dev_slope;
+}
+
+static cairo_status_t
+_cairo_stroker_add_caps (cairo_stroker_t *stroker)
+{
+ cairo_status_t status;
+
+ /* check for a degenerative sub_path */
+ if (stroker->has_initial_sub_path
+ && ! stroker->has_first_face
+ && ! stroker->has_current_face
+ && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+ {
+ /* pick an arbitrary slope to use */
+ double dx = 1.0, dy = 0.0;
+ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+ cairo_stroke_face_t face;
+ face.length = 0.0;
+
+ _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;
+ start.length = 0.0;
+
+ stroker->has_initial_sub_path = TRUE;
+
+ if (p1->x == point->x && p1->y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_slope_init (&dev_slope, p1, point);
+ slope_dx = _cairo_fixed_to_double (point->x - p1->x);
+ slope_dy = _cairo_fixed_to_double (point->y - p1->y);
+ _compute_normalized_device_slope (&slope_dx, &slope_dy,
+ stroker->ctm_inverse, NULL);
+
+ status = _cairo_stroker_add_sub_edge (stroker,
+ p1, point,
+ &dev_slope,
+ slope_dx, slope_dy,
+ &start, &end);
+ if (unlikely (status))
+ return status;
+
+ if (stroker->has_current_face) {
+ /* Join with final face from previous segment */
+ status = _cairo_stroker_join (stroker,
+ &stroker->current_face,
+ &start);
+ if (unlikely (status))
+ return status;
+ } else if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = start;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->current_face = end;
+ stroker->has_current_face = TRUE;
+
+ stroker->current_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_spline_to (void *closure,
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent)
+{
+ cairo_stroker_t *stroker = closure;
+ cairo_stroke_face_t new_face;
+ double slope_dx, slope_dy;
+ cairo_point_t points[3];
+ cairo_point_t intersect_point;
+ cairo_line_join_t line_join_save;
+ cairo_status_t status;
+
+ stroker->has_initial_sub_path = TRUE;
+ new_face.length = 0.0;
+
+ if (stroker->current_point.x == point->x &&
+ stroker->current_point.y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ slope_dx = _cairo_fixed_to_double (tangent->dx);
+ slope_dy = _cairo_fixed_to_double (tangent->dy);
+
+ if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+ stroker->ctm_inverse, NULL))
+ return CAIRO_STATUS_SUCCESS;
+
+ _compute_face (point, tangent,
+ slope_dx, slope_dy,
+ stroker, &new_face);
+
+ assert(stroker->has_current_face);
+
+ if (_cairo_stroke_segment_intersect (&stroker->current_face.cw,
+ &stroker->current_face.ccw,
+ &new_face.cw,
+ &new_face.ccw,
+ &intersect_point)) {
+ points[0] = stroker->current_face.ccw;
+ points[1] = new_face.ccw;
+ points[2] = intersect_point;
+ stroker->add_triangle (stroker->closure, points);
+
+ points[0] = stroker->current_face.cw;
+ points[1] = new_face.cw;
+ stroker->add_triangle (stroker->closure, points);
+ }
+ else {
+ points[0] = stroker->current_face.ccw;
+ points[1] = stroker->current_face.cw;
+ points[2] = new_face.cw;
+ stroker->add_triangle (stroker->closure, points);
+
+ points[0] = stroker->current_face.ccw;
+ points[1] = new_face.cw;
+ points[2] = new_face.ccw;
+ stroker->add_triangle (stroker->closure, points);
+ }
+
+ /* compute join */
+ /* 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;
+
+ if (! stroker->dash.dashed || stroker->dash.dash_on) {
+ if (stroker->has_current_face) {
+ status = _cairo_stroker_join (stroker,
+ &stroker->current_face, &new_face);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ stroker->style.line_join = line_join_save;
+
+ stroker->current_face = new_face;
+ stroker->has_current_face = TRUE;
+ stroker->current_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Dashed lines. Cap each dash end, join around turns when on
+ */
+static cairo_status_t
+_cairo_stroker_line_to_dashed (void *closure,
+ const cairo_point_t *p2)
+{
+ cairo_stroker_t *stroker = closure;
+ double mag, remain, step_length = 0;
+ double slope_dx, slope_dy;
+ double dx2, dy2;
+ cairo_stroke_face_t sub_start, sub_end;
+ cairo_point_t *p1 = &stroker->current_point;
+ cairo_slope_t dev_slope;
+ cairo_line_t segment;
+ cairo_bool_t fully_in_bounds;
+ cairo_status_t status;
+ sub_start.length = 0.0;
+
+ stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
+
+ if (p1->x == p2->x && p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ fully_in_bounds = TRUE;
+ if (stroker->has_bounds &&
+ (! _cairo_box_contains_point (&stroker->bounds, p1) ||
+ ! _cairo_box_contains_point (&stroker->bounds, p2)))
+ {
+ fully_in_bounds = FALSE;
+ }
+
+ _cairo_slope_init (&dev_slope, p1, p2);
+
+ slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
+ slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
+
+ if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+ stroker->ctm_inverse, &mag))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ remain = mag;
+ segment.p1 = *p1;
+ while (remain) {
+ step_length = MIN (stroker->dash.dash_remain, remain);
+ remain -= step_length;
+ dx2 = slope_dx * (mag - remain);
+ dy2 = slope_dy * (mag - remain);
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+ segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
+ segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
+
+ if (stroker->dash.dash_on &&
+ (fully_in_bounds ||
+ (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+ status = _cairo_stroker_add_sub_edge (stroker,
+ &segment.p1, &segment.p2,
+ &dev_slope,
+ slope_dx, slope_dy,
+ &sub_start, &sub_end);
+ if (unlikely (status))
+ return status;
+
+ if (stroker->has_current_face)
+ {
+ /* Join with final face from previous segment */
+ status = _cairo_stroker_join (stroker,
+ &stroker->current_face,
+ &sub_start);
+ if (unlikely (status))
+ return status;
+
+ stroker->has_current_face = FALSE;
+ }
+ else if (! stroker->has_first_face &&
+ stroker->dash.dash_starts_on)
+ {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = sub_start;
+ stroker->has_first_face = TRUE;
+ }
+ else
+ {
+ /* Cap dash start if not connecting to a previous segment */
+ status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (remain) {
+ /* Cap dash end if not at end of segment */
+ status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
+ if (unlikely (status))
+ return status;
+ } else {
+ stroker->current_face = sub_end;
+ stroker->has_current_face = TRUE;
+ }
+ } else {
+ if (stroker->has_current_face) {
+ /* Cap final face from previous segment */
+ status = _cairo_stroker_add_trailing_cap (stroker,
+ &stroker->current_face);
+ if (unlikely (status))
+ return status;
+
+ stroker->has_current_face = FALSE;
+ }
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ if (stroker->dash.dash_on && ! stroker->has_current_face) {
+ /* This segment ends on a transition to dash_on, compute a new face
+ * and add cap for the beginning of the next dash_on step.
+ *
+ * Note: this will create a degenerate cap if this is not the last line
+ * in the path. Whether this behaviour is desirable or not is debatable.
+ * On one side these degenerate caps can not be reproduced with regular
+ * path stroking.
+ * On the other hand, Acroread 7 also produces the degenerate caps.
+ */
+ _compute_face (p2, &dev_slope,
+ slope_dx, slope_dy,
+ stroker,
+ &stroker->current_face);
+
+ status = _cairo_stroker_add_leading_cap (stroker,
+ &stroker->current_face);
+ if (unlikely (status))
+ return status;
+
+ stroker->has_current_face = TRUE;
+ }
+
+ stroker->current_point = *p2;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+static cairo_status_t
+_cairo_stroker_curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ cairo_stroker_t *stroker = closure;
+ cairo_spline_t spline;
+ cairo_line_join_t line_join_save;
+ cairo_stroke_face_t face;
+ double slope_dx, slope_dy;
+ cairo_spline_add_point_func_t line_to;
+ cairo_spline_add_point_func_t spline_to;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ face.length = 0.0;
+
+ line_to = stroker->dash.dashed ?
+ (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed :
+ (cairo_spline_add_point_func_t) _cairo_stroker_line_to;
+
+ /* spline_to is only capable of rendering non-degenerate splines. */
+ spline_to = stroker->dash.dashed ?
+ (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed :
+ (cairo_spline_add_point_func_t) _cairo_stroker_spline_to;
+
+ if (! _cairo_spline_init (&spline,
+ spline_to,
+ stroker,
+ &stroker->current_point, b, c, d))
+ {
+ cairo_slope_t fallback_slope;
+ _cairo_slope_init (&fallback_slope, &stroker->current_point, d);
+ return line_to (closure, d, &fallback_slope);
+ }
+
+ /* If the line width is so small that the pen is reduced to a
+ single point, then we have nothing to do. */
+ if (stroker->pen.num_vertices <= 1)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Compute the initial face */
+ if (! stroker->dash.dashed || stroker->dash.dash_on) {
+ slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
+ slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
+ if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+ stroker->ctm_inverse, NULL))
+ {
+ _compute_face (&stroker->current_point,
+ &spline.initial_slope,
+ slope_dx, slope_dy,
+ stroker, &face);
+ }
+ if (stroker->has_current_face) {
+ status = _cairo_stroker_join (stroker,
+ &stroker->current_face, &face);
+ if (unlikely (status))
+ return status;
+ } else if (! stroker->has_first_face) {
+ stroker->first_face = face;
+ stroker->has_first_face = TRUE;
+ }
+
+ stroker->current_face = face;
+ stroker->has_current_face = TRUE;
+ }
+
+ /* Temporarily modify the stroker to use round joins to guarantee
+ * smooth stroked curves. */
+ line_join_save = stroker->style.line_join;
+ stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
+
+ status = _cairo_spline_decompose (&spline, stroker->tolerance);
+ if (unlikely (status))
+ return status;
+
+ /* And join the final face */
+ if (! stroker->dash.dashed || stroker->dash.dash_on) {
+ slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
+ slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
+ if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+ stroker->ctm_inverse, NULL))
+ {
+ _compute_face (&stroker->current_point,
+ &spline.final_slope,
+ slope_dx, slope_dy,
+ stroker, &face);
+ }
+
+ status = _cairo_stroker_join (stroker, &stroker->current_face, &face);
+ if (unlikely (status))
+ return status;
+
+ stroker->current_face = face;
+ }
+
+ stroker->style.line_join = line_join_save;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_close_path (void *closure)
+{
+ cairo_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ if (stroker->dash.dashed)
+ status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
+ else
+ status = _cairo_stroker_line_to (stroker, &stroker->first_point);
+ if (unlikely (status))
+ return status;
+
+ if (stroker->has_first_face && stroker->has_current_face) {
+ /* Join first and final faces of sub path */
+ status = _cairo_stroker_join (stroker,
+ &stroker->current_face,
+ &stroker->first_face);
+ if (unlikely (status))
+ return status;
+ } else {
+ /* Cap the start and end of the sub path as needed */
+ status = _cairo_stroker_add_caps (stroker);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->has_initial_sub_path = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_status_t (*add_triangle) (void *closure,
+ const cairo_point_t triangle[3]),
+ cairo_status_t (*add_triangle_fan) (void *closure,
+ const cairo_point_t *midpt,
+ const cairo_point_t *points,
+ int npoints),
+ cairo_status_t (*add_convex_quad) (void *closure,
+ const cairo_point_t quad[4]),
+ void *closure)
+{
+ cairo_stroker_t stroker;
+ cairo_status_t status;
+
+ status = _cairo_stroker_init (&stroker, path, stroke_style,
+ ctm, ctm_inverse, tolerance,
+ NULL, 0);
+ if (unlikely (status))
+ return status;
+
+ stroker.add_triangle = add_triangle;
+ stroker.add_triangle_fan = add_triangle_fan;
+ stroker.add_convex_quad = add_convex_quad;
+ stroker.closure = closure;
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_stroker_move_to,
+ stroker.dash.dashed ?
+ _cairo_stroker_line_to_dashed :
+ _cairo_stroker_line_to,
+ _cairo_stroker_curve_to,
+ _cairo_stroker_close_path,
+ &stroker);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ /* Cap the start and end of the final sub path as needed */
+ status = _cairo_stroker_add_caps (&stroker);
+
+BAIL:
+ _cairo_stroker_fini (&stroker);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon)
+{
+ cairo_stroker_t stroker;
+ cairo_status_t status;
+
+ status = _cairo_stroker_init (&stroker, path, stroke_style,
+ ctm, ctm_inverse, tolerance,
+ polygon->limits, polygon->num_limits);
+ if (unlikely (status))
+ return status;
+
+ stroker.add_external_edge = _cairo_polygon_add_external_edge,
+ stroker.closure = polygon;
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_stroker_move_to,
+ stroker.dash.dashed ?
+ _cairo_stroker_line_to_dashed :
+ _cairo_stroker_line_to,
+ _cairo_stroker_curve_to,
+ _cairo_stroker_close_path,
+ &stroker);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ /* Cap the start and end of the final sub path as needed */
+ status = _cairo_stroker_add_caps (&stroker);
+
+BAIL:
+ _cairo_stroker_fini (&stroker);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps)
+{
+ cairo_int_status_t status;
+ cairo_polygon_t polygon;
+
+ _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm,
+ ctm_inverse,
+ tolerance,
+ &polygon);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _cairo_polygon_status (&polygon);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
+ CAIRO_FILL_RULE_WINDING);
+
+BAIL:
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
diff --git a/src/cairo-path.c b/src/cairo-path.c
new file mode 100755
index 000000000..43cd175a3
--- /dev/null
+++ b/src/cairo-path.c
@@ -0,0 +1,479 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@redhat.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-private.h"
+#include "cairo-path-fixed-private.h"
+
+/**
+ * SECTION:cairo-paths
+ * @Title: Paths
+ * @Short_Description: Creating paths and manipulating path data
+ *
+ * Paths are the most basic drawing tools and are primarily used to implicitly
+ * generate simple masks.
+ **/
+
+static const cairo_path_t _cairo_path_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
+
+/* Closure for path interpretation. */
+typedef struct cairo_path_count {
+ int count;
+} cpc_t;
+
+static cairo_status_t
+_cpc_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cpc_t *cpc = closure;
+
+ cpc->count += 2;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpc_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cpc_t *cpc = closure;
+
+ cpc->count += 2;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpc_curve_to (void *closure,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ const cairo_point_t *p3)
+{
+ cpc_t *cpc = closure;
+
+ cpc->count += 4;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpc_close_path (void *closure)
+{
+ cpc_t *cpc = closure;
+
+ cpc->count += 1;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+_cairo_path_count (cairo_path_t *path,
+ cairo_path_fixed_t *path_fixed,
+ double tolerance,
+ cairo_bool_t flatten)
+{
+ cairo_status_t status;
+ cpc_t cpc;
+
+ cpc.count = 0;
+
+ if (flatten) {
+ status = _cairo_path_fixed_interpret_flat (path_fixed,
+ _cpc_move_to,
+ _cpc_line_to,
+ _cpc_close_path,
+ &cpc,
+ tolerance);
+ } else {
+ status = _cairo_path_fixed_interpret (path_fixed,
+ _cpc_move_to,
+ _cpc_line_to,
+ _cpc_curve_to,
+ _cpc_close_path,
+ &cpc);
+ }
+
+ if (unlikely (status))
+ return -1;
+
+ return cpc.count;
+}
+
+/* Closure for path interpretation. */
+typedef struct cairo_path_populate {
+ cairo_path_data_t *data;
+ cairo_t *cr;
+} cpp_t;
+
+static cairo_status_t
+_cpp_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cpp_t *cpp = closure;
+ cairo_path_data_t *data = cpp->data;
+ double x, y;
+
+ x = _cairo_fixed_to_double (point->x);
+ y = _cairo_fixed_to_double (point->y);
+
+ _cairo_backend_to_user (cpp->cr, &x, &y);
+
+ data->header.type = CAIRO_PATH_MOVE_TO;
+ data->header.length = 2;
+
+ /* We index from 1 to leave room for data->header */
+ data[1].point.x = x;
+ data[1].point.y = y;
+
+ cpp->data += data->header.length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpp_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cpp_t *cpp = closure;
+ cairo_path_data_t *data = cpp->data;
+ double x, y;
+
+ x = _cairo_fixed_to_double (point->x);
+ y = _cairo_fixed_to_double (point->y);
+
+ _cairo_backend_to_user (cpp->cr, &x, &y);
+
+ data->header.type = CAIRO_PATH_LINE_TO;
+ data->header.length = 2;
+
+ /* We index from 1 to leave room for data->header */
+ data[1].point.x = x;
+ data[1].point.y = y;
+
+ cpp->data += data->header.length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpp_curve_to (void *closure,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ const cairo_point_t *p3)
+{
+ cpp_t *cpp = closure;
+ cairo_path_data_t *data = cpp->data;
+ double x1, y1;
+ double x2, y2;
+ double x3, y3;
+
+ x1 = _cairo_fixed_to_double (p1->x);
+ y1 = _cairo_fixed_to_double (p1->y);
+ _cairo_backend_to_user (cpp->cr, &x1, &y1);
+
+ x2 = _cairo_fixed_to_double (p2->x);
+ y2 = _cairo_fixed_to_double (p2->y);
+ _cairo_backend_to_user (cpp->cr, &x2, &y2);
+
+ x3 = _cairo_fixed_to_double (p3->x);
+ y3 = _cairo_fixed_to_double (p3->y);
+ _cairo_backend_to_user (cpp->cr, &x3, &y3);
+
+ data->header.type = CAIRO_PATH_CURVE_TO;
+ data->header.length = 4;
+
+ /* We index from 1 to leave room for data->header */
+ data[1].point.x = x1;
+ data[1].point.y = y1;
+
+ data[2].point.x = x2;
+ data[2].point.y = y2;
+
+ data[3].point.x = x3;
+ data[3].point.y = y3;
+
+ cpp->data += data->header.length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpp_close_path (void *closure)
+{
+ cpp_t *cpp = closure;
+ cairo_path_data_t *data = cpp->data;
+
+ data->header.type = CAIRO_PATH_CLOSE_PATH;
+ data->header.length = 1;
+
+ cpp->data += data->header.length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_populate (cairo_path_t *path,
+ cairo_path_fixed_t *path_fixed,
+ cairo_t *cr,
+ cairo_bool_t flatten)
+{
+ cairo_status_t status;
+ cpp_t cpp;
+
+ cpp.data = path->data;
+ cpp.cr = cr;
+
+ if (flatten) {
+ status = _cairo_path_fixed_interpret_flat (path_fixed,
+ _cpp_move_to,
+ _cpp_line_to,
+ _cpp_close_path,
+ &cpp,
+ cairo_get_tolerance (cr));
+ } else {
+ status = _cairo_path_fixed_interpret (path_fixed,
+ _cpp_move_to,
+ _cpp_line_to,
+ _cpp_curve_to,
+ _cpp_close_path,
+ &cpp);
+ }
+
+ if (unlikely (status))
+ return status;
+
+ /* Sanity check the count */
+ assert (cpp.data - path->data == path->num_data);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_path_t *
+_cairo_path_create_in_error (cairo_status_t status)
+{
+ cairo_path_t *path;
+
+ /* special case NO_MEMORY so as to avoid allocations */
+ if (status == CAIRO_STATUS_NO_MEMORY)
+ return (cairo_path_t*) &_cairo_path_nil;
+
+ path = malloc (sizeof (cairo_path_t));
+ if (unlikely (path == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_path_t*) &_cairo_path_nil;
+ }
+
+ path->num_data = 0;
+ path->data = NULL;
+ path->status = status;
+
+ return path;
+}
+
+static cairo_path_t *
+_cairo_path_create_internal (cairo_path_fixed_t *path_fixed,
+ cairo_t *cr,
+ cairo_bool_t flatten)
+{
+ cairo_path_t *path;
+
+ path = malloc (sizeof (cairo_path_t));
+ if (unlikely (path == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_path_t*) &_cairo_path_nil;
+ }
+
+ path->num_data = _cairo_path_count (path, path_fixed,
+ cairo_get_tolerance (cr),
+ flatten);
+ if (path->num_data < 0) {
+ free (path);
+ return (cairo_path_t*) &_cairo_path_nil;
+ }
+
+ if (path->num_data) {
+ path->data = _cairo_malloc_ab (path->num_data,
+ sizeof (cairo_path_data_t));
+ if (unlikely (path->data == NULL)) {
+ free (path);
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_path_t*) &_cairo_path_nil;
+ }
+
+ path->status = _cairo_path_populate (path, path_fixed, cr, flatten);
+ } else {
+ path->data = NULL;
+ path->status = CAIRO_STATUS_SUCCESS;
+ }
+
+ return path;
+}
+
+/**
+ * cairo_path_destroy:
+ * @path: a path previously returned by either cairo_copy_path() or
+ * cairo_copy_path_flat().
+ *
+ * Immediately releases all memory associated with @path. After a call
+ * to cairo_path_destroy() the @path pointer is no longer valid and
+ * should not be used further.
+ *
+ * Note: cairo_path_destroy() should only be called with a
+ * pointer to a #cairo_path_t returned by a cairo function. Any path
+ * that is created manually (ie. outside of cairo) should be destroyed
+ * manually as well.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_path_destroy (cairo_path_t *path)
+{
+ if (path == NULL || path == &_cairo_path_nil)
+ return;
+
+ free (path->data);
+
+ free (path);
+}
+slim_hidden_def (cairo_path_destroy);
+
+/**
+ * _cairo_path_create:
+ * @path: a fixed-point, device-space path to be converted and copied
+ * @cr: the current graphics context
+ *
+ * Creates a user-space #cairo_path_t copy of the given device-space
+ * @path. The @cr parameter provides the inverse CTM for the
+ * conversion.
+ *
+ * Return value: the new copy of the path. If there is insufficient
+ * memory a pointer to a special static nil #cairo_path_t will be
+ * returned instead with status==%CAIRO_STATUS_NO_MEMORY and
+ * data==%NULL.
+ **/
+cairo_path_t *
+_cairo_path_create (cairo_path_fixed_t *path,
+ cairo_t *cr)
+{
+ return _cairo_path_create_internal (path, cr, FALSE);
+}
+
+/**
+ * _cairo_path_create_flat:
+ * @path: a fixed-point, device-space path to be flattened, converted and copied
+ * @cr: the current graphics context
+ *
+ * Creates a flattened, user-space #cairo_path_t copy of the given
+ * device-space @path. The @cr parameter provide the inverse CTM
+ * for the conversion, as well as the tolerance value to control the
+ * accuracy of the flattening.
+ *
+ * Return value: the flattened copy of the path. If there is insufficient
+ * memory a pointer to a special static nil #cairo_path_t will be
+ * returned instead with status==%CAIRO_STATUS_NO_MEMORY and
+ * data==%NULL.
+ **/
+cairo_path_t *
+_cairo_path_create_flat (cairo_path_fixed_t *path,
+ cairo_t *cr)
+{
+ return _cairo_path_create_internal (path, cr, TRUE);
+}
+
+/**
+ * _cairo_path_append_to_context:
+ * @path: the path data to be appended
+ * @cr: a cairo context
+ *
+ * Append @path to the current path within @cr.
+ *
+ * Return value: %CAIRO_STATUS_INVALID_PATH_DATA if the data in @path
+ * is invalid, and %CAIRO_STATUS_SUCCESS otherwise.
+ **/
+cairo_status_t
+_cairo_path_append_to_context (const cairo_path_t *path,
+ cairo_t *cr)
+{
+ const cairo_path_data_t *p, *end;
+
+ end = &path->data[path->num_data];
+ for (p = &path->data[0]; p < end; p += p->header.length) {
+ switch (p->header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ if (unlikely (p->header.length < 2))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ cairo_move_to (cr, p[1].point.x, p[1].point.y);
+ break;
+
+ case CAIRO_PATH_LINE_TO:
+ if (unlikely (p->header.length < 2))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ cairo_line_to (cr, p[1].point.x, p[1].point.y);
+ break;
+
+ case CAIRO_PATH_CURVE_TO:
+ if (unlikely (p->header.length < 4))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ cairo_curve_to (cr,
+ p[1].point.x, p[1].point.y,
+ p[2].point.x, p[2].point.y,
+ p[3].point.x, p[3].point.y);
+ break;
+
+ case CAIRO_PATH_CLOSE_PATH:
+ if (unlikely (p->header.length < 1))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ cairo_close_path (cr);
+ break;
+
+ default:
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+ }
+
+ if (unlikely (cr->status))
+ return cr->status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-pattern-inline.h b/src/cairo-pattern-inline.h
new file mode 100755
index 000000000..97e8ea034
--- /dev/null
+++ b/src/cairo-pattern-inline.h
@@ -0,0 +1,65 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_PATTERN_INLINE_H
+#define CAIRO_PATTERN_INLINE_H
+
+#include "cairo-pattern-private.h"
+
+#include "cairo-list-inline.h"
+
+CAIRO_BEGIN_DECLS
+
+static inline void
+_cairo_pattern_add_observer (cairo_pattern_t *pattern,
+ cairo_pattern_observer_t *observer,
+ void (*func) (cairo_pattern_observer_t *,
+ cairo_pattern_t *,
+ unsigned int))
+{
+ observer->notify = func;
+ cairo_list_add (&observer->link, &pattern->observers);
+}
+
+static inline cairo_surface_t *
+_cairo_pattern_get_source (const cairo_surface_pattern_t *pattern,
+ cairo_rectangle_int_t *extents)
+{
+ return _cairo_surface_get_source (pattern->surface, extents);
+}
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_PATTERN_INLINE_H */
diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
new file mode 100755
index 000000000..a0092b135
--- /dev/null
+++ b/src/cairo-pattern-private.h
@@ -0,0 +1,397 @@
+/* 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_PATTERN_PRIVATE_H
+#define CAIRO_PATTERN_PRIVATE_H
+
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+#include "cairo-list-private.h"
+#include "cairo-surface-private.h"
+
+#include <stdio.h> /* FILE* */
+
+#define CAIRO_MAX_SIGMA 4 /* 4 defined in skia */
+#define CAIRO_DEFAULT_SIGMA 0
+#define CAIRO_MAX_BLUR 64
+#define CAIRO_MIN_SHRINK_SIZE 32
+#define CAIRO_MIN_LINE_WIDTH 1.0
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_pattern_observer cairo_pattern_observer_t;
+
+enum {
+ CAIRO_PATTERN_NOTIFY_MATRIX = 0x1,
+ CAIRO_PATTERN_NOTIFY_FILTER = 0x2,
+ CAIRO_PATTERN_NOTIFY_EXTEND = 0x4,
+ CAIRO_PATTERN_NOTIFY_OPACITY = 0x9,
+};
+
+struct _cairo_pattern_observer {
+ void (*notify) (cairo_pattern_observer_t *,
+ cairo_pattern_t *pattern,
+ unsigned int flags);
+ cairo_list_t link;
+};
+
+struct _cairo_pattern {
+ cairo_reference_count_t ref_count;
+ cairo_status_t status;
+ cairo_user_data_array_t user_data;
+ cairo_list_t observers;
+
+ cairo_pattern_type_t type;
+
+ cairo_filter_t filter;
+ cairo_extend_t extend;
+ cairo_bool_t has_component_alpha;
+
+ cairo_matrix_t matrix;
+ double opacity;
+
+ /* we use this to shrink image before we apply blur */
+ unsigned int shrink_factor_x;
+ unsigned int shrink_factor_y;
+ unsigned int x_radius;
+ unsigned int y_radius;
+ double x_sigma;
+ double y_sigma;
+
+ double *convolution_matrix;
+ cairo_bool_t convolution_changed;
+
+ /* FIXME: I don't like to attach shadow to pattern. However,
+ * cairo does not have a way to pass shadow info to backend.
+ * so we attach shadow info in pattern */
+ cairo_shadow_t shadow;
+};
+
+struct _cairo_solid_pattern {
+ cairo_pattern_t base;
+ cairo_color_t color;
+};
+
+typedef struct _cairo_surface_pattern {
+ cairo_pattern_t base;
+
+ cairo_surface_t *surface;
+} cairo_surface_pattern_t;
+
+typedef struct _cairo_gradient_stop {
+ double offset;
+ cairo_color_stop_t color;
+} cairo_gradient_stop_t;
+
+typedef struct _cairo_gradient_pattern {
+ cairo_pattern_t base;
+
+ unsigned int n_stops;
+ unsigned int stops_size;
+ cairo_gradient_stop_t *stops;
+ cairo_gradient_stop_t stops_embedded[2];
+} cairo_gradient_pattern_t;
+
+typedef struct _cairo_linear_pattern {
+ cairo_gradient_pattern_t base;
+
+ cairo_point_double_t pd1;
+ cairo_point_double_t pd2;
+} cairo_linear_pattern_t;
+
+typedef struct _cairo_radial_pattern {
+ cairo_gradient_pattern_t base;
+
+ cairo_circle_double_t cd1;
+ cairo_circle_double_t cd2;
+} cairo_radial_pattern_t;
+
+typedef union {
+ cairo_gradient_pattern_t base;
+
+ cairo_linear_pattern_t linear;
+ cairo_radial_pattern_t radial;
+} cairo_gradient_pattern_union_t;
+
+/*
+ * A mesh patch is a tensor-product patch (bicubic Bezier surface
+ * patch). It has 16 control points. Each set of 4 points along the
+ * sides of the 4x4 grid of control points is a Bezier curve that
+ * defines one side of the patch. A color is assigned to each
+ * corner. The inner 4 points provide additional control over the
+ * shape and the color mapping.
+ *
+ * Cairo uses the same convention as the PDF Reference for numbering
+ * the points and side of the patch.
+ *
+ *
+ * Side 1
+ *
+ * p[0][3] p[1][3] p[2][3] p[3][3]
+ * Side 0 p[0][2] p[1][2] p[2][2] p[3][2] Side 2
+ * p[0][1] p[1][1] p[2][1] p[3][1]
+ * p[0][0] p[1][0] p[2][0] p[3][0]
+ *
+ * Side 3
+ *
+ *
+ * Point Color
+ * -------------------------
+ * points[0][0] colors[0]
+ * points[0][3] colors[1]
+ * points[3][3] colors[2]
+ * points[3][0] colors[3]
+ */
+
+typedef struct _cairo_mesh_patch {
+ cairo_point_double_t points[4][4];
+ cairo_color_t colors[4];
+} cairo_mesh_patch_t;
+
+typedef struct _cairo_mesh_pattern {
+ cairo_pattern_t base;
+
+ cairo_array_t patches;
+ cairo_mesh_patch_t *current_patch;
+ int current_side;
+ cairo_bool_t has_control_point[4];
+ cairo_bool_t has_color[4];
+} cairo_mesh_pattern_t;
+
+typedef struct _cairo_raster_source_pattern {
+ cairo_pattern_t base;
+
+ cairo_content_t content;
+ cairo_rectangle_int_t extents;
+
+ cairo_raster_source_acquire_func_t acquire;
+ cairo_raster_source_release_func_t release;
+ cairo_raster_source_snapshot_func_t snapshot;
+ cairo_raster_source_copy_func_t copy;
+ cairo_raster_source_finish_func_t finish;
+
+ /* an explicit pre-allocated member in preference to the general user-data */
+ void *user_data;
+} cairo_raster_source_pattern_t;
+
+typedef union {
+ cairo_pattern_t base;
+
+ cairo_solid_pattern_t solid;
+ cairo_surface_pattern_t surface;
+ cairo_gradient_pattern_union_t gradient;
+ cairo_mesh_pattern_t mesh;
+ cairo_raster_source_pattern_t raster_source;
+} cairo_pattern_union_t;
+
+/* cairo-pattern.c */
+
+cairo_private cairo_pattern_t *
+_cairo_pattern_create_in_error (cairo_status_t status);
+
+cairo_private cairo_status_t
+_cairo_pattern_create_copy (cairo_pattern_t **pattern,
+ const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_pattern_init (cairo_pattern_t *pattern,
+ cairo_pattern_type_t type);
+
+cairo_private cairo_status_t
+_cairo_pattern_init_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_pattern_init_static_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other);
+
+cairo_private cairo_status_t
+_cairo_pattern_init_snapshot (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
+ const cairo_color_t *color);
+
+cairo_private void
+_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
+ cairo_surface_t *surface);
+
+cairo_private void
+_cairo_pattern_fini (cairo_pattern_t *pattern);
+
+cairo_private cairo_pattern_t *
+_cairo_pattern_create_solid (const cairo_color_t *color);
+
+cairo_private void
+_cairo_pattern_transform (cairo_pattern_t *pattern,
+ const cairo_matrix_t *ctm_inverse);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_opaque (const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_clear (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents,
+ cairo_color_t *color);
+
+cairo_private void
+_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
+ double max_value,
+ cairo_matrix_t *out_matrix,
+ cairo_circle_double_t out_circle[2]);
+
+cairo_private cairo_bool_t
+_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial);
+
+cairo_private void
+_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
+ double x0, double y0,
+ double x1, double y1,
+ double tolerance,
+ double out_range[2]);
+
+cairo_private void
+_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
+ double t,
+ cairo_circle_double_t *out_circle);
+
+cairo_private void
+_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
+ double *out_min,
+ double *out_max);
+
+cairo_private cairo_bool_t
+_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
+ double *out_xmin,
+ double *out_ymin,
+ double *out_xmax,
+ double *out_ymax);
+
+cairo_private_no_warn cairo_filter_t
+_cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_rectangle_int_t *sample);
+
+cairo_private void
+_cairo_pattern_get_extents (const cairo_pattern_t *pattern,
+ cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_pattern_get_exact_extents (const cairo_pattern_t *pattern,
+ cairo_rectangle_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern,
+ cairo_rectangle_int_t *extents);
+
+cairo_private unsigned long
+_cairo_pattern_hash (const cairo_pattern_t *pattern);
+
+cairo_private unsigned long
+_cairo_linear_pattern_hash (unsigned long hash,
+ const cairo_linear_pattern_t *linear);
+
+cairo_private unsigned long
+_cairo_radial_pattern_hash (unsigned long hash,
+ const cairo_radial_pattern_t *radial);
+
+cairo_private cairo_bool_t
+_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
+ const cairo_linear_pattern_t *b);
+
+cairo_private unsigned long
+_cairo_pattern_size (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
+ const cairo_radial_pattern_t *b);
+
+cairo_private cairo_bool_t
+_cairo_pattern_equal (const cairo_pattern_t *a,
+ const cairo_pattern_t *b);
+
+/* cairo-mesh-pattern-rasterizer.c */
+
+cairo_private void
+_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
+ void *data,
+ int width,
+ int height,
+ int stride,
+ double x_offset,
+ double y_offset);
+
+cairo_private cairo_surface_t *
+_cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern,
+ cairo_surface_t *target,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern,
+ cairo_surface_t *surface);
+
+cairo_private cairo_status_t
+_cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern);
+
+cairo_private cairo_status_t
+_cairo_raster_source_pattern_init_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern);
+
+cairo_private cairo_status_t
+_cairo_pattern_create_gaussian_matrix (cairo_pattern_t *pattern,
+ double line_width);
+
+unsigned long
+_cairo_pattern_hash_with_hash (unsigned long hash,
+ const cairo_pattern_t *pattern,
+ const cairo_bool_t use_color);
+
+cairo_private void
+_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_PATTERN_PRIVATE */
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
new file mode 100755
index 000000000..4d6094ba2
--- /dev/null
+++ b/src/cairo-pattern.c
@@ -0,0 +1,5076 @@
+/* -*- 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-array-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-surface-snapshot-inline.h"
+
+#include <float.h>
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+/**
+ * SECTION:cairo-pattern
+ * @Title: cairo_pattern_t
+ * @Short_Description: Sources for drawing
+ * @See_Also: #cairo_t, #cairo_surface_t
+ *
+ * #cairo_pattern_t is the paint with which cairo draws.
+ * The primary use of patterns is as the source for all cairo drawing
+ * operations, although they can also be used as masks, that is, as the
+ * brush too.
+ *
+ * A cairo pattern is created by using one of the many constructors,
+ * of the form
+ * <function>cairo_pattern_create_<emphasis>type</emphasis>()</function>
+ * or implicitly through
+ * <function>cairo_set_source_<emphasis>type</emphasis>()</function>
+ * functions.
+ **/
+
+static freed_pool_t freed_pattern_pool[5];
+
+static const cairo_solid_pattern_t _cairo_pattern_nil = {
+ {
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_NO_MEMORY, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { NULL, NULL }, /* observers */
+
+ CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_FILTER_DEFAULT, /* filter */
+ CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */
+ FALSE, /* has component alpha */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ 1.0 /* opacity */
+ }
+};
+
+static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = {
+ {
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_NULL_POINTER, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { NULL, NULL }, /* observers */
+
+ CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_FILTER_DEFAULT, /* filter */
+ CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */
+ FALSE, /* has component alpha */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ 1.0 /* opacity */
+ }
+};
+
+const cairo_solid_pattern_t _cairo_pattern_black = {
+ {
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_SUCCESS, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { NULL, NULL }, /* observers */
+
+ CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_FILTER_NEAREST, /* filter */
+ CAIRO_EXTEND_REPEAT, /* extend */
+ FALSE, /* has component alpha */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ 1.0 /* opacity */
+ },
+ { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */
+};
+
+const cairo_solid_pattern_t _cairo_pattern_clear = {
+ {
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_SUCCESS, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { NULL, NULL }, /* observers */
+
+ CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_FILTER_NEAREST, /* filter */
+ CAIRO_EXTEND_REPEAT, /* extend */
+ FALSE, /* has component alpha */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ 1.0, /* opacity */
+ },
+ { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */
+};
+
+const cairo_solid_pattern_t _cairo_pattern_white = {
+ {
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_SUCCESS, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { NULL, NULL }, /* observers */
+
+ CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_FILTER_NEAREST, /* filter */
+ CAIRO_EXTEND_REPEAT, /* extend */
+ FALSE, /* has component alpha */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ 1.0 /* opacity */
+ },
+ { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */
+};
+
+static void
+_cairo_pattern_notify_observers (cairo_pattern_t *pattern,
+ unsigned int flags)
+{
+ cairo_pattern_observer_t *pos;
+
+ cairo_list_foreach_entry (pos, cairo_pattern_observer_t, &pattern->observers, link)
+ pos->notify (pos, pattern, flags);
+}
+
+/**
+ * _cairo_pattern_set_error:
+ * @pattern: a pattern
+ * @status: a status value indicating an error
+ *
+ * Atomically sets pattern->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS.
+ *
+ * All assignments of an error status to pattern->status should happen
+ * through _cairo_pattern_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the nil
+ * objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ **/
+static cairo_status_t
+_cairo_pattern_set_error (cairo_pattern_t *pattern,
+ cairo_status_t status)
+{
+ if (status == CAIRO_STATUS_SUCCESS)
+ return status;
+
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&pattern->status, status);
+
+ return _cairo_error (status);
+}
+
+void
+_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
+{
+#if HAVE_VALGRIND
+ switch (type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ break;
+ }
+#endif
+
+ pattern->type = type;
+ pattern->status = CAIRO_STATUS_SUCCESS;
+
+ /* Set the reference count to zero for on-stack patterns.
+ * Callers needs to explicitly increment the count for heap allocations. */
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
+
+ _cairo_user_data_array_init (&pattern->user_data);
+
+ if (type == CAIRO_PATTERN_TYPE_SURFACE ||
+ type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT;
+ else
+ pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT;
+
+ pattern->filter = CAIRO_FILTER_DEFAULT;
+ pattern->opacity = 1.0;
+
+ pattern->has_component_alpha = FALSE;
+
+ cairo_matrix_init_identity (&pattern->matrix);
+
+ cairo_list_init (&pattern->observers);
+
+ pattern->x_sigma = 0.0f;
+ pattern->y_sigma = 0.0;
+ pattern->x_radius = 0;
+ pattern->y_radius = 0;
+ pattern->convolution_matrix = NULL;
+ pattern->convolution_changed = FALSE;
+
+ memset (&pattern->shadow, 0, sizeof (cairo_shadow_t));
+}
+
+static cairo_status_t
+_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern,
+ const cairo_gradient_pattern_t *other)
+{
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern;
+ cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other;
+
+ *dst = *src;
+ }
+ else
+ {
+ cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern;
+ cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other;
+
+ *dst = *src;
+ }
+
+ if (other->stops == other->stops_embedded)
+ pattern->stops = pattern->stops_embedded;
+ else if (other->stops)
+ {
+ pattern->stops = _cairo_malloc_ab (other->stops_size,
+ sizeof (cairo_gradient_stop_t));
+ if (unlikely (pattern->stops == NULL)) {
+ pattern->stops_size = 0;
+ pattern->n_stops = 0;
+ return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
+ }
+
+ memcpy (pattern->stops, other->stops,
+ other->n_stops * sizeof (cairo_gradient_stop_t));
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t *pattern,
+ const cairo_mesh_pattern_t *other)
+{
+ void *data = NULL;
+ *pattern = *other;
+
+ _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t));
+ data = _cairo_array_index_const (&other->patches, 0);
+ if (data == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ return _cairo_array_append_multiple (&pattern->patches,
+ data,
+ _cairo_array_num_elements (&other->patches));
+}
+
+cairo_status_t
+_cairo_pattern_init_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other)
+{
+ cairo_status_t status;
+
+ if (other->status)
+ return _cairo_pattern_set_error (pattern, other->status);
+
+ switch (other->type) {
+ case CAIRO_PATTERN_TYPE_SOLID: {
+ cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern;
+ cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)));
+
+ *dst = *src;
+ } break;
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern;
+ cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)));
+
+ *dst = *src;
+ cairo_surface_reference (dst->surface);
+ } break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL: {
+ cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern;
+ cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other;
+
+ if (other->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)));
+ } else {
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)));
+ }
+
+ status = _cairo_gradient_pattern_init_copy (dst, src);
+ if (unlikely (status))
+ return status;
+
+ } break;
+ case CAIRO_PATTERN_TYPE_MESH: {
+ cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern;
+ cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)));
+
+ status = _cairo_mesh_pattern_init_copy (dst, src);
+ if (unlikely (status))
+ return status;
+
+ } break;
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
+ status = _cairo_raster_source_pattern_init_copy (pattern, other);
+ if (unlikely (status))
+ return status;
+ } break;
+ }
+
+ /* The reference count and user_data array are unique to the copy. */
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
+ _cairo_user_data_array_init (&pattern->user_data);
+
+ /* make separate copy of convolution matrix */
+ if (other->convolution_matrix) {
+ int col = 2 * other->x_radius + 1;
+ int row = 2 * other->y_radius + 1;
+ int size = row * col;
+
+ pattern->convolution_matrix = _cairo_malloc_ab (size, sizeof(double));
+ if (pattern->convolution_matrix == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ memcpy (pattern->convolution_matrix, other->convolution_matrix,
+ sizeof (double) * size);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pattern_init_static_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other)
+{
+ int size;
+
+ assert (other->status == CAIRO_STATUS_SUCCESS);
+
+ switch (other->type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ size = sizeof (cairo_solid_pattern_t);
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ size = sizeof (cairo_surface_pattern_t);
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ size = sizeof (cairo_linear_pattern_t);
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ size = sizeof (cairo_radial_pattern_t);
+ break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ size = sizeof (cairo_mesh_pattern_t);
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ size = sizeof (cairo_raster_source_pattern_t);
+ break;
+ }
+
+ memcpy (pattern, other, size);
+
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
+ _cairo_user_data_array_init (&pattern->user_data);
+}
+
+cairo_status_t
+_cairo_pattern_init_snapshot (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other)
+{
+ cairo_status_t status;
+
+ /* We don't bother doing any fancy copy-on-write implementation
+ * for the pattern's data. It's generally quite tiny. */
+ status = _cairo_pattern_init_copy (pattern, other);
+ if (unlikely (status))
+ return status;
+
+ /* But we do let the surface snapshot stuff be as fancy as it
+ * would like to be. */
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern =
+ (cairo_surface_pattern_t *) pattern;
+ cairo_surface_t *surface = surface_pattern->surface;
+
+ surface_pattern->surface = _cairo_surface_snapshot (surface);
+
+ cairo_surface_destroy (surface);
+
+ status = surface_pattern->surface->status;
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ status = _cairo_raster_source_pattern_snapshot (pattern);
+
+ return status;
+}
+
+void
+_cairo_pattern_fini (cairo_pattern_t *pattern)
+{
+ _cairo_user_data_array_fini (&pattern->user_data);
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_pattern_t *surface_pattern =
+ (cairo_surface_pattern_t *) pattern;
+
+ cairo_surface_destroy (surface_pattern->surface);
+ } break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL: {
+ cairo_gradient_pattern_t *gradient =
+ (cairo_gradient_pattern_t *) pattern;
+
+ if (gradient->stops && gradient->stops != gradient->stops_embedded)
+ free (gradient->stops);
+ } break;
+ case CAIRO_PATTERN_TYPE_MESH: {
+ cairo_mesh_pattern_t *mesh =
+ (cairo_mesh_pattern_t *) pattern;
+
+ _cairo_array_fini (&mesh->patches);
+ } break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ _cairo_raster_source_pattern_finish (pattern);
+ break;
+ }
+
+ if (pattern->convolution_matrix)
+ free (pattern->convolution_matrix);
+
+#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;
+ case CAIRO_PATTERN_TYPE_MESH:
+ VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_mesh_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ break;
+ }
+#endif
+}
+
+cairo_status_t
+_cairo_pattern_create_copy (cairo_pattern_t **pattern_out,
+ const cairo_pattern_t *other)
+{
+ cairo_pattern_t *pattern;
+ cairo_status_t status;
+
+ if (other->status)
+ return other->status;
+
+ switch (other->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ pattern = 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;
+ case CAIRO_PATTERN_TYPE_MESH:
+ pattern = malloc (sizeof (cairo_mesh_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ pattern = malloc (sizeof (cairo_raster_source_pattern_t));
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ }
+ if (unlikely (pattern == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_pattern_init_copy (pattern, other);
+ if (unlikely (status)) {
+ free (pattern);
+ return status;
+ }
+
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1);
+ *pattern_out = pattern;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
+ const cairo_color_t *color)
+{
+ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID);
+ pattern->color = *color;
+}
+
+void
+_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
+ cairo_surface_t *surface)
+{
+ if (surface->status) {
+ /* Force to solid to simplify the pattern_fini process. */
+ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID);
+ _cairo_pattern_set_error (&pattern->base, surface->status);
+ return;
+ }
+
+ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE);
+
+ pattern->surface = cairo_surface_reference (surface);
+}
+
+static void
+_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern,
+ cairo_pattern_type_t type)
+{
+ _cairo_pattern_init (&pattern->base, type);
+
+ pattern->n_stops = 0;
+ pattern->stops_size = 0;
+ pattern->stops = NULL;
+}
+
+static void
+_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
+ double x0, double y0, double x1, double y1)
+{
+ _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR);
+
+ pattern->pd1.x = x0;
+ pattern->pd1.y = y0;
+ pattern->pd2.x = x1;
+ pattern->pd2.y = y1;
+}
+
+static void
+_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
+ double cx0, double cy0, double radius0,
+ double cx1, double cy1, double radius1)
+{
+ _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
+
+ pattern->cd1.center.x = cx0;
+ pattern->cd1.center.y = cy0;
+ pattern->cd1.radius = fabs (radius0);
+ pattern->cd2.center.x = cx1;
+ pattern->cd2.center.y = cy1;
+ pattern->cd2.radius = fabs (radius1);
+}
+
+cairo_pattern_t *
+_cairo_pattern_create_solid (const cairo_color_t *color)
+{
+ cairo_solid_pattern_t *pattern;
+
+ pattern =
+ _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]);
+ if (unlikely (pattern == NULL)) {
+ /* None cached, need to create a new pattern. */
+ pattern = malloc (sizeof (cairo_solid_pattern_t));
+ if (unlikely (pattern == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_pattern_t *) &_cairo_pattern_nil;
+ }
+ }
+
+ _cairo_pattern_init_solid (pattern, color);
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+ return &pattern->base;
+}
+
+cairo_pattern_t *
+_cairo_pattern_create_in_error (cairo_status_t status)
+{
+ cairo_pattern_t *pattern;
+
+ if (status == CAIRO_STATUS_NO_MEMORY)
+ return (cairo_pattern_t *)&_cairo_pattern_nil.base;
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK);
+ if (pattern->status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_pattern_set_error (pattern, status);
+
+ return pattern;
+}
+
+/**
+ * cairo_pattern_create_rgb:
+ * @red: red component of the color
+ * @green: green component of the color
+ * @blue: blue component of the color
+ *
+ * Creates a new #cairo_pattern_t corresponding to an opaque color. The
+ * color components are floating point numbers in the range 0 to 1.
+ * If the values passed in are outside that range, they will be
+ * clamped.
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect
+ * the status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.0
+ **/
+cairo_pattern_t *
+cairo_pattern_create_rgb (double red, double green, double blue)
+{
+ return cairo_pattern_create_rgba (red, green, blue, 1.0);
+}
+slim_hidden_def (cairo_pattern_create_rgb);
+
+/**
+ * cairo_pattern_create_rgba:
+ * @red: red component of the color
+ * @green: green component of the color
+ * @blue: blue component of the color
+ * @alpha: alpha component of the color
+ *
+ * Creates a new #cairo_pattern_t corresponding to a translucent color.
+ * The color components are floating point numbers in the range 0 to
+ * 1. If the values passed in are outside that range, they will be
+ * clamped.
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect
+ * the status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.0
+ **/
+cairo_pattern_t *
+cairo_pattern_create_rgba (double red, double green, double blue,
+ double alpha)
+{
+ cairo_color_t color;
+
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+ alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+ _cairo_color_init_rgba (&color, red, green, blue, alpha);
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ return _cairo_pattern_create_solid (&color);
+}
+slim_hidden_def (cairo_pattern_create_rgba);
+
+/**
+ * cairo_pattern_create_for_surface:
+ * @surface: the surface
+ *
+ * Create a new #cairo_pattern_t for the given surface.
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect
+ * the status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.0
+ **/
+cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface)
+{
+ cairo_surface_pattern_t *pattern;
+
+ if (surface == NULL) {
+ _cairo_error_throw (CAIRO_STATUS_NULL_POINTER);
+ return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer;
+ }
+
+ if (surface->status)
+ return _cairo_pattern_create_in_error (surface->status);
+
+ pattern =
+ _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]);
+ if (unlikely (pattern == NULL)) {
+ pattern = malloc (sizeof (cairo_surface_pattern_t));
+ if (unlikely (pattern == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_pattern_t *)&_cairo_pattern_nil.base;
+ }
+ }
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ _cairo_pattern_init_for_surface (pattern, surface);
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+ return &pattern->base;
+}
+slim_hidden_def (cairo_pattern_create_for_surface);
+
+/**
+ * cairo_pattern_create_linear:
+ * @x0: x coordinate of the start point
+ * @y0: y coordinate of the start point
+ * @x1: x coordinate of the end point
+ * @y1: y coordinate of the end point
+ *
+ * Create a new linear gradient #cairo_pattern_t along the line defined
+ * by (x0, y0) and (x1, y1). Before using the gradient pattern, a
+ * number of color stops should be defined using
+ * cairo_pattern_add_color_stop_rgb() or
+ * cairo_pattern_add_color_stop_rgba().
+ *
+ * Note: The coordinates here are in pattern space. For a new pattern,
+ * pattern space is identical to user space, but the relationship
+ * between the spaces can be changed with cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect
+ * the status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.0
+ **/
+cairo_pattern_t *
+cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
+{
+ cairo_linear_pattern_t *pattern;
+
+ pattern =
+ _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]);
+ if (unlikely (pattern == NULL)) {
+ pattern = malloc (sizeof (cairo_linear_pattern_t));
+ if (unlikely (pattern == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+ }
+ }
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ _cairo_pattern_init_linear (pattern, x0, y0, x1, y1);
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1);
+
+ return &pattern->base.base;
+}
+
+/**
+ * cairo_pattern_create_radial:
+ * @cx0: x coordinate for the center of the start circle
+ * @cy0: y coordinate for the center of the start circle
+ * @radius0: radius of the start circle
+ * @cx1: x coordinate for the center of the end circle
+ * @cy1: y coordinate for the center of the end circle
+ * @radius1: radius of the end circle
+ *
+ * Creates a new radial gradient #cairo_pattern_t between the two
+ * circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1). Before using the
+ * gradient pattern, a number of color stops should be defined using
+ * cairo_pattern_add_color_stop_rgb() or
+ * cairo_pattern_add_color_stop_rgba().
+ *
+ * Note: The coordinates here are in pattern space. For a new pattern,
+ * pattern space is identical to user space, but the relationship
+ * between the spaces can be changed with cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect
+ * the status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.0
+ **/
+cairo_pattern_t *
+cairo_pattern_create_radial (double cx0, double cy0, double radius0,
+ double cx1, double cy1, double radius1)
+{
+ cairo_radial_pattern_t *pattern;
+
+ pattern =
+ _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]);
+ if (unlikely (pattern == NULL)) {
+ pattern = malloc (sizeof (cairo_radial_pattern_t));
+ if (unlikely (pattern == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+ }
+ }
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1);
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1);
+
+ return &pattern->base.base;
+}
+
+/* This order is specified in the diagram in the documentation for
+ * cairo_pattern_create_mesh() */
+static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 };
+static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 };
+static const int mesh_control_point_i[4] = { 1, 1, 2, 2 };
+static const int mesh_control_point_j[4] = { 1, 2, 2, 1 };
+
+/**
+ * cairo_pattern_create_mesh:
+ *
+ * Create a new mesh pattern.
+ *
+ * Mesh patterns are tensor-product patch meshes (type 7 shadings in
+ * PDF). Mesh patterns may also be used to create other types of
+ * shadings that are special cases of tensor-product patch meshes such
+ * as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded
+ * triangle meshes (type 4 and 5 shadings in PDF).
+ *
+ * Mesh patterns consist of one or more tensor-product patches, which
+ * should be defined before using the mesh pattern. Using a mesh
+ * pattern with a partially defined patch as source or mask will put
+ * the context in an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2,
+ * 3) and by 4 additional control points (P0, P1, P2, P3) that provide
+ * further control over the patch and complete the definition of the
+ * tensor-product patch. The corner C0 is the first point of the
+ * patch.
+ *
+ * Degenerate sides are permitted so straight lines may be used. A
+ * zero length line on one side may be used to create 3 sided patches.
+ *
+ * <informalexample><screen>
+ * C1 Side 1 C2
+ * +---------------+
+ * | |
+ * | P1 P2 |
+ * | |
+ * Side 0 | | Side 2
+ * | |
+ * | |
+ * | P0 P3 |
+ * | |
+ * +---------------+
+ * C0 Side 3 C3
+ * </screen></informalexample>
+ *
+ * Each patch is constructed by first calling
+ * cairo_mesh_pattern_begin_patch(), then cairo_mesh_pattern_move_to()
+ * to specify the first point in the patch (C0). Then the sides are
+ * specified with calls to cairo_mesh_pattern_curve_to() and
+ * cairo_mesh_pattern_line_to().
+ *
+ * The four additional control points (P0, P1, P2, P3) in a patch can
+ * be specified with cairo_mesh_pattern_set_control_point().
+ *
+ * At each corner of the patch (C0, C1, C2, C3) a color may be
+ * specified with cairo_mesh_pattern_set_corner_color_rgb() or
+ * cairo_mesh_pattern_set_corner_color_rgba(). Any corner whose color
+ * is not explicitly specified defaults to transparent black.
+ *
+ * A Coons patch is a special case of the tensor-product patch where
+ * the control points are implicitly defined by the sides of the
+ * patch. The default value for any control point not specified is the
+ * implicit value for a Coons patch, i.e. if no control points are
+ * specified the patch is a Coons patch.
+ *
+ * A triangle is a special case of the tensor-product patch where the
+ * control points are implicitly defined by the sides of the patch,
+ * all the sides are lines and one of them has length 0, i.e. if the
+ * patch is specified using just 3 lines, it is a triangle. If the
+ * corners connected by the 0-length side have the same color, the
+ * patch is a Gouraud-shaded triangle.
+ *
+ * Patches may be oriented differently to the above diagram. For
+ * example the first point could be at the top left. The diagram only
+ * shows the relationship between the sides, corners and control
+ * points. Regardless of where the first point is located, when
+ * specifying colors, corner 0 will always be the first point, corner
+ * 1 the point between side 0 and side 1 etc.
+ *
+ * Calling cairo_mesh_pattern_end_patch() completes the current
+ * patch. If less than 4 sides have been defined, the first missing
+ * side is defined as a line from the current point to the first point
+ * of the patch (C0) and the other sides are degenerate lines from C0
+ * to C0. The corners between the added sides will all be coincident
+ * with C0 of the patch and their color will be set to be the same as
+ * the color of C0.
+ *
+ * Additional patches may be added with additional calls to
+ * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch().
+ *
+ * <informalexample><programlisting>
+ * cairo_pattern_t *pattern = cairo_pattern_create_mesh ();
+ *
+ * /&ast; Add a Coons patch &ast;/
+ * cairo_mesh_pattern_begin_patch (pattern);
+ * cairo_mesh_pattern_move_to (pattern, 0, 0);
+ * cairo_mesh_pattern_curve_to (pattern, 30, -30, 60, 30, 100, 0);
+ * cairo_mesh_pattern_curve_to (pattern, 60, 30, 130, 60, 100, 100);
+ * cairo_mesh_pattern_curve_to (pattern, 60, 70, 30, 130, 0, 100);
+ * cairo_mesh_pattern_curve_to (pattern, 30, 70, -30, 30, 0, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+ * cairo_mesh_pattern_end_patch (pattern);
+ *
+ * /&ast; Add a Gouraud-shaded triangle &ast;/
+ * cairo_mesh_pattern_begin_patch (pattern)
+ * cairo_mesh_pattern_move_to (pattern, 100, 100);
+ * cairo_mesh_pattern_line_to (pattern, 130, 130);
+ * cairo_mesh_pattern_line_to (pattern, 130, 70);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_mesh_pattern_end_patch (pattern)
+ * </programlisting></informalexample>
+ *
+ * When two patches overlap, the last one that has been added is drawn
+ * over the first one.
+ *
+ * When a patch folds over itself, points are sorted depending on
+ * their parameter coordinates inside the patch. The v coordinate
+ * ranges from 0 to 1 when moving from side 3 to side 1; the u
+ * coordinate ranges from 0 to 1 when going from side 0 to side
+ * 2. Points with higher v coordinate hide points with lower v
+ * coordinate. When two points have the same v coordinate, the one
+ * with higher u coordinate is above. This means that points nearer to
+ * side 1 are above points nearer to side 3; when this is not
+ * sufficient to decide which point is above (for example when both
+ * points belong to side 1 or side 3) points nearer to side 2 are
+ * above points nearer to side 0.
+ *
+ * For a complete definition of tensor-product patches, see the PDF
+ * specification (ISO32000), which describes the parametrization in
+ * detail.
+ *
+ * Note: The coordinates are always in pattern space. For a new
+ * pattern, pattern space is identical to user space, but the
+ * relationship between the spaces can be changed with
+ * cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the returned
+ * object and should call cairo_pattern_destroy() when finished with
+ * it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect the
+ * status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.12
+ **/
+cairo_pattern_t *
+cairo_pattern_create_mesh (void)
+{
+ cairo_mesh_pattern_t *pattern;
+
+ pattern =
+ _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]);
+ if (unlikely (pattern == NULL)) {
+ pattern = malloc (sizeof (cairo_mesh_pattern_t));
+ if (unlikely (pattern == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+ }
+ }
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH);
+ _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t));
+ pattern->current_patch = NULL;
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+ return &pattern->base;
+}
+
+/**
+ * cairo_pattern_reference:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Increases the reference count on @pattern by one. This prevents
+ * @pattern from being destroyed until a matching call to
+ * cairo_pattern_destroy() is made.
+ *
+ * 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.
+ *
+ * Since: 1.0
+ **/
+cairo_pattern_t *
+cairo_pattern_reference (cairo_pattern_t *pattern)
+{
+ if (pattern == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+ return pattern;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count));
+
+ _cairo_reference_count_inc (&pattern->ref_count);
+
+ return pattern;
+}
+slim_hidden_def (cairo_pattern_reference);
+
+/**
+ * cairo_pattern_get_type:
+ * @pattern: a #cairo_pattern_t
+ *
+ * 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,
+ * %CAIRO_STATUS_INVALID_MATRIX, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
+ * or %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_pattern_status (cairo_pattern_t *pattern)
+{
+ return pattern->status;
+}
+
+/**
+ * cairo_pattern_destroy:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Decreases the reference count on @pattern by one. If the result is
+ * zero, then @pattern and all associated resources are freed. See
+ * cairo_pattern_reference().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_pattern_destroy (cairo_pattern_t *pattern)
+{
+ cairo_pattern_type_t type;
+
+ if (pattern == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+ return;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&pattern->ref_count))
+ return;
+
+ type = pattern->type;
+ _cairo_pattern_fini (pattern);
+
+ /* maintain a small cache of freed patterns */
+ if (type < ARRAY_LENGTH (freed_pattern_pool))
+ _freed_pool_put (&freed_pattern_pool[type], pattern);
+ else
+ free (pattern);
+}
+slim_hidden_def (cairo_pattern_destroy);
+
+/**
+ * cairo_pattern_get_reference_count:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Returns the current reference count of @pattern.
+ *
+ * Return value: the current reference count of @pattern. If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_pattern_get_reference_count (cairo_pattern_t *pattern)
+{
+ if (pattern == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+ return 0;
+
+ return CAIRO_REFERENCE_COUNT_GET_VALUE (&pattern->ref_count);
+}
+
+/**
+ * cairo_pattern_get_user_data:
+ * @pattern: a #cairo_pattern_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @pattern using the
+ * specified key. If no user data has been attached with the given
+ * key this function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.4
+ **/
+void *
+cairo_pattern_get_user_data (cairo_pattern_t *pattern,
+ const cairo_user_data_key_t *key)
+{
+ return _cairo_user_data_array_get_data (&pattern->user_data,
+ key);
+}
+
+/**
+ * cairo_pattern_set_user_data:
+ * @pattern: a #cairo_pattern_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the #cairo_pattern_t
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * #cairo_t is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @pattern. To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_set_user_data (cairo_pattern_t *pattern,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy)
+{
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+ return pattern->status;
+
+ return _cairo_user_data_array_set_data (&pattern->user_data,
+ key, user_data, destroy);
+}
+
+/**
+ * cairo_mesh_pattern_begin_patch:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Begin a patch in a mesh pattern.
+ *
+ * After calling this function, the patch shape should be defined with
+ * cairo_mesh_pattern_move_to(), cairo_mesh_pattern_line_to() and
+ * cairo_mesh_pattern_curve_to().
+ *
+ * After defining the patch, cairo_mesh_pattern_end_patch() must be
+ * called before using @pattern as a source or mask.
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern already has a
+ * current patch, it will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern)
+{
+ cairo_mesh_pattern_t *mesh;
+ cairo_status_t status;
+ cairo_mesh_patch_t *current_patch;
+ int i;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ if (unlikely (mesh->current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ status = _cairo_array_allocate (&mesh->patches, 1, (void **) &current_patch);
+ if (unlikely (status)) {
+ _cairo_pattern_set_error (pattern, status);
+ return;
+ }
+
+ mesh->current_patch = current_patch;
+ mesh->current_side = -2; /* no current point */
+
+ for (i = 0; i < 4; i++)
+ mesh->has_control_point[i] = FALSE;
+
+ for (i = 0; i < 4; i++)
+ mesh->has_color[i] = FALSE;
+}
+
+
+static void
+_calc_control_point (cairo_mesh_patch_t *patch, int control_point)
+{
+ /* The Coons patch is a special case of the Tensor Product patch
+ * where the four control points are:
+ *
+ * P11 = S(1/3, 1/3)
+ * P12 = S(1/3, 2/3)
+ * P21 = S(2/3, 1/3)
+ * P22 = S(2/3, 2/3)
+ *
+ * where S is the gradient surface.
+ *
+ * When one or more control points has not been specified
+ * calculated the Coons patch control points are substituted. If
+ * no control points are specified the gradient will be a Coons
+ * patch.
+ *
+ * The equations below are defined in the ISO32000 standard.
+ */
+ cairo_point_double_t *p[3][3];
+ int cp_i, cp_j, i, j;
+
+ cp_i = mesh_control_point_i[control_point];
+ cp_j = mesh_control_point_j[control_point];
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j];
+
+ p[0][0]->x = (- 4 * p[1][1]->x
+ + 6 * (p[1][0]->x + p[0][1]->x)
+ - 2 * (p[1][2]->x + p[2][1]->x)
+ + 3 * (p[2][0]->x + p[0][2]->x)
+ - 1 * p[2][2]->x) * (1. / 9);
+
+ p[0][0]->y = (- 4 * p[1][1]->y
+ + 6 * (p[1][0]->y + p[0][1]->y)
+ - 2 * (p[1][2]->y + p[2][1]->y)
+ + 3 * (p[2][0]->y + p[0][2]->y)
+ - 1 * p[2][2]->y) * (1. / 9);
+}
+
+/**
+ * cairo_mesh_pattern_end_patch:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Indicates the end of the current patch in a mesh pattern.
+ *
+ * If the current patch has less than 4 sides, it is closed with a
+ * straight line from the current point to the first point of the
+ * patch as if cairo_mesh_pattern_line_to() was used.
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch has no current point, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern)
+{
+ cairo_mesh_pattern_t *mesh;
+ cairo_mesh_patch_t *current_patch;
+ int i;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ current_patch = mesh->current_patch;
+ if (unlikely (!current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ if (unlikely (mesh->current_side == -2)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ while (mesh->current_side < 3) {
+ int corner_num;
+
+ cairo_mesh_pattern_line_to (pattern,
+ current_patch->points[0][0].x,
+ current_patch->points[0][0].y);
+
+ corner_num = mesh->current_side + 1;
+ if (corner_num < 4 && ! mesh->has_color[corner_num]) {
+ current_patch->colors[corner_num] = current_patch->colors[0];
+ mesh->has_color[corner_num] = TRUE;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (! mesh->has_control_point[i])
+ _calc_control_point (current_patch, i);
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (! mesh->has_color[i])
+ current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT;
+ }
+
+ mesh->current_patch = NULL;
+}
+
+/**
+ * cairo_mesh_pattern_curve_to:
+ * @pattern: a #cairo_pattern_t
+ * @x1: the X coordinate of the first control point
+ * @y1: the Y coordinate of the first control point
+ * @x2: the X coordinate of the second control point
+ * @y2: the Y coordinate of the second control point
+ * @x3: the X coordinate of the end of the curve
+ * @y3: the Y coordinate of the end of the curve
+ *
+ * Adds a cubic Bézier spline to the current patch from the current
+ * point to position (@x3, @y3) in pattern-space coordinates, using
+ * (@x1, @y1) and (@x2, @y2) as the control points.
+ *
+ * If the current patch has no current point before the call to
+ * cairo_mesh_pattern_curve_to(), this function will behave as if
+ * preceded by a call to cairo_mesh_pattern_move_to(@pattern, @x1,
+ * @y1).
+ *
+ * After this call the current point will be (@x3, @y3).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has 4 sides, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_mesh_pattern_t *mesh;
+ int current_point, i, j;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ if (unlikely (!mesh->current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ if (unlikely (mesh->current_side == 3)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ if (mesh->current_side == -2)
+ cairo_mesh_pattern_move_to (pattern, x1, y1);
+
+ assert (mesh->current_side >= -1);
+ assert (pattern->status == CAIRO_STATUS_SUCCESS);
+
+ mesh->current_side++;
+
+ current_point = 3 * mesh->current_side;
+
+ current_point++;
+ i = mesh_path_point_i[current_point];
+ j = mesh_path_point_j[current_point];
+ mesh->current_patch->points[i][j].x = x1;
+ mesh->current_patch->points[i][j].y = y1;
+
+ current_point++;
+ i = mesh_path_point_i[current_point];
+ j = mesh_path_point_j[current_point];
+ mesh->current_patch->points[i][j].x = x2;
+ mesh->current_patch->points[i][j].y = y2;
+
+ current_point++;
+ if (current_point < 12) {
+ i = mesh_path_point_i[current_point];
+ j = mesh_path_point_j[current_point];
+ mesh->current_patch->points[i][j].x = x3;
+ mesh->current_patch->points[i][j].y = y3;
+ }
+}
+slim_hidden_def (cairo_mesh_pattern_curve_to);
+
+/**
+ * cairo_mesh_pattern_line_to:
+ * @pattern: a #cairo_pattern_t
+ * @x: the X coordinate of the end of the new line
+ * @y: the Y coordinate of the end of the new line
+ *
+ * Adds a line to the current patch from the current point to position
+ * (@x, @y) in pattern-space coordinates.
+ *
+ * If there is no current point before the call to
+ * cairo_mesh_pattern_line_to() this function will behave as
+ * cairo_mesh_pattern_move_to(@pattern, @x, @y).
+ *
+ * After this call the current point will be (@x, @y).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has 4 sides, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_line_to (cairo_pattern_t *pattern,
+ double x, double y)
+{
+ cairo_mesh_pattern_t *mesh;
+ cairo_point_double_t last_point;
+ int last_point_idx, i, j;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ if (unlikely (!mesh->current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ if (unlikely (mesh->current_side == 3)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ if (mesh->current_side == -2) {
+ cairo_mesh_pattern_move_to (pattern, x, y);
+ return;
+ }
+
+ last_point_idx = 3 * (mesh->current_side + 1);
+ i = mesh_path_point_i[last_point_idx];
+ j = mesh_path_point_j[last_point_idx];
+
+ last_point = mesh->current_patch->points[i][j];
+
+ cairo_mesh_pattern_curve_to (pattern,
+ (2 * last_point.x + x) * (1. / 3),
+ (2 * last_point.y + y) * (1. / 3),
+ (last_point.x + 2 * x) * (1. / 3),
+ (last_point.y + 2 * y) * (1. / 3),
+ x, y);
+}
+slim_hidden_def (cairo_mesh_pattern_line_to);
+
+/**
+ * cairo_mesh_pattern_move_to:
+ * @pattern: a #cairo_pattern_t
+ * @x: the X coordinate of the new position
+ * @y: the Y coordinate of the new position
+ *
+ * Define the first point of the current patch in a mesh pattern.
+ *
+ * After this call the current point will be (@x, @y).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has at least one side, @pattern
+ * will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_move_to (cairo_pattern_t *pattern,
+ double x, double y)
+{
+ cairo_mesh_pattern_t *mesh;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ if (unlikely (!mesh->current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ if (unlikely (mesh->current_side >= 0)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ mesh->current_side = -1;
+ mesh->current_patch->points[0][0].x = x;
+ mesh->current_patch->points[0][0].y = y;
+}
+slim_hidden_def (cairo_mesh_pattern_move_to);
+
+/**
+ * cairo_mesh_pattern_set_control_point:
+ * @pattern: a #cairo_pattern_t
+ * @point_num: the control point to set the position for
+ * @x: the X coordinate of the control point
+ * @y: the Y coordinate of the control point
+ *
+ * Set an internal control point of the current patch.
+ *
+ * Valid values for @point_num are from 0 to 3 and identify the
+ * control points as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @point_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern,
+ unsigned int point_num,
+ double x,
+ double y)
+{
+ cairo_mesh_pattern_t *mesh;
+ int i, j;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ if (unlikely (point_num > 3)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ if (unlikely (!mesh->current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ i = mesh_control_point_i[point_num];
+ j = mesh_control_point_j[point_num];
+
+ mesh->current_patch->points[i][j].x = x;
+ mesh->current_patch->points[i][j].y = y;
+ mesh->has_control_point[point_num] = TRUE;
+}
+
+/* make room for at least one more color stop */
+static cairo_status_t
+_cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
+{
+ cairo_gradient_stop_t *new_stops;
+ int old_size = pattern->stops_size;
+ int embedded_size = ARRAY_LENGTH (pattern->stops_embedded);
+ int new_size = 2 * MAX (old_size, 4);
+
+ /* we have a local buffer at pattern->stops_embedded. try to fulfill the request
+ * from there. */
+ if (old_size < embedded_size) {
+ pattern->stops = pattern->stops_embedded;
+ pattern->stops_size = embedded_size;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ assert (pattern->n_stops <= pattern->stops_size);
+
+ if (pattern->stops == pattern->stops_embedded) {
+ new_stops = _cairo_malloc_ab (new_size, sizeof (cairo_gradient_stop_t));
+ if (new_stops)
+ memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t));
+ } else {
+ new_stops = _cairo_realloc_ab (pattern->stops,
+ new_size,
+ sizeof (cairo_gradient_stop_t));
+ }
+
+ if (unlikely (new_stops == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ pattern->stops = new_stops;
+ pattern->stops_size = new_size;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_mesh_pattern_set_corner_color (cairo_mesh_pattern_t *mesh,
+ unsigned int corner_num,
+ double red, double green, double blue,
+ double alpha)
+{
+ cairo_color_t *color;
+
+ assert (mesh->current_patch);
+ assert (corner_num <= 3);
+
+ color = &mesh->current_patch->colors[corner_num];
+ color->red = red;
+ color->green = green;
+ color->blue = blue;
+ color->alpha = alpha;
+
+ color->red_short = _cairo_color_double_to_short (red);
+ color->green_short = _cairo_color_double_to_short (green);
+ color->blue_short = _cairo_color_double_to_short (blue);
+ color->alpha_short = _cairo_color_double_to_short (alpha);
+
+ mesh->has_color[corner_num] = TRUE;
+}
+
+/**
+ * cairo_mesh_pattern_set_corner_color_rgb:
+ * @pattern: a #cairo_pattern_t
+ * @corner_num: the corner to set the color for
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ *
+ * Sets the color of a corner of the current patch in a mesh pattern.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgb().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern,
+ unsigned int corner_num,
+ double red, double green, double blue)
+{
+ cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0);
+}
+
+/**
+ * cairo_mesh_pattern_set_corner_color_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @corner_num: the corner to set the color for
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ * @alpha: alpha component of color
+ *
+ * Sets the color of a corner of the current patch in a mesh pattern.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgba().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern,
+ unsigned int corner_num,
+ double red, double green, double blue,
+ double alpha)
+{
+ cairo_mesh_pattern_t *mesh;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ if (unlikely (corner_num > 3)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ if (unlikely (!mesh->current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+ alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+ _cairo_mesh_pattern_set_corner_color (mesh, corner_num, red, green, blue, alpha);
+}
+slim_hidden_def (cairo_mesh_pattern_set_corner_color_rgba);
+
+static void
+_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
+ double offset,
+ double red,
+ double green,
+ double blue,
+ double alpha)
+{
+ cairo_gradient_stop_t *stops;
+ unsigned int i;
+
+ if (pattern->n_stops >= pattern->stops_size) {
+ cairo_status_t status = _cairo_pattern_gradient_grow (pattern);
+ if (unlikely (status)) {
+ status = _cairo_pattern_set_error (&pattern->base, status);
+ return;
+ }
+ }
+
+ stops = pattern->stops;
+
+ for (i = 0; i < pattern->n_stops; i++)
+ {
+ if (offset < stops[i].offset)
+ {
+ memmove (&stops[i + 1], &stops[i],
+ sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i));
+
+ break;
+ }
+ }
+
+ stops[i].offset = offset;
+
+ stops[i].color.red = red;
+ stops[i].color.green = green;
+ stops[i].color.blue = blue;
+ stops[i].color.alpha = alpha;
+
+ stops[i].color.red_short = _cairo_color_double_to_short (red);
+ stops[i].color.green_short = _cairo_color_double_to_short (green);
+ stops[i].color.blue_short = _cairo_color_double_to_short (blue);
+ stops[i].color.alpha_short = _cairo_color_double_to_short (alpha);
+
+ pattern->n_stops++;
+}
+
+/**
+ * cairo_pattern_add_color_stop_rgb:
+ * @pattern: a #cairo_pattern_t
+ * @offset: an offset in the range [0.0 .. 1.0]
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ *
+ * Adds an opaque color stop to a gradient pattern. The offset
+ * specifies the location along the gradient's control vector. For
+ * example, a linear gradient's control vector is from (x0,y0) to
+ * (x1,y1) while a radial gradient's control vector is from any point
+ * on the start circle to the corresponding point on the end circle.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgb().
+ *
+ * If two (or more) stops are specified with identical offset values,
+ * they will be sorted according to the order in which the stops are
+ * added, (stops added earlier will compare less than stops added
+ * later). This can be useful for reliably making sharp color
+ * transitions instead of the typical blend.
+ *
+ *
+ * Note: If the pattern is not a gradient pattern, (eg. a linear or
+ * radial pattern), then the pattern will be put into an error status
+ * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
+ double offset,
+ double red,
+ double green,
+ double blue)
+{
+ cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0);
+}
+
+/**
+ * cairo_pattern_add_color_stop_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @offset: an offset in the range [0.0 .. 1.0]
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ * @alpha: alpha component of color
+ *
+ * Adds a translucent color stop to a gradient pattern. The offset
+ * specifies the location along the gradient's control vector. For
+ * example, a linear gradient's control vector is from (x0,y0) to
+ * (x1,y1) while a radial gradient's control vector is from any point
+ * on the start circle to the corresponding point on the end circle.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgba().
+ *
+ * If two (or more) stops are specified with identical offset values,
+ * they will be sorted according to the order in which the stops are
+ * added, (stops added earlier will compare less than stops added
+ * later). This can be useful for reliably making sharp color
+ * transitions instead of the typical blend.
+ *
+ * Note: If the pattern is not a gradient pattern, (eg. a linear or
+ * radial pattern), then the pattern will be put into an error status
+ * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
+ double offset,
+ double red,
+ double green,
+ double blue,
+ double alpha)
+{
+ if (pattern->status)
+ return;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
+ pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ offset = _cairo_restrict_value (offset, 0.0, 1.0);
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+ alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+ _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
+ offset, red, green, blue, alpha);
+}
+slim_hidden_def (cairo_pattern_add_color_stop_rgba);
+
+/**
+ * cairo_pattern_set_matrix:
+ * @pattern: a #cairo_pattern_t
+ * @matrix: a #cairo_matrix_t
+ *
+ * Sets the pattern's transformation matrix to @matrix. This matrix is
+ * a transformation from user space to pattern space.
+ *
+ * When a pattern is first created it always has the identity matrix
+ * for its transformation matrix, which means that pattern space is
+ * initially identical to user space.
+ *
+ * Important: Please note that the direction of this transformation
+ * matrix is from user space to pattern space. This means that if you
+ * imagine the flow from a pattern to user space (and on to device
+ * space), then coordinates in that flow will be transformed by the
+ * inverse of the pattern matrix.
+ *
+ * For example, if you want to make a pattern appear twice as large as
+ * it does by default the correct code to use is:
+ *
+ * <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().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_pattern_set_matrix (cairo_pattern_t *pattern,
+ const cairo_matrix_t *matrix)
+{
+ cairo_matrix_t inverse;
+ cairo_status_t status;
+
+ if (pattern->status)
+ return;
+
+ if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0)
+ return;
+
+ pattern->matrix = *matrix;
+ _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_MATRIX);
+
+ inverse = *matrix;
+ status = cairo_matrix_invert (&inverse);
+ if (unlikely (status))
+ status = _cairo_pattern_set_error (pattern, status);
+}
+slim_hidden_def (cairo_pattern_set_matrix);
+
+/**
+ * cairo_pattern_get_matrix:
+ * @pattern: a #cairo_pattern_t
+ * @matrix: return value for the matrix
+ *
+ * Stores the pattern's transformation matrix into @matrix.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix)
+{
+ *matrix = pattern->matrix;
+}
+
+/**
+ * cairo_pattern_set_filter:
+ * @pattern: a #cairo_pattern_t
+ * @filter: a #cairo_filter_t describing the filter to use for resizing
+ * the pattern
+ *
+ * Sets the filter to be used for resizing when using this pattern.
+ * See #cairo_filter_t for details on each filter.
+ *
+ * * Note that you might want to control filtering even when you do not
+ * have an explicit #cairo_pattern_t object, (for example when using
+ * cairo_set_source_surface()). In these cases, it is convenient to
+ * use cairo_get_source() to get access to the pattern that cairo
+ * creates implicitly. For example:
+ *
+ * <informalexample><programlisting>
+ * cairo_set_source_surface (cr, image, x, y);
+ * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+ * </programlisting></informalexample>
+ *
+ * Since: 1.0
+ **/
+void
+cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
+{
+ if (pattern->status)
+ return;
+
+ pattern->filter = filter;
+ _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_FILTER);
+}
+
+/**
+ * cairo_pattern_get_filter:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Gets the current filter for a pattern. See #cairo_filter_t
+ * for details on each filter.
+ *
+ * Return value: the current filter used for resizing the pattern.
+ *
+ * Since: 1.0
+ **/
+cairo_filter_t
+cairo_pattern_get_filter (cairo_pattern_t *pattern)
+{
+ return pattern->filter;
+}
+
+/**
+ * cairo_pattern_set_extend:
+ * @pattern: a #cairo_pattern_t
+ * @extend: a #cairo_extend_t describing how the area outside of the
+ * pattern will be drawn
+ *
+ * Sets the mode to be used for drawing outside the area of a pattern.
+ * See #cairo_extend_t for details on the semantics of each extend
+ * strategy.
+ *
+ * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
+ * and %CAIRO_EXTEND_PAD for gradient patterns.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
+{
+ if (pattern->status)
+ return;
+
+ pattern->extend = extend;
+ _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND);
+}
+
+/**
+ * cairo_pattern_get_extend:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Gets the current extend mode for a pattern. See #cairo_extend_t
+ * for details on the semantics of each extend strategy.
+ *
+ * Return value: the current extend strategy used for drawing the
+ * pattern.
+ *
+ * Since: 1.0
+ **/
+cairo_extend_t
+cairo_pattern_get_extend (cairo_pattern_t *pattern)
+{
+ return pattern->extend;
+}
+slim_hidden_def (cairo_pattern_get_extend);
+
+void
+_cairo_pattern_transform (cairo_pattern_t *pattern,
+ const cairo_matrix_t *ctm_inverse)
+{
+ if (pattern->status)
+ return;
+
+ cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
+}
+
+static cairo_bool_t
+_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
+{
+ return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON &&
+ fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON;
+}
+
+static cairo_bool_t
+_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
+{
+ /* A radial pattern is considered degenerate if it can be
+ * represented as a solid or clear pattern. This corresponds to
+ * one of the two cases:
+ *
+ * 1) The radii are both very small:
+ * |dr| < DBL_EPSILON && min (r0, r1) < DBL_EPSILON
+ *
+ * 2) The two circles have about the same radius and are very
+ * close to each other (approximately a cylinder gradient that
+ * doesn't move with the parameter):
+ * |dr| < DBL_EPSILON && max (|dx|, |dy|) < 2 * DBL_EPSILON
+ *
+ * These checks are consistent with the assumptions used in
+ * _cairo_radial_pattern_box_to_parameter ().
+ */
+
+ return fabs (radial->cd1.radius - radial->cd2.radius) < DBL_EPSILON &&
+ (MIN (radial->cd1.radius, radial->cd2.radius) < DBL_EPSILON ||
+ MAX (fabs (radial->cd1.center.x - radial->cd2.center.x),
+ fabs (radial->cd1.center.y - radial->cd2.center.y)) < 2 * DBL_EPSILON);
+}
+
+static void
+_cairo_linear_pattern_box_to_parameter (const cairo_linear_pattern_t *linear,
+ double x0, double y0,
+ double x1, double y1,
+ double range[2])
+{
+ double t0, tdx, tdy;
+ double p1x, p1y, pdx, pdy, invsqnorm;
+
+ assert (! _linear_pattern_is_degenerate (linear));
+
+ /*
+ * Linear gradients are othrogonal to the line passing through
+ * their extremes. Because of convexity, the parameter range can
+ * be computed as the convex hull (one the real line) of the
+ * parameter values of the 4 corners of the box.
+ *
+ * The parameter value t for a point (x,y) can be computed as:
+ *
+ * t = (p2 - p1) . (x,y) / |p2 - p1|^2
+ *
+ * t0 is the t value for the top left corner
+ * tdx is the difference between left and right corners
+ * tdy is the difference between top and bottom corners
+ */
+
+ p1x = linear->pd1.x;
+ p1y = linear->pd1.y;
+ pdx = linear->pd2.x - p1x;
+ pdy = linear->pd2.y - p1y;
+ invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
+ pdx *= invsqnorm;
+ pdy *= invsqnorm;
+
+ t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy;
+ tdx = (x1 - x0) * pdx;
+ tdy = (y1 - y0) * pdy;
+
+ /*
+ * Because of the linearity of the t value, tdx can simply be
+ * added the t0 to move along the top edge. After this, range[0]
+ * and range[1] represent the parameter range for the top edge, so
+ * extending it to include the whole box simply requires adding
+ * tdy to the correct extreme.
+ */
+
+ range[0] = range[1] = t0;
+ if (tdx < 0)
+ range[0] += tdx;
+ else
+ range[1] += tdx;
+
+ if (tdy < 0)
+ range[0] += tdy;
+ else
+ range[1] += tdy;
+}
+
+static cairo_bool_t
+_extend_range (double range[2], double value, cairo_bool_t valid)
+{
+ if (!valid)
+ range[0] = range[1] = value;
+ else if (value < range[0])
+ range[0] = value;
+ else if (value > range[1])
+ range[1] = value;
+
+ return TRUE;
+}
+
+/*
+ * _cairo_radial_pattern_focus_is_inside:
+ *
+ * Returns %TRUE if and only if the focus point exists and is
+ * contained in one of the two extreme circles. This condition is
+ * equivalent to one of the two extreme circles being completely
+ * contained in the other one.
+ *
+ * Note: if the focus is on the border of one of the two circles (in
+ * which case the circles are tangent in the focus point), it is not
+ * considered as contained in the circle, hence this function returns
+ * %FALSE.
+ *
+ */
+cairo_bool_t
+_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial)
+{
+ double cx, cy, cr, dx, dy, dr;
+
+ cx = radial->cd1.center.x;
+ cy = radial->cd1.center.y;
+ cr = radial->cd1.radius;
+ dx = radial->cd2.center.x - cx;
+ dy = radial->cd2.center.y - cy;
+ dr = radial->cd2.radius - cr;
+
+ return dx*dx + dy*dy < dr*dr;
+}
+
+static void
+_cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial,
+ double x0, double y0,
+ double x1, double y1,
+ double tolerance,
+ double range[2])
+{
+ double cx, cy, cr, dx, dy, dr;
+ double a, x_focus, y_focus;
+ double mindr, minx, miny, maxx, maxy;
+ cairo_bool_t valid;
+
+ assert (! _radial_pattern_is_degenerate (radial));
+ assert (x0 < x1);
+ assert (y0 < y1);
+
+ tolerance = MAX (tolerance, DBL_EPSILON);
+
+ range[0] = range[1] = 0;
+ valid = FALSE;
+
+ x_focus = y_focus = 0; /* silence gcc */
+
+ cx = radial->cd1.center.x;
+ cy = radial->cd1.center.y;
+ cr = radial->cd1.radius;
+ dx = radial->cd2.center.x - cx;
+ dy = radial->cd2.center.y - cy;
+ dr = radial->cd2.radius - cr;
+
+ /* translate by -(cx, cy) to simplify computations */
+ x0 -= cx;
+ y0 -= cy;
+ x1 -= cx;
+ y1 -= cy;
+
+ /* enlarge boundaries slightly to avoid rounding problems in the
+ * parameter range computation */
+ x0 -= DBL_EPSILON;
+ y0 -= DBL_EPSILON;
+ x1 += DBL_EPSILON;
+ y1 += DBL_EPSILON;
+
+ /* enlarge boundaries even more to avoid rounding problems when
+ * testing if a point belongs to the box */
+ minx = x0 - DBL_EPSILON;
+ miny = y0 - DBL_EPSILON;
+ maxx = x1 + DBL_EPSILON;
+ maxy = y1 + DBL_EPSILON;
+
+ /* we dont' allow negative radiuses, so we will be checking that
+ * t*dr >= mindr to consider t valid */
+ mindr = -(cr + DBL_EPSILON);
+
+ /*
+ * After the previous transformations, the start circle is
+ * centered in the origin and has radius cr. A 1-unit change in
+ * the t parameter corresponds to dx,dy,dr changes in the x,y,r of
+ * the circle (center coordinates, radius).
+ *
+ * To compute the minimum range needed to correctly draw the
+ * pattern, we start with an empty range and extend it to include
+ * the circles touching the bounding box or within it.
+ */
+
+ /*
+ * Focus, the point where the circle has radius == 0.
+ *
+ * r = cr + t * dr = 0
+ * t = -cr / dr
+ *
+ * If the radius is constant (dr == 0) there is no focus (the
+ * gradient represents a cylinder instead of a cone).
+ */
+ if (fabs (dr) >= DBL_EPSILON) {
+ double t_focus;
+
+ t_focus = -cr / dr;
+ x_focus = t_focus * dx;
+ y_focus = t_focus * dy;
+ if (minx <= x_focus && x_focus <= maxx &&
+ miny <= y_focus && y_focus <= maxy)
+ {
+ valid = _extend_range (range, t_focus, valid);
+ }
+ }
+
+ /*
+ * Circles externally tangent to box edges.
+ *
+ * All circles have center in (dx, dy) * t
+ *
+ * If the circle is tangent to the line defined by the edge of the
+ * box, then at least one of the following holds true:
+ *
+ * (dx*t) + (cr + dr*t) == x0 (left edge)
+ * (dx*t) - (cr + dr*t) == x1 (right edge)
+ * (dy*t) + (cr + dr*t) == y0 (top edge)
+ * (dy*t) - (cr + dr*t) == y1 (bottom edge)
+ *
+ * The solution is only valid if the tangent point is actually on
+ * the edge, i.e. if its y coordinate is in [y0,y1] for left/right
+ * edges and if its x coordinate is in [x0,x1] for top/bottom
+ * edges.
+ *
+ * For the first equation:
+ *
+ * (dx + dr) * t = x0 - cr
+ * t = (x0 - cr) / (dx + dr)
+ * y = dy * t
+ *
+ * in the code this becomes:
+ *
+ * t_edge = (num) / (den)
+ * v = (delta) * t_edge
+ *
+ * If the denominator in t is 0, the pattern is tangent to a line
+ * parallel to the edge under examination. The corner-case where
+ * the boundary line is the same as the edge is handled by the
+ * focus point case and/or by the a==0 case.
+ */
+#define T_EDGE(num,den,delta,lower,upper) \
+ if (fabs (den) >= DBL_EPSILON) { \
+ double t_edge, v; \
+ \
+ t_edge = (num) / (den); \
+ v = t_edge * (delta); \
+ if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \
+ valid = _extend_range (range, t_edge, valid); \
+ }
+
+ /* circles tangent (externally) to left/right/top/bottom edge */
+ T_EDGE (x0 - cr, dx + dr, dy, miny, maxy);
+ T_EDGE (x1 + cr, dx - dr, dy, miny, maxy);
+ T_EDGE (y0 - cr, dy + dr, dx, minx, maxx);
+ T_EDGE (y1 + cr, dy - dr, dx, minx, maxx);
+
+#undef T_EDGE
+
+ /*
+ * Circles passing through a corner.
+ *
+ * A circle passing through the point (x,y) satisfies:
+ *
+ * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
+ *
+ * If we set:
+ * a = dx^2 + dy^2 - dr^2
+ * b = x*dx + y*dy + cr*dr
+ * c = x^2 + y^2 - cr^2
+ * we have:
+ * a*t^2 - 2*b*t + c == 0
+ */
+ a = dx * dx + dy * dy - dr * dr;
+ if (fabs (a) < DBL_EPSILON * DBL_EPSILON) {
+ double b, maxd2;
+
+ /* Ensure that gradients with both a and dr small are
+ * considered degenerate.
+ * The floating point version of the degeneracy test implemented
+ * in _radial_pattern_is_degenerate() is:
+ *
+ * 1) The circles are practically the same size:
+ * |dr| < DBL_EPSILON
+ * AND
+ * 2a) The circles are both very small:
+ * min (r0, r1) < DBL_EPSILON
+ * OR
+ * 2b) The circles are very close to each other:
+ * max (|dx|, |dy|) < 2 * DBL_EPSILON
+ *
+ * Assuming that the gradient is not degenerate, we want to
+ * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON.
+ *
+ * If the gradient is not degenerate yet it has |dr| <
+ * DBL_EPSILON, (2b) is false, thus:
+ *
+ * max (|dx|, |dy|) >= 2*DBL_EPSILON
+ * which implies:
+ * 4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
+ *
+ * From the definition of a, we get:
+ * a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2
+ * dx^2 + dy^2 - DBL_EPSILON^2 < dr^2
+ * 3*DBL_EPSILON^2 < dr^2
+ *
+ * which is inconsistent with the hypotheses, thus |dr| <
+ * DBL_EPSILON is false or the gradient is degenerate.
+ */
+ assert (fabs (dr) >= DBL_EPSILON);
+
+ /*
+ * If a == 0, all the circles are tangent to a line in the
+ * focus point. If this line is within the box extents, we
+ * should add the circle with infinite radius, but this would
+ * make the range unbounded, so we add the smallest circle whose
+ * distance to the desired (degenerate) circle within the
+ * bounding box does not exceed tolerance.
+ *
+ * The equation of the line is b==0, i.e.:
+ * x*dx + y*dy + cr*dr == 0
+ *
+ * We compute the intersection of the line with the box and
+ * keep the intersection with maximum square distance (maxd2)
+ * from the focus point.
+ *
+ * In the code the intersection is represented in another
+ * coordinate system, whose origin is the focus point and
+ * which has a u,v axes, which are respectively orthogonal and
+ * parallel to the edge being intersected.
+ *
+ * The intersection is valid only if it belongs to the box,
+ * otherwise it is ignored.
+ *
+ * For example:
+ *
+ * y = y0
+ * x*dx + y0*dy + cr*dr == 0
+ * x = -(y0*dy + cr*dr) / dx
+ *
+ * which in (u,v) is:
+ * u = y0 - y_focus
+ * v = -(y0*dy + cr*dr) / dx - x_focus
+ *
+ * In the code:
+ * u = (edge) - (u_origin)
+ * v = -((edge) * (delta) + cr*dr) / (den) - v_focus
+ */
+#define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \
+ if (fabs (den) >= DBL_EPSILON) { \
+ double v; \
+ \
+ v = -((edge) * (delta) + cr * dr) / (den); \
+ if ((lower) <= v && v <= (upper)) { \
+ double u, d2; \
+ \
+ u = (edge) - (u_origin); \
+ v -= (v_origin); \
+ d2 = u*u + v*v; \
+ if (maxd2 < d2) \
+ maxd2 = d2; \
+ } \
+ }
+
+ maxd2 = 0;
+
+ /* degenerate circles (lines) passing through each edge */
+ T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus);
+ T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus);
+ T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus);
+ T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus);
+
+#undef T_EDGE
+
+ /*
+ * The limit circle can be transformed rigidly to the y=0 line
+ * and the circles tangent to it in (0,0) are:
+ *
+ * x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0
+ *
+ * y is the distance from the line, in our case tolerance;
+ * x is the distance along the line, i.e. sqrt(maxd2),
+ * so:
+ *
+ * r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance)
+ * t = (r - cr) / dr =
+ * (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr)
+ */
+ if (maxd2 > 0) {
+ double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr;
+ t_limit /= 2 * tolerance * dr;
+ valid = _extend_range (range, t_limit, valid);
+ }
+
+ /*
+ * Nondegenerate, nonlimit circles passing through the corners.
+ *
+ * a == 0 && a*t^2 - 2*b*t + c == 0
+ *
+ * t = c / (2*b)
+ *
+ * The b == 0 case has just been handled, so we only have to
+ * compute this if b != 0.
+ */
+#define T_CORNER(x,y) \
+ b = (x) * dx + (y) * dy + cr * dr; \
+ if (fabs (b) >= DBL_EPSILON) { \
+ double t_corner; \
+ double x2 = (x) * (x); \
+ double y2 = (y) * (y); \
+ double cr2 = (cr) * (cr); \
+ double c = x2 + y2 - cr2; \
+ \
+ t_corner = 0.5 * c / b; \
+ if (t_corner * dr >= mindr) \
+ valid = _extend_range (range, t_corner, valid); \
+ }
+
+ /* circles touching each corner */
+ T_CORNER (x0, y0);
+ T_CORNER (x0, y1);
+ T_CORNER (x1, y0);
+ T_CORNER (x1, y1);
+
+#undef T_CORNER
+ } else {
+ double inva, b, c, d;
+
+ inva = 1 / a;
+
+ /*
+ * Nondegenerate, nonlimit circles passing through the corners.
+ *
+ * a != 0 && a*t^2 - 2*b*t + c == 0
+ *
+ * t = (b +- sqrt (b*b - a*c)) / a
+ *
+ * If the argument of sqrt() is negative, then no circle
+ * passes through the corner.
+ */
+#define T_CORNER(x,y) \
+ b = (x) * dx + (y) * dy + cr * dr; \
+ c = (x) * (x) + (y) * (y) - cr * cr; \
+ d = b * b - a * c; \
+ if (d >= 0) { \
+ double t_corner; \
+ \
+ d = sqrt (d); \
+ t_corner = (b + d) * inva; \
+ if (t_corner * dr >= mindr) \
+ valid = _extend_range (range, t_corner, valid); \
+ t_corner = (b - d) * inva; \
+ if (t_corner * dr >= mindr) \
+ valid = _extend_range (range, t_corner, valid); \
+ }
+
+ /* circles touching each corner */
+ T_CORNER (x0, y0);
+ T_CORNER (x0, y1);
+ T_CORNER (x1, y0);
+ T_CORNER (x1, y1);
+
+#undef T_CORNER
+ }
+}
+
+/**
+ * _cairo_gradient_pattern_box_to_parameter:
+ *
+ * Compute a interpolation range sufficient to draw (within the given
+ * tolerance) the gradient in the given box getting the same result as
+ * using the (-inf, +inf) range.
+ *
+ * Assumes that the pattern is not degenerate. This can be guaranteed
+ * by simplifying it to a solid clear if _cairo_pattern_is_clear or to
+ * a solid color if _cairo_gradient_pattern_is_solid.
+ *
+ * The range isn't guaranteed to be minimal, but it tries to.
+ **/
+void
+_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
+ double x0, double y0,
+ double x1, double y1,
+ double tolerance,
+ double out_range[2])
+{
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ _cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient,
+ x0, y0, x1, y1, out_range);
+ } else {
+ _cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient,
+ x0, y0, x1, y1, tolerance, out_range);
+ }
+}
+
+/**
+ * _cairo_gradient_pattern_interpolate:
+ *
+ * Interpolate between the start and end objects of linear or radial
+ * gradients. The interpolated object is stored in out_circle, with
+ * the radius being zero in the linear gradient case.
+ **/
+void
+_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
+ double t,
+ cairo_circle_double_t *out_circle)
+{
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+#define lerp(a,b) (a)*(1-t) + (b)*t
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ out_circle->center.x = lerp (linear->pd1.x, linear->pd2.x);
+ out_circle->center.y = lerp (linear->pd1.y, linear->pd2.y);
+ out_circle->radius = 0;
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+ out_circle->center.x = lerp (radial->cd1.center.x, radial->cd2.center.x);
+ out_circle->center.y = lerp (radial->cd1.center.y, radial->cd2.center.y);
+ out_circle->radius = lerp (radial->cd1.radius , radial->cd2.radius);
+ }
+
+#undef lerp
+}
+
+
+/**
+ * _cairo_gradient_pattern_fit_to_range:
+ *
+ * Scale the extremes of a gradient to guarantee that the coordinates
+ * and their deltas are within the range (-max_value, max_value). The
+ * new extremes are stored in out_circle.
+ *
+ * The pattern matrix is scaled to guarantee that the aspect of the
+ * gradient is the same and the result is stored in out_matrix.
+ *
+ **/
+void
+_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
+ double max_value,
+ cairo_matrix_t *out_matrix,
+ cairo_circle_double_t out_circle[2])
+{
+ double dim;
+
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+
+ out_circle[0].center = linear->pd1;
+ out_circle[0].radius = 0;
+ out_circle[1].center = linear->pd2;
+ out_circle[1].radius = 0;
+
+ dim = fabs (linear->pd1.x);
+ dim = MAX (dim, fabs (linear->pd1.y));
+ dim = MAX (dim, fabs (linear->pd2.x));
+ dim = MAX (dim, fabs (linear->pd2.y));
+ dim = MAX (dim, fabs (linear->pd1.x - linear->pd2.x));
+ dim = MAX (dim, fabs (linear->pd1.y - linear->pd2.y));
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+
+ out_circle[0] = radial->cd1;
+ out_circle[1] = radial->cd2;
+
+ dim = fabs (radial->cd1.center.x);
+ dim = MAX (dim, fabs (radial->cd1.center.y));
+ dim = MAX (dim, fabs (radial->cd1.radius));
+ dim = MAX (dim, fabs (radial->cd2.center.x));
+ dim = MAX (dim, fabs (radial->cd2.center.y));
+ dim = MAX (dim, fabs (radial->cd2.radius));
+ dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x));
+ dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y));
+ dim = MAX (dim, fabs (radial->cd1.radius - radial->cd2.radius));
+ }
+
+ if (unlikely (dim > max_value)) {
+ cairo_matrix_t scale;
+
+ dim = max_value / dim;
+
+ out_circle[0].center.x *= dim;
+ out_circle[0].center.y *= dim;
+ out_circle[0].radius *= dim;
+ out_circle[1].center.x *= dim;
+ out_circle[1].center.y *= dim;
+ out_circle[1].radius *= dim;
+
+ cairo_matrix_init_scale (&scale, dim, dim);
+ cairo_matrix_multiply (out_matrix, &gradient->base.matrix, &scale);
+ } else {
+ *out_matrix = gradient->base.matrix;
+ }
+}
+
+static cairo_bool_t
+_gradient_is_clear (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents)
+{
+ unsigned int i;
+
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ if (gradient->n_stops == 0 ||
+ (gradient->base.extend == CAIRO_EXTEND_NONE &&
+ gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
+ return TRUE;
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) {
+ /* degenerate radial gradients are clear */
+ if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient))
+ return TRUE;
+ } else if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ /* EXTEND_NONE degenerate linear gradients are clear */
+ if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient))
+ return TRUE;
+ }
+
+ /* Check if the extents intersect the drawn part of the pattern. */
+ if (extents != NULL &&
+ (gradient->base.extend == CAIRO_EXTEND_NONE ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL))
+ {
+ double t[2];
+
+ _cairo_gradient_pattern_box_to_parameter (gradient,
+ extents->x,
+ extents->y,
+ extents->x + extents->width,
+ extents->y + extents->height,
+ DBL_EPSILON,
+ t);
+
+ if (gradient->base.extend == CAIRO_EXTEND_NONE &&
+ (t[0] >= gradient->stops[gradient->n_stops - 1].offset ||
+ t[1] <= gradient->stops[0].offset))
+ {
+ return TRUE;
+ }
+
+ if (t[0] == t[1])
+ return TRUE;
+ }
+
+ for (i = 0; i < gradient->n_stops; i++)
+ if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+_gradient_color_average (const cairo_gradient_pattern_t *gradient,
+ cairo_color_t *color)
+{
+ double delta0, delta1;
+ double r, g, b, a;
+ unsigned int i, start = 1, end;
+
+ assert (gradient->n_stops > 0);
+ assert (gradient->base.extend != CAIRO_EXTEND_NONE);
+
+ if (gradient->n_stops == 1) {
+ _cairo_color_init_rgba (color,
+ gradient->stops[0].color.red,
+ gradient->stops[0].color.green,
+ gradient->stops[0].color.blue,
+ gradient->stops[0].color.alpha);
+ return;
+ }
+
+ end = gradient->n_stops - 1;
+
+ switch (gradient->base.extend) {
+ case CAIRO_EXTEND_REPEAT:
+ /*
+ * Sa, Sb and Sy, Sz are the first two and last two stops respectively.
+ * The weight of the first and last stop can be computed as the area of
+ * the following triangles (taken with height 1, since the whole [0-1]
+ * will have total weight 1 this way): b*h/2
+ *
+ * + +
+ * / |\ / | \
+ * / | \ / | \
+ * / | \ / | \
+ * ~~~~~+---+---+---+~~~~~~~+-------+---+---+~~~~~
+ * -1+Sz 0 Sa Sb Sy Sz 1 1+Sa
+ *
+ * For the first stop: (Sb-(-1+Sz)/2 = (1+Sb-Sz)/2
+ * For the last stop: ((1+Sa)-Sy)/2 = (1+Sa-Sy)/2
+ * Halving the result is done after summing up all the areas.
+ */
+ delta0 = 1.0 + gradient->stops[1].offset - gradient->stops[end].offset;
+ delta1 = 1.0 + gradient->stops[0].offset - gradient->stops[end-1].offset;
+ break;
+
+ case CAIRO_EXTEND_REFLECT:
+ /*
+ * Sa, Sb and Sy, Sz are the first two and last two stops respectively.
+ * The weight of the first and last stop can be computed as the area of
+ * the following trapezoids (taken with height 1, since the whole [0-1]
+ * will have total weight 1 this way): (b+B)*h/2
+ *
+ * +-------+ +---+
+ * | |\ / | |
+ * | | \ / | |
+ * | | \ / | |
+ * +-------+---+~~~~~~~+-------+---+
+ * 0 Sa Sb Sy Sz 1
+ *
+ * For the first stop: (Sa+Sb)/2
+ * For the last stop: ((1-Sz) + (1-Sy))/2 = (2-Sy-Sz)/2
+ * Halving the result is done after summing up all the areas.
+ */
+ delta0 = gradient->stops[0].offset + gradient->stops[1].offset;
+ delta1 = 2.0 - gradient->stops[end-1].offset - gradient->stops[end].offset;
+ break;
+
+ case CAIRO_EXTEND_PAD:
+ /* PAD is computed as the average of the first and last stop:
+ * - take both of them with weight 1 (they will be halved
+ * after the whole sum has been computed).
+ * - avoid summing any of the inner stops.
+ */
+ delta0 = delta1 = 1.0;
+ start = end;
+ break;
+
+ case CAIRO_EXTEND_NONE:
+ default:
+ ASSERT_NOT_REACHED;
+ _cairo_color_init_rgba (color, 0, 0, 0, 0);
+ return;
+ }
+
+ r = delta0 * gradient->stops[0].color.red;
+ g = delta0 * gradient->stops[0].color.green;
+ b = delta0 * gradient->stops[0].color.blue;
+ a = delta0 * gradient->stops[0].color.alpha;
+
+ for (i = start; i < end; ++i) {
+ /* Inner stops weight is the same as the area of the triangle they influence
+ * (which goes from the stop before to the stop after), again with height 1
+ * since the whole must sum up to 1: b*h/2
+ * Halving is done after the whole sum has been computed.
+ */
+ double delta = gradient->stops[i+1].offset - gradient->stops[i-1].offset;
+ r += delta * gradient->stops[i].color.red;
+ g += delta * gradient->stops[i].color.green;
+ b += delta * gradient->stops[i].color.blue;
+ a += delta * gradient->stops[i].color.alpha;
+ }
+
+ r += delta1 * gradient->stops[end].color.red;
+ g += delta1 * gradient->stops[end].color.green;
+ b += delta1 * gradient->stops[end].color.blue;
+ a += delta1 * gradient->stops[end].color.alpha;
+
+ _cairo_color_init_rgba (color, r * .5, g * .5, b * .5, a * .5);
+}
+
+/**
+ * _cairo_pattern_alpha_range:
+ *
+ * Convenience function to determine the minimum and maximum alpha in
+ * the drawn part of a pattern (i.e. ignoring clear parts caused by
+ * extend modes and/or pattern shape).
+ *
+ * If not NULL, out_min and out_max will be set respectively to the
+ * minimum and maximum alpha value of the pattern.
+ **/
+void
+_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
+ double *out_min,
+ double *out_max)
+{
+ double alpha_min, alpha_max;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID: {
+ const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+ alpha_min = alpha_max = solid->color.alpha;
+ break;
+ }
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL: {
+ const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+ unsigned int i;
+
+ assert (gradient->n_stops >= 1);
+
+ alpha_min = alpha_max = gradient->stops[0].color.alpha;
+ for (i = 1; i < gradient->n_stops; i++) {
+ if (alpha_min > gradient->stops[i].color.alpha)
+ alpha_min = gradient->stops[i].color.alpha;
+ else if (alpha_max < gradient->stops[i].color.alpha)
+ alpha_max = gradient->stops[i].color.alpha;
+ }
+
+ break;
+ }
+
+ case CAIRO_PATTERN_TYPE_MESH: {
+ const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern;
+ const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+ unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches);
+
+ assert (n >= 1);
+
+ alpha_min = alpha_max = patch[0].colors[0].alpha;
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < 4; j++) {
+ if (patch[i].colors[j].alpha < alpha_min)
+ alpha_min = patch[i].colors[j].alpha;
+ else if (patch[i].colors[j].alpha > alpha_max)
+ alpha_max = patch[i].colors[j].alpha;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ ASSERT_NOT_REACHED;
+ /* fall through */
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ alpha_min = 0;
+ alpha_max = 1;
+ break;
+ }
+
+ if (out_min)
+ *out_min = alpha_min;
+ if (out_max)
+ *out_max = alpha_max;
+}
+
+/**
+ * _cairo_mesh_pattern_coord_box:
+ *
+ * Convenience function to determine the range of the coordinates of
+ * the points used to define the patches of the mesh.
+ *
+ * This is guaranteed to contain the pattern extents, but might not be
+ * tight, just like a Bezier curve is always inside the convex hull of
+ * the control points.
+ *
+ * This function cannot be used while the mesh is being constructed.
+ *
+ * The function returns TRUE and sets the output parametes to define
+ * the coodrinate range if the mesh pattern contains at least one
+ * patch, otherwise it returns FALSE.
+ **/
+cairo_bool_t
+_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
+ double *out_xmin,
+ double *out_ymin,
+ double *out_xmax,
+ double *out_ymax)
+{
+ const cairo_mesh_patch_t *patch;
+ unsigned int num_patches, i, j, k;
+ double x0, y0, x1, y1;
+
+ assert (mesh->current_patch == NULL);
+
+ num_patches = _cairo_array_num_elements (&mesh->patches);
+
+ if (num_patches == 0)
+ return FALSE;
+
+ patch = _cairo_array_index_const (&mesh->patches, 0);
+ x0 = x1 = patch->points[0][0].x;
+ y0 = y1 = patch->points[0][0].y;
+
+ for (i = 0; i < num_patches; i++) {
+ for (j = 0; j < 4; j++) {
+ for (k = 0; k < 4; k++) {
+ x0 = MIN (x0, patch[i].points[j][k].x);
+ y0 = MIN (y0, patch[i].points[j][k].y);
+ x1 = MAX (x1, patch[i].points[j][k].x);
+ y1 = MAX (y1, patch[i].points[j][k].y);
+ }
+ }
+ }
+
+ *out_xmin = x0;
+ *out_ymin = y0;
+ *out_xmax = x1;
+ *out_ymax = y1;
+
+ return TRUE;
+}
+
+/**
+ * _cairo_gradient_pattern_is_solid:
+ *
+ * Convenience function to determine whether a gradient pattern is
+ * a solid color within the given extents. In this case the color
+ * argument is initialized to the color the pattern represents.
+ * This functions doesn't handle completely transparent gradients,
+ * thus it should be called only after _cairo_pattern_is_clear has
+ * returned FALSE.
+ *
+ * Return value: %TRUE if the pattern is a solid color.
+ **/
+cairo_bool_t
+_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents,
+ cairo_color_t *color)
+{
+ unsigned int i;
+
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ /* TODO: radial */
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ if (_linear_pattern_is_degenerate (linear)) {
+ _gradient_color_average (gradient, color);
+ return TRUE;
+ }
+
+ if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ double t[2];
+
+ /* We already know that the pattern is not clear, thus if some
+ * part of it is clear, the whole is not solid.
+ */
+
+ if (extents == NULL)
+ return FALSE;
+
+ _cairo_linear_pattern_box_to_parameter (linear,
+ extents->x,
+ extents->y,
+ extents->x + extents->width,
+ extents->y + extents->height,
+ t);
+
+ if (t[0] < 0.0 || t[1] > 1.0)
+ return FALSE;
+ }
+ } else
+ return FALSE;
+
+ for (i = 1; i < gradient->n_stops; i++)
+ if (! _cairo_color_stop_equal (&gradient->stops[0].color,
+ &gradient->stops[i].color))
+ return FALSE;
+
+ _cairo_color_init_rgba (color,
+ gradient->stops[0].color.red,
+ gradient->stops[0].color.green,
+ gradient->stops[0].color.blue,
+ gradient->stops[0].color.alpha);
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_mesh_is_clear (const cairo_mesh_pattern_t *mesh)
+{
+ double x1, y1, x2, y2;
+ cairo_bool_t is_valid;
+
+ is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
+ if (!is_valid)
+ return TRUE;
+
+ if (x2 - x1 < DBL_EPSILON || y2 - y1 < DBL_EPSILON)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * _cairo_pattern_is_opaque_solid:
+ *
+ * Convenience function to determine whether a pattern is an opaque
+ * (alpha==1.0) solid color pattern. This is done by testing whether
+ * the pattern's alpha value when converted to a byte is 255, so if a
+ * backend actually supported deep alpha channels this function might
+ * not do the right thing.
+ *
+ * Return value: %TRUE if the pattern is an opaque, solid color.
+ **/
+cairo_bool_t
+_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern)
+{
+ cairo_solid_pattern_t *solid;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return FALSE;
+
+ solid = (cairo_solid_pattern_t *) pattern;
+
+ return CAIRO_COLOR_IS_OPAQUE (&solid->color);
+}
+
+static cairo_bool_t
+_surface_is_opaque (const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample)
+{
+ cairo_rectangle_int_t extents;
+
+ if (pattern->surface->content & CAIRO_CONTENT_ALPHA)
+ return FALSE;
+
+ if (pattern->base.extend != CAIRO_EXTEND_NONE)
+ return TRUE;
+
+ if (! _cairo_surface_get_extents (pattern->surface, &extents))
+ return TRUE;
+
+ if (sample == NULL)
+ return FALSE;
+
+ return _cairo_rectangle_contains_rectangle (&extents, sample);
+}
+
+static cairo_bool_t
+_raster_source_is_opaque (const cairo_raster_source_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample)
+{
+ if (pattern->content & CAIRO_CONTENT_ALPHA)
+ return FALSE;
+
+ if (pattern->base.extend != CAIRO_EXTEND_NONE)
+ return TRUE;
+
+ if (sample == NULL)
+ return FALSE;
+
+ return _cairo_rectangle_contains_rectangle (&pattern->extents, sample);
+}
+
+static cairo_bool_t
+_surface_is_clear (const cairo_surface_pattern_t *pattern)
+{
+ cairo_rectangle_int_t extents;
+
+ if (_cairo_surface_get_extents (pattern->surface, &extents) &&
+ (extents.width == 0 || extents.height == 0))
+ return TRUE;
+
+ return pattern->surface->is_clear &&
+ pattern->surface->content & CAIRO_CONTENT_ALPHA;
+}
+
+static cairo_bool_t
+_raster_source_is_clear (const cairo_raster_source_pattern_t *pattern)
+{
+ return pattern->extents.width == 0 || pattern->extents.height == 0;
+}
+
+static cairo_bool_t
+_gradient_is_opaque (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *sample)
+{
+ unsigned int i;
+
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ if (gradient->n_stops == 0 ||
+ (gradient->base.extend == CAIRO_EXTEND_NONE &&
+ gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
+ return FALSE;
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ double t[2];
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+
+ /* EXTEND_NONE degenerate radial gradients are clear */
+ if (_linear_pattern_is_degenerate (linear))
+ return FALSE;
+
+ if (sample == NULL)
+ return FALSE;
+
+ _cairo_linear_pattern_box_to_parameter (linear,
+ sample->x,
+ sample->y,
+ sample->x + sample->width,
+ sample->y + sample->height,
+ t);
+
+ if (t[0] < 0.0 || t[1] > 1.0)
+ return FALSE;
+ }
+ } else
+ return FALSE; /* TODO: check actual intersection */
+
+ for (i = 0; i < gradient->n_stops; i++)
+ if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * _cairo_pattern_is_opaque:
+ *
+ * Convenience function to determine whether a pattern is an opaque
+ * pattern (of any type). The same caveats that apply to
+ * _cairo_pattern_is_opaque_solid apply here as well.
+ *
+ * Return value: %TRUE if the pattern is a opaque.
+ **/
+cairo_bool_t
+_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern,
+ const cairo_rectangle_int_t *sample)
+{
+ const cairo_pattern_union_t *pattern;
+
+ if (abstract_pattern->has_component_alpha)
+ return FALSE;
+
+ pattern = (cairo_pattern_union_t *) abstract_pattern;
+ switch (pattern->base.type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _cairo_pattern_is_opaque_solid (abstract_pattern);
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _surface_is_opaque (&pattern->surface, sample);
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _raster_source_is_opaque (&pattern->raster_source, sample);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _gradient_is_opaque (&pattern->gradient.base, sample);
+ case CAIRO_PATTERN_TYPE_MESH:
+ return FALSE;
+ }
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+cairo_bool_t
+_cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern)
+{
+ const cairo_pattern_union_t *pattern;
+
+ if (abstract_pattern->has_component_alpha)
+ return FALSE;
+
+ pattern = (cairo_pattern_union_t *) abstract_pattern;
+ switch (abstract_pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color);
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _surface_is_clear (&pattern->surface);
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _raster_source_is_clear (&pattern->raster_source);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _gradient_is_clear (&pattern->gradient.base, NULL);
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _mesh_is_clear (&pattern->mesh);
+ }
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+/**
+ * _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;
+}
+
+cairo_filter_t
+_cairo_pattern_sampled_area (const cairo_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;
+
+ filter = _cairo_pattern_analyze_filter (pattern, &pad);
+ if (pad == 0.0 && _cairo_matrix_is_identity (&pattern->matrix)) {
+ *sample = *extents;
+ return filter;
+ }
+
+ x1 = extents->x;
+ y1 = extents->y;
+ x2 = extents->x + (int) extents->width;
+ y2 = extents->y + (int) extents->height;
+
+ _cairo_matrix_transform_bounding_box (&pattern->matrix,
+ &x1, &y1, &x2, &y2,
+ NULL);
+ if (x1 > CAIRO_RECT_INT_MIN)
+ sample->x = floor (x1 - pad);
+ else
+ sample->x = CAIRO_RECT_INT_MIN;
+
+ if (y1 > CAIRO_RECT_INT_MIN)
+ sample->y = floor (y1 - pad);
+ else
+ sample->y = CAIRO_RECT_INT_MIN;
+
+ if (x2 < CAIRO_RECT_INT_MAX)
+ sample->width = ceil (x2 + pad);
+ else
+ sample->width = CAIRO_RECT_INT_MAX;
+
+ if (y2 < CAIRO_RECT_INT_MAX)
+ sample->height = ceil (y2 + pad);
+ else
+ sample->height = CAIRO_RECT_INT_MAX;
+
+ sample->width -= sample->x;
+ sample->height -= sample->y;
+
+ return filter;
+}
+
+/**
+ * _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_RASTER_SOURCE:
+ {
+ const cairo_raster_source_pattern_t *raster =
+ (const cairo_raster_source_pattern_t *) pattern;
+ double pad;
+
+ if (raster->extents.width == 0 || raster->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 (pattern, &pad);
+ x1 = raster->extents.x - pad;
+ y1 = raster->extents.y - pad;
+ x2 = raster->extents.x + (int) raster->extents.width + pad;
+ y2 = raster->extents.y + (int) raster->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 r1, r2;
+
+ if (_radial_pattern_is_degenerate (radial)) {
+ /* cairo-gstate should have optimised degenerate
+ * patterns to solid clear patterns, so we can ignore
+ * them here. */
+ goto EMPTY;
+ }
+
+ /* TODO: in some cases (focus outside/on the circle) it is
+ * half-bounded. */
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ goto UNBOUNDED;
+
+ cx1 = radial->cd1.center.x;
+ cy1 = radial->cd1.center.y;
+ r1 = radial->cd1.radius;
+
+ cx2 = radial->cd2.center.x;
+ cy2 = radial->cd2.center.y;
+ r2 = radial->cd2.radius;
+
+ x1 = MIN (cx1 - r1, cx2 - r2);
+ y1 = MIN (cy1 - r1, cy2 - r2);
+ x2 = MAX (cx1 + r1, cx2 + r2);
+ y2 = MAX (cy1 + r1, cy2 + r2);
+ }
+ break;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ {
+ const cairo_linear_pattern_t *linear =
+ (const cairo_linear_pattern_t *) pattern;
+
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ goto UNBOUNDED;
+
+ if (_linear_pattern_is_degenerate (linear)) {
+ /* cairo-gstate should have optimised degenerate
+ * patterns to solid ones, so we can again ignore
+ * them here. */
+ goto EMPTY;
+ }
+
+ /* TODO: to get tight extents, use the matrix to transform
+ * the pattern instead of transforming the extents later. */
+ if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.)
+ goto UNBOUNDED;
+
+ if (linear->pd1.x == linear->pd2.x) {
+ x1 = -HUGE_VAL;
+ x2 = HUGE_VAL;
+ y1 = MIN (linear->pd1.y, linear->pd2.y);
+ y2 = MAX (linear->pd1.y, linear->pd2.y);
+ } else if (linear->pd1.y == linear->pd2.y) {
+ x1 = MIN (linear->pd1.x, linear->pd2.x);
+ x2 = MAX (linear->pd1.x, linear->pd2.x);
+ y1 = -HUGE_VAL;
+ y2 = HUGE_VAL;
+ } else {
+ goto UNBOUNDED;
+ }
+ }
+ break;
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ {
+ const cairo_mesh_pattern_t *mesh =
+ (const cairo_mesh_pattern_t *) pattern;
+ double padx, pady;
+ cairo_bool_t is_valid;
+
+ is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
+ if (!is_valid)
+ goto EMPTY;
+
+ padx = pady = 1.;
+ cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady);
+ padx = fabs (padx);
+ pady = fabs (pady);
+
+ x1 -= padx;
+ y1 -= pady;
+ x2 += padx;
+ y2 += pady;
+ }
+ 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;
+}
+
+void
+_cairo_pattern_get_exact_extents (const cairo_pattern_t *pattern,
+ cairo_rectangle_t *extents)
+{
+ double x1, y1, x2, y2;
+ cairo_status_t status;
+ cairo_rectangle_int_t int_rect;;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ goto UNBOUNDED;
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ {
+ cairo_rectangle_int_t surface_extents;
+ const cairo_surface_pattern_t *surface_pattern =
+ (const cairo_surface_pattern_t *) pattern;
+ cairo_surface_t *surface = surface_pattern->surface;
+
+ if (! _cairo_surface_get_extents (surface, &surface_extents))
+ goto UNBOUNDED;
+
+ if (surface_extents.width == 0 || surface_extents.height == 0)
+ goto EMPTY;
+
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ goto UNBOUNDED;
+
+ x1 = surface_extents.x;
+ y1 = surface_extents.y;
+ x2 = surface_extents.x + surface_extents.width;
+ y2 = surface_extents.y + surface_extents.height;
+ }
+ break;
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ {
+ const cairo_raster_source_pattern_t *raster =
+ (const cairo_raster_source_pattern_t *) pattern;
+
+ if (raster->extents.width == 0 || raster->extents.height == 0)
+ goto EMPTY;
+
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ goto UNBOUNDED;
+
+ x1 = raster->extents.x;
+ y1 = raster->extents.y;
+ x2 = raster->extents.x + raster->extents.width;
+ y2 = raster->extents.y + raster->extents.height;
+ }
+ break;
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ {
+ const cairo_radial_pattern_t *radial =
+ (const cairo_radial_pattern_t *) pattern;
+ double cx1, cy1;
+ double cx2, cy2;
+ double r1, r2;
+
+ if (_radial_pattern_is_degenerate (radial)) {
+ /* cairo-gstate should have optimised degenerate
+ * patterns to solid clear patterns, so we can ignore
+ * them here. */
+ goto EMPTY;
+ }
+
+ /* TODO: in some cases (focus outside/on the circle) it is
+ * half-bounded. */
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ goto UNBOUNDED;
+
+ cx1 = radial->cd1.center.x;
+ cy1 = radial->cd1.center.y;
+ r1 = radial->cd1.radius;
+
+ cx2 = radial->cd2.center.x;
+ cy2 = radial->cd2.center.y;
+ r2 = radial->cd2.radius;
+
+ x1 = MIN (cx1 - r1, cx2 - r2);
+ y1 = MIN (cy1 - r1, cy2 - r2);
+ x2 = MAX (cx1 + r1, cx2 + r2);
+ y2 = MAX (cy1 + r1, cy2 + r2);
+ }
+ break;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ {
+ const cairo_linear_pattern_t *linear =
+ (const cairo_linear_pattern_t *) pattern;
+
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ goto UNBOUNDED;
+
+ if (_linear_pattern_is_degenerate (linear)) {
+ /* cairo-gstate should have optimised degenerate
+ * patterns to solid ones, so we can again ignore
+ * them here. */
+ goto EMPTY;
+ }
+
+ /* TODO: to get tight extents, use the matrix to transform
+ * the pattern instead of transforming the extents later. */
+ if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.)
+ goto UNBOUNDED;
+
+ if (linear->pd1.x == linear->pd2.x) {
+ x1 = -HUGE_VAL;
+ x2 = HUGE_VAL;
+ y1 = MIN (linear->pd1.y, linear->pd2.y);
+ y2 = MAX (linear->pd1.y, linear->pd2.y);
+ } else if (linear->pd1.y == linear->pd2.y) {
+ x1 = MIN (linear->pd1.x, linear->pd2.x);
+ x2 = MAX (linear->pd1.x, linear->pd2.x);
+ y1 = -HUGE_VAL;
+ y2 = HUGE_VAL;
+ } else {
+ goto UNBOUNDED;
+ }
+ }
+ break;
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ {
+ const cairo_mesh_pattern_t *mesh =
+ (const cairo_mesh_pattern_t *) pattern;
+ double padx, pady;
+ cairo_bool_t is_valid;
+
+ is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
+ if (!is_valid)
+ goto EMPTY;
+
+ padx = pady = 1.;
+ cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady);
+ padx = fabs (padx);
+ pady = fabs (pady);
+
+ x1 -= padx;
+ y1 -= pady;
+ x2 += padx;
+ y2 += pady;
+ }
+ 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);
+ }
+
+ if (x1 < CAIRO_RECT_INT_MIN)
+ x1 = CAIRO_RECT_INT_MIN;
+ if (y1 < CAIRO_RECT_INT_MIN)
+ y1 = CAIRO_RECT_INT_MIN;
+
+ if (x2 > CAIRO_RECT_INT_MAX)
+ x2 = CAIRO_RECT_INT_MAX;
+ 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 (&int_rect);
+ extents->x = int_rect.x;
+ extents->y = int_rect.y;
+ extents->width = int_rect.width;
+ extents->height = int_rect.height;
+ return;
+
+ EMPTY:
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ return;
+}
+
+/**
+ * _cairo_pattern_get_ink_extents:
+ *
+ * Return the "target-space" inked extents of @pattern in @extents.
+ **/
+cairo_int_status_t
+_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern,
+ cairo_rectangle_int_t *extents)
+{
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ pattern->extend == CAIRO_EXTEND_NONE)
+ {
+ const cairo_surface_pattern_t *surface_pattern =
+ (const cairo_surface_pattern_t *) pattern;
+ cairo_surface_t *surface = surface_pattern->surface;
+
+ surface = _cairo_surface_get_source (surface, NULL);
+ if (_cairo_surface_is_recording (surface)) {
+ cairo_matrix_t imatrix;
+ cairo_box_t box;
+ cairo_status_t status;
+
+ imatrix = pattern->matrix;
+ status = cairo_matrix_invert (&imatrix);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)surface,
+ &box, &imatrix);
+ if (unlikely (status))
+ return status;
+
+ _cairo_box_round_to_rectangle (&box, extents);
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ _cairo_pattern_get_extents (pattern, extents);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned long
+_cairo_solid_pattern_hash (unsigned long hash,
+ const cairo_solid_pattern_t *solid)
+{
+ hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color));
+
+ return hash;
+}
+
+static unsigned long
+_cairo_gradient_color_stops_hash (unsigned long hash,
+ const cairo_gradient_pattern_t *gradient)
+{
+ unsigned int n;
+
+ hash = _cairo_hash_bytes (hash,
+ &gradient->n_stops,
+ sizeof (gradient->n_stops));
+
+ for (n = 0; n < gradient->n_stops; n++) {
+ hash = _cairo_hash_bytes (hash,
+ &gradient->stops[n].offset,
+ sizeof (double));
+ hash = _cairo_hash_bytes (hash,
+ &gradient->stops[n].color,
+ sizeof (cairo_color_stop_t));
+ }
+
+ return hash;
+}
+
+unsigned long
+_cairo_linear_pattern_hash (unsigned long hash,
+ const cairo_linear_pattern_t *linear)
+{
+ hash = _cairo_hash_bytes (hash, &linear->pd1, sizeof (linear->pd1));
+ hash = _cairo_hash_bytes (hash, &linear->pd2, sizeof (linear->pd2));
+
+ return _cairo_gradient_color_stops_hash (hash, &linear->base);
+}
+
+unsigned long
+_cairo_radial_pattern_hash (unsigned long hash,
+ const cairo_radial_pattern_t *radial)
+{
+ hash = _cairo_hash_bytes (hash, &radial->cd1.center, sizeof (radial->cd1.center));
+ hash = _cairo_hash_bytes (hash, &radial->cd1.radius, sizeof (radial->cd1.radius));
+ hash = _cairo_hash_bytes (hash, &radial->cd2.center, sizeof (radial->cd2.center));
+ hash = _cairo_hash_bytes (hash, &radial->cd2.radius, sizeof (radial->cd2.radius));
+
+ return _cairo_gradient_color_stops_hash (hash, &radial->base);
+}
+
+static unsigned long
+_cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh)
+{
+ const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+ unsigned int i, n = _cairo_array_num_elements (&mesh->patches);
+
+ for (i = 0; i < n; i++)
+ hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t));
+
+ return hash;
+}
+
+static unsigned long
+_cairo_surface_pattern_hash (unsigned long hash,
+ const cairo_surface_pattern_t *surface)
+{
+ hash ^= surface->surface->unique_id;
+
+ return hash;
+}
+
+static unsigned long
+_cairo_raster_source_pattern_hash (unsigned long hash,
+ const cairo_raster_source_pattern_t *raster)
+{
+ hash ^= (uintptr_t)raster->user_data;
+
+ return hash;
+}
+
+unsigned long
+_cairo_pattern_hash (const cairo_pattern_t *pattern)
+{
+ unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+
+ if (pattern->status)
+ return 0;
+
+ hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type));
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
+ hash = _cairo_hash_bytes (hash,
+ &pattern->matrix, sizeof (pattern->matrix));
+ hash = _cairo_hash_bytes (hash,
+ &pattern->filter, sizeof (pattern->filter));
+ hash = _cairo_hash_bytes (hash,
+ &pattern->extend, sizeof (pattern->extend));
+ hash = _cairo_hash_bytes (hash,
+ &pattern->has_component_alpha,
+ sizeof (pattern->has_component_alpha));
+ }
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _cairo_raster_source_pattern_hash (hash, (cairo_raster_source_pattern_t *) pattern);
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+_cairo_solid_pattern_equal (const cairo_solid_pattern_t *a,
+ const cairo_solid_pattern_t *b)
+{
+ return _cairo_color_equal (&a->color, &b->color);
+}
+
+static cairo_bool_t
+_cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a,
+ const cairo_gradient_pattern_t *b)
+{
+ unsigned int n;
+
+ if (a->n_stops != b->n_stops)
+ return FALSE;
+
+ for (n = 0; n < a->n_stops; n++) {
+ if (a->stops[n].offset != b->stops[n].offset)
+ return FALSE;
+ if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
+ const cairo_linear_pattern_t *b)
+{
+ if (a->pd1.x != b->pd1.x)
+ return FALSE;
+
+ if (a->pd1.y != b->pd1.y)
+ return FALSE;
+
+ if (a->pd2.x != b->pd2.x)
+ return FALSE;
+
+ if (a->pd2.y != b->pd2.y)
+ return FALSE;
+
+ return _cairo_gradient_color_stops_equal (&a->base, &b->base);
+}
+
+cairo_bool_t
+_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
+ const cairo_radial_pattern_t *b)
+{
+ if (a->cd1.center.x != b->cd1.center.x)
+ return FALSE;
+
+ if (a->cd1.center.y != b->cd1.center.y)
+ return FALSE;
+
+ if (a->cd1.radius != b->cd1.radius)
+ return FALSE;
+
+ if (a->cd2.center.x != b->cd2.center.x)
+ return FALSE;
+
+ if (a->cd2.center.y != b->cd2.center.y)
+ return FALSE;
+
+ if (a->cd2.radius != b->cd2.radius)
+ return FALSE;
+
+ return _cairo_gradient_color_stops_equal (&a->base, &b->base);
+}
+
+static cairo_bool_t
+_cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a,
+ const cairo_mesh_pattern_t *b)
+{
+ const cairo_mesh_patch_t *patch_a, *patch_b;
+ unsigned int i, num_patches_a, num_patches_b;
+
+ num_patches_a = _cairo_array_num_elements (&a->patches);
+ num_patches_b = _cairo_array_num_elements (&b->patches);
+
+ if (num_patches_a != num_patches_b)
+ return FALSE;
+
+ for (i = 0; i < num_patches_a; i++) {
+ patch_a = _cairo_array_index_const (&a->patches, i);
+ patch_b = _cairo_array_index_const (&b->patches, i);
+ if (patch_a == NULL || patch_b == NULL)
+ return FALSE;
+
+ if (memcmp (patch_a, patch_b, sizeof(cairo_mesh_patch_t)) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_surface_pattern_equal (const cairo_surface_pattern_t *a,
+ const cairo_surface_pattern_t *b)
+{
+ return a->surface->unique_id == b->surface->unique_id;
+}
+
+static cairo_bool_t
+_cairo_raster_source_pattern_equal (const cairo_raster_source_pattern_t *a,
+ const cairo_raster_source_pattern_t *b)
+{
+ return a->user_data == b->user_data;
+}
+
+cairo_bool_t
+_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
+{
+ if (a->status || b->status)
+ return FALSE;
+
+ if (a == b)
+ return TRUE;
+
+ if (a->type != b->type)
+ return FALSE;
+
+ if (a->has_component_alpha != b->has_component_alpha)
+ return FALSE;
+
+ if (a->type != CAIRO_PATTERN_TYPE_SOLID) {
+ if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t)))
+ return FALSE;
+
+ if (a->filter != b->filter)
+ return FALSE;
+
+ if (a->extend != b->extend)
+ return FALSE;
+ }
+
+ switch (a->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a,
+ (cairo_solid_pattern_t *) b);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a,
+ (cairo_linear_pattern_t *) b);
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a,
+ (cairo_radial_pattern_t *) b);
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a,
+ (cairo_mesh_pattern_t *) b);
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a,
+ (cairo_surface_pattern_t *) b);
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _cairo_raster_source_pattern_equal ((cairo_raster_source_pattern_t *) a,
+ (cairo_raster_source_pattern_t *) b);
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
+
+/**
+ * cairo_pattern_get_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @red: return value for red component of color, or %NULL
+ * @green: return value for green component of color, or %NULL
+ * @blue: return value for blue component of color, or %NULL
+ * @alpha: return value for alpha component of color, or %NULL
+ *
+ * Gets the solid color for a solid color pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid
+ * color pattern.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_rgba (cairo_pattern_t *pattern,
+ double *red, double *green,
+ double *blue, double *alpha)
+{
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+ double r0, g0, b0, a0;
+
+ if (pattern->status)
+ return pattern->status;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+ _cairo_color_get_rgba (&solid->color, &r0, &g0, &b0, &a0);
+
+ if (red)
+ *red = r0;
+ if (green)
+ *green = g0;
+ if (blue)
+ *blue = b0;
+ if (alpha)
+ *alpha = a0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_surface:
+ * @pattern: a #cairo_pattern_t
+ * @surface: return value for surface of pattern, or %NULL
+ *
+ * Gets the surface of a surface pattern. The reference returned in
+ * @surface is owned by the pattern; the caller should call
+ * cairo_surface_reference() if the surface is to be retained.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface
+ * pattern.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_surface (cairo_pattern_t *pattern,
+ cairo_surface_t **surface)
+{
+ cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern;
+
+ if (pattern->status)
+ return pattern->status;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+ if (surface)
+ *surface = spat->surface;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_color_stop_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @index: index of the stop to return data for
+ * @offset: return value for the offset of the stop, or %NULL
+ * @red: return value for red component of color, or %NULL
+ * @green: return value for green component of color, or %NULL
+ * @blue: return value for blue component of color, or %NULL
+ * @alpha: return value for alpha component of color, or %NULL
+ *
+ * Gets the color and offset information at the given @index for a
+ * gradient pattern. Values of @index 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 = linear->pd1.x;
+ if (y0)
+ *y0 = linear->pd1.y;
+ if (x1)
+ *x1 = linear->pd2.x;
+ if (y1)
+ *y1 = linear->pd2.y;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_radial_circles:
+ * @pattern: a #cairo_pattern_t
+ * @x0: return value for the x coordinate of the center of the first circle, or %NULL
+ * @y0: return value for the y coordinate of the center of the first circle, or %NULL
+ * @r0: return value for the radius of the first circle, or %NULL
+ * @x1: return value for the x coordinate of the center of the second circle, or %NULL
+ * @y1: return value for the y coordinate of the center of the second circle, or %NULL
+ * @r1: return value for the radius of the second circle, or %NULL
+ *
+ * Gets the gradient endpoint circles for a radial gradient, each
+ * specified as a center coordinate and a radius.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial
+ * gradient pattern.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
+ double *x0, double *y0, double *r0,
+ double *x1, double *y1, double *r1)
+{
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern;
+
+ if (pattern->status)
+ return pattern->status;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+ if (x0)
+ *x0 = radial->cd1.center.x;
+ if (y0)
+ *y0 = radial->cd1.center.y;
+ if (r0)
+ *r0 = radial->cd1.radius;
+ if (x1)
+ *x1 = radial->cd2.center.x;
+ if (y1)
+ *y1 = radial->cd2.center.y;
+ if (r1)
+ *r1 = radial->cd2.radius;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_mesh_pattern_get_patch_count:
+ * @pattern: a #cairo_pattern_t
+ * @count: return value for the number patches, or %NULL
+ *
+ * Gets the number of patches specified in the given mesh pattern.
+ *
+ * The number only includes patches which have been finished by
+ * calling cairo_mesh_pattern_end_patch(). For example it will be 0
+ * during the definition of the first patch.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh
+ * pattern.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern,
+ unsigned int *count)
+{
+ cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+
+ if (unlikely (pattern->status))
+ return pattern->status;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+ if (count) {
+ *count = _cairo_array_num_elements (&mesh->patches);
+ if (mesh->current_patch)
+ *count -= 1;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_mesh_pattern_get_patch_count);
+
+/**
+ * cairo_mesh_pattern_get_path:
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ *
+ * Gets path defining the patch @patch_num for a mesh
+ * pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_mesh_pattern_get_patch_count().
+ *
+ * Return value: the path defining the patch, or a path with status
+ * %CAIRO_STATUS_INVALID_INDEX if @patch_num or @point_num is not
+ * valid for @pattern. If @pattern is not a mesh pattern, a path with
+ * status %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is returned.
+ *
+ * Since: 1.12
+ **/
+cairo_path_t *
+cairo_mesh_pattern_get_path (cairo_pattern_t *pattern,
+ unsigned int patch_num)
+{
+ cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+ const cairo_mesh_patch_t *patch;
+ cairo_path_t *path;
+ cairo_path_data_t *data;
+ unsigned int patch_count;
+ int l, current_point;
+
+ if (unlikely (pattern->status))
+ return _cairo_path_create_in_error (pattern->status);
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+ return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH));
+
+ patch_count = _cairo_array_num_elements (&mesh->patches);
+ if (mesh->current_patch)
+ patch_count--;
+
+ if (unlikely (patch_num >= patch_count))
+ return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
+
+ patch = _cairo_array_index_const (&mesh->patches, patch_num);
+
+ if (patch == NULL)
+ return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
+
+ path = malloc (sizeof (cairo_path_t));
+ if (path == NULL)
+ return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ path->num_data = 18;
+ path->data = _cairo_malloc_ab (path->num_data,
+ sizeof (cairo_path_data_t));
+ if (path->data == NULL) {
+ free (path);
+ return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ data = path->data;
+ data[0].header.type = CAIRO_PATH_MOVE_TO;
+ data[0].header.length = 2;
+ data[1].point.x = patch->points[0][0].x;
+ data[1].point.y = patch->points[0][0].y;
+ data += data[0].header.length;
+
+ current_point = 0;
+
+ for (l = 0; l < 4; l++) {
+ int i, j, k;
+
+ data[0].header.type = CAIRO_PATH_CURVE_TO;
+ data[0].header.length = 4;
+
+ for (k = 1; k < 4; k++) {
+ current_point = (current_point + 1) % 12;
+ i = mesh_path_point_i[current_point];
+ j = mesh_path_point_j[current_point];
+ data[k].point.x = patch->points[i][j].x;
+ data[k].point.y = patch->points[i][j].y;
+ }
+
+ data += data[0].header.length;
+ }
+
+ path->status = CAIRO_STATUS_SUCCESS;
+
+ return path;
+}
+slim_hidden_def (cairo_mesh_pattern_get_path);
+
+/**
+ * cairo_mesh_pattern_get_corner_color_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ * @corner_num: the corner number to return data for
+ * @red: return value for red component of color, or %NULL
+ * @green: return value for green component of color, or %NULL
+ * @blue: return value for blue component of color, or %NULL
+ * @alpha: return value for alpha component of color, or %NULL
+ *
+ * Gets the color information in corner @corner_num of patch
+ * @patch_num for a mesh pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_mesh_pattern_get_patch_count().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
+ * if @patch_num or @corner_num is not valid for @pattern. If
+ * @pattern is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH
+ * is returned.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern,
+ unsigned int patch_num,
+ unsigned int corner_num,
+ double *red, double *green,
+ double *blue, double *alpha)
+{
+ cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+ unsigned int patch_count;
+ const cairo_mesh_patch_t *patch;
+
+ if (unlikely (pattern->status))
+ return pattern->status;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+ if (unlikely (corner_num > 3))
+ return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+ patch_count = _cairo_array_num_elements (&mesh->patches);
+ if (mesh->current_patch)
+ patch_count--;
+
+ if (unlikely (patch_num >= patch_count))
+ return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+ patch = _cairo_array_index_const (&mesh->patches, patch_num);
+ if (patch == NULL)
+ return _cairo_error(CAIRO_STATUS_NULL_POINTER);
+
+ if (red)
+ *red = patch->colors[corner_num].red;
+ if (green)
+ *green = patch->colors[corner_num].green;
+ if (blue)
+ *blue = patch->colors[corner_num].blue;
+ if (alpha)
+ *alpha = patch->colors[corner_num].alpha;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_mesh_pattern_get_corner_color_rgba);
+
+/**
+ * cairo_mesh_pattern_get_control_point:
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ * @point_num: the control point number to return data for
+ * @x: return value for the x coordinate of the control point, or %NULL
+ * @y: return value for the y coordinate of the control point, or %NULL
+ *
+ * Gets the control point @point_num of patch @patch_num for a mesh
+ * pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_mesh_pattern_get_patch_count().
+ *
+ * Valid values for @point_num are from 0 to 3 and identify the
+ * control points as explained in cairo_pattern_create_mesh().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
+ * if @patch_num or @point_num is not valid for @pattern. If @pattern
+ * is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is
+ * returned.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
+ unsigned int patch_num,
+ unsigned int point_num,
+ double *x, double *y)
+{
+ cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+ const cairo_mesh_patch_t *patch;
+ unsigned int patch_count;
+ int i, j;
+
+ if (pattern->status)
+ return pattern->status;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_MESH)
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+ if (point_num > 3)
+ return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+ patch_count = _cairo_array_num_elements (&mesh->patches);
+ if (mesh->current_patch)
+ patch_count--;
+
+ if (unlikely (patch_num >= patch_count))
+ return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+ patch = _cairo_array_index_const (&mesh->patches, patch_num);
+ if (patch == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ i = mesh_control_point_i[point_num];
+ j = mesh_control_point_j[point_num];
+
+ if (x)
+ *x = patch->points[i][j].x;
+ if (y)
+ *y = patch->points[i][j].y;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_mesh_pattern_get_control_point);
+
+cairo_status_t
+cairo_pattern_set_sigma (cairo_pattern_t *pattern,
+ const double x_sigma,
+ const double y_sigma)
+{
+ double x = x_sigma;
+ double y = y_sigma;
+
+ if (pattern->status)
+ return pattern->status;
+
+ if (pattern->filter != CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
+
+
+ if (x < 0.0)
+ x = 0.0;
+ if (y < 0.0)
+ y = 0.0;
+
+ if (pattern->x_sigma == x &&
+ pattern->y_sigma == y)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x > CAIRO_MAX_BLUR >> 1)
+ x = CAIRO_MAX_BLUR >> 1;
+ if (y > CAIRO_MAX_BLUR >> 1)
+ y = CAIRO_MAX_BLUR >> 1;
+
+ pattern->x_sigma = x;
+ pattern->y_sigma = y;
+ pattern->convolution_changed = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+cairo_pattern_get_sigma (cairo_pattern_t *pattern,
+ double *x_sigma,
+ double *y_sigma)
+{
+ if (pattern->status)
+ return pattern->status;
+
+ if (pattern->filter != CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
+
+ *x_sigma = pattern->x_sigma;
+ *y_sigma = pattern->y_sigma;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_pattern_create_gaussian_matrix (cairo_pattern_t *pattern,
+ double line_width)
+{
+ double x_sigma, y_sigma;
+ unsigned int x_factor, y_factor;
+
+ double x_sigma_sq, y_sigma_sq;
+ int row, col, n;
+ double *buffer;
+ int i, x, y;
+ double u, v;
+ double u1, v1;
+ double sum = 0.0;
+ cairo_rectangle_int_t extents;
+ int width = CAIRO_MIN_SHRINK_SIZE;
+ int height = CAIRO_MIN_SHRINK_SIZE;
+ double min_line_width = (line_width >= 1.0) ? CAIRO_MIN_LINE_WIDTH : line_width;
+ double max_sigma = CAIRO_MAX_SIGMA;
+ double test_line_width = line_width;
+
+ if (pattern->status)
+ return pattern->status;
+
+ if (pattern->filter != CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
+
+ if (! pattern->convolution_changed)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_surface_get_extents (((cairo_surface_pattern_t *)pattern)->surface, &extents)) {
+ width = extents.width;
+ height = extents.height;
+ }
+
+ x_factor = 1;
+ y_factor = 1;
+ x_sigma = pattern->x_sigma;
+ y_sigma = pattern->y_sigma;
+
+ /* no blur */
+ if (x_sigma == 0.0 && y_sigma == 0.0) {
+ if (pattern->convolution_matrix)
+ free (pattern->convolution_matrix);
+ pattern->convolution_matrix = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (x_sigma == 0.0)
+ pattern->x_radius = 0;
+ else {
+ while (x_sigma >= max_sigma && test_line_width >= min_line_width) {
+ if (width <= CAIRO_MIN_SHRINK_SIZE)
+ break;
+
+ x_sigma /= 2.0;
+ x_factor *= 2;
+ width *= 0.5;
+ test_line_width *= 0.5;
+ }
+ if (x_sigma > max_sigma)
+ x_sigma = max_sigma;
+ /* XXX: skia uses 3, we follow css spec which is 2 */
+ pattern->x_radius = ceil (x_sigma * 2);
+ }
+ pattern->shrink_factor_x = x_factor;
+ test_line_width = line_width;
+
+ if (y_sigma == 0.0)
+ pattern->y_radius = 0;
+ else {
+ while (y_sigma >= max_sigma && test_line_width >= min_line_width) {
+ if (height <= CAIRO_MIN_SHRINK_SIZE)
+ break;
+
+ y_sigma *= 0.5;
+ y_factor *= 2;
+ height *= 0.5;
+ test_line_width *= 0.5;
+ }
+ if (y_sigma > max_sigma)
+ y_sigma = max_sigma;
+ pattern->y_radius = ceil (y_sigma * 2);
+ }
+ pattern->shrink_factor_y = y_factor;
+
+ if (pattern->convolution_matrix)
+ free (pattern->convolution_matrix);
+
+ pattern->convolution_matrix = NULL;
+
+ /* 2D gaussian
+ * f(x, y) = exp (-((x-x0)^2/(2*x_sigma^2)+(y-y0)^2/(2*y_sigma*2)))
+ */
+ row = pattern->y_radius;
+ col = pattern->x_radius;
+ n = (2 * row + 1) * (2 * col + 1);
+
+ x_sigma_sq = 2 * x_sigma * x_sigma;
+ y_sigma_sq = 2 * y_sigma * y_sigma;
+
+ buffer = _cairo_malloc_ab (n, sizeof (double));
+ if (buffer == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ i = 0;
+ for (y = -row; y <= row; y++) {
+ for (x = - col; x <= col; x++) {
+ u = x * x;
+ v = y * y;
+ if (u == 0.0)
+ u1 = 0.0;
+ else
+ u1 = u / x_sigma_sq;
+
+ if (v == 0.0)
+ v1 = 0.0;
+ else
+ v1 = v / y_sigma_sq;
+ buffer[i] = exp (-(u1 + v1));
+ sum += buffer[i];
+ i++;
+ }
+ }
+
+ /* normalize */
+ sum = 1.0 / sum;
+ for (i = 0; i < n; i++)
+ buffer[i] *= sum;
+
+ pattern->convolution_matrix = buffer;
+ pattern->convolution_changed = FALSE;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pattern_reset_static_data (void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++)
+ _freed_pool_reset (&freed_pattern_pool[i]);
+}
+
+static void
+_cairo_debug_print_surface_pattern (FILE *file,
+ const cairo_surface_pattern_t *pattern)
+{
+ printf (" surface type: %d\n", pattern->surface->type);
+}
+
+static void
+_cairo_debug_print_raster_source_pattern (FILE *file,
+ const cairo_raster_source_pattern_t *raster)
+{
+ printf (" content: %x, size %dx%d\n", raster->content, raster->extents.width, raster->extents.height);
+}
+
+static void
+_cairo_debug_print_linear_pattern (FILE *file,
+ const cairo_linear_pattern_t *pattern)
+{
+}
+
+static void
+_cairo_debug_print_radial_pattern (FILE *file,
+ const cairo_radial_pattern_t *pattern)
+{
+}
+
+static void
+_cairo_debug_print_mesh_pattern (FILE *file,
+ const cairo_mesh_pattern_t *pattern)
+{
+}
+
+void
+_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern)
+{
+ const char *s;
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID: s = "solid"; break;
+ case CAIRO_PATTERN_TYPE_SURFACE: s = "surface"; break;
+ case CAIRO_PATTERN_TYPE_LINEAR: s = "linear"; break;
+ case CAIRO_PATTERN_TYPE_RADIAL: s = "radial"; break;
+ case CAIRO_PATTERN_TYPE_MESH: s = "mesh"; break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE: s = "raster"; break;
+ default: s = "invalid"; ASSERT_NOT_REACHED; break;
+ }
+
+ fprintf (file, "pattern: %s\n", s);
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+ return;
+
+ switch (pattern->extend) {
+ case CAIRO_EXTEND_NONE: s = "none"; break;
+ case CAIRO_EXTEND_REPEAT: s = "repeat"; break;
+ case CAIRO_EXTEND_REFLECT: s = "reflect"; break;
+ case CAIRO_EXTEND_PAD: s = "pad"; break;
+ default: s = "invalid"; ASSERT_NOT_REACHED; break;
+ }
+ fprintf (file, " extend: %s\n", s);
+
+ switch (pattern->filter) {
+ case CAIRO_FILTER_FAST: s = "fast"; break;
+ case CAIRO_FILTER_GOOD: s = "good"; break;
+ case CAIRO_FILTER_BEST: s = "best"; break;
+ case CAIRO_FILTER_NEAREST: s = "nearest"; break;
+ case CAIRO_FILTER_BILINEAR: s = "bilinear"; break;
+ case CAIRO_FILTER_GAUSSIAN: s = "guassian"; break;
+ default: s = "invalid"; ASSERT_NOT_REACHED; break;
+ }
+ fprintf (file, " filter: %s\n", s);
+ fprintf (file, " matrix: [%g %g %g %g %g %g]\n",
+ pattern->matrix.xx, pattern->matrix.yx,
+ pattern->matrix.xy, pattern->matrix.yy,
+ pattern->matrix.x0, pattern->matrix.y0);
+ switch (pattern->type) {
+ default:
+ case CAIRO_PATTERN_TYPE_SOLID:
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ _cairo_debug_print_raster_source_pattern (file, (cairo_raster_source_pattern_t *)pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ _cairo_debug_print_surface_pattern (file, (cairo_surface_pattern_t *)pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ _cairo_debug_print_linear_pattern (file, (cairo_linear_pattern_t *)pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ _cairo_debug_print_radial_pattern (file, (cairo_radial_pattern_t *)pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ _cairo_debug_print_mesh_pattern (file, (cairo_mesh_pattern_t *)pattern);
+ break;
+ }
+}
+
+static unsigned long
+_cairo_solid_pattern_alpha_hash (unsigned long hash,
+ const cairo_solid_pattern_t *solid)
+{
+ return _cairo_hash_bytes (hash, &solid->color.alpha, sizeof (double));
+}
+
+unsigned long
+_cairo_pattern_hash_with_hash (unsigned long hash,
+ const cairo_pattern_t *pattern,
+ const cairo_bool_t use_color)
+{
+ if (pattern->status)
+ return hash;
+
+ 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 (double) * 4);
+ 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));
+ }
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
+ hash = _cairo_hash_bytes (hash, &pattern->x_sigma, sizeof (double) * 2);
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ if (use_color)
+ return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern);
+ else
+ return _cairo_solid_pattern_alpha_hash (hash, (cairo_solid_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern);
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _cairo_raster_source_pattern_hash (hash, (cairo_raster_source_pattern_t *) pattern);
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h
new file mode 100755
index 000000000..6e1ae1833
--- /dev/null
+++ b/src/cairo-pdf-operators-private.h
@@ -0,0 +1,173 @@
+/* -*- 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-error-private.h"
+#include "cairo-types-private.h"
+
+/* The glyph buffer size is based on the expected maximum glyphs in a
+ * line so that an entire line can be emitted in as one string. If the
+ * glyphs in a line exceeds this size the only downside is the slight
+ * overhead of emitting two strings.
+ */
+#define PDF_GLYPH_BUFFER_SIZE 200
+
+typedef cairo_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;
+ cairo_bool_t is_latin;
+ int num_glyphs;
+ double glyph_buf_x_pos;
+ cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE];
+
+ /* PDF line style */
+ cairo_bool_t has_line_style;
+ double line_width;
+ cairo_line_cap_t line_cap;
+ cairo_line_join_t line_join;
+ double miter_limit;
+ cairo_bool_t has_dashes;
+} cairo_pdf_operators_t;
+
+cairo_private void
+_cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators,
+ cairo_output_stream_t *stream,
+ cairo_matrix_t *cairo_to_pdf,
+ cairo_scaled_font_subsets_t *font_subsets);
+
+cairo_private cairo_status_t
+_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators);
+
+cairo_private void
+_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators,
+ cairo_pdf_operators_use_font_subset_t use_font_subset,
+ void *closure);
+
+cairo_private void
+_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators,
+ cairo_output_stream_t *stream);
+
+
+cairo_private void
+_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators,
+ cairo_matrix_t *cairo_to_pdf);
+
+cairo_private void
+_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators,
+ cairo_bool_t enable);
+
+cairo_private cairo_status_t
+_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators);
+
+cairo_private void
+_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators,
+ const cairo_stroke_style_t *style,
+ double scale);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font);
+
+#endif /* CAIRO_PDF_OPERATORS_H */
diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
new file mode 100755
index 000000000..fceaf1cc4
--- /dev/null
+++ b/src/cairo-pdf-operators.c
@@ -0,0 +1,1557 @@
+/* -*- 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 enum _cairo_word_wrap_state {
+ WRAP_STATE_DELIMITER,
+ WRAP_STATE_WORD,
+ WRAP_STATE_STRING,
+ WRAP_STATE_HEXSTRING
+} cairo_word_wrap_state_t;
+
+
+typedef struct _word_wrap_stream {
+ cairo_output_stream_t base;
+ cairo_output_stream_t *output;
+ int max_column;
+ int column;
+ cairo_word_wrap_state_t state;
+ cairo_bool_t in_escape;
+ int escape_digits;
+} word_wrap_stream_t;
+
+
+
+/* Emit word bytes up to the next delimiter character */
+static int
+_word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream,
+ const unsigned char *data, int length)
+{
+ const unsigned char *s = data;
+ int count = 0;
+
+ while (length--) {
+ if (_cairo_isspace (*s) || *s == '<' || *s == '(') {
+ stream->state = WRAP_STATE_DELIMITER;
+ break;
+ }
+
+ count++;
+ stream->column++;
+ s++;
+ }
+
+ if (count)
+ _cairo_output_stream_write (stream->output, data, count);
+
+ return count;
+}
+
+
+/* Emit hexstring bytes up to either the end of the ASCII hexstring or the number
+ * of columns remaining.
+ */
+static int
+_word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream,
+ const unsigned char *data, int length)
+{
+ const unsigned char *s = data;
+ int count = 0;
+ cairo_bool_t newline = FALSE;
+
+ while (length--) {
+ count++;
+ stream->column++;
+ if (*s == '>') {
+ stream->state = WRAP_STATE_DELIMITER;
+ break;
+ }
+
+ if (stream->column > stream->max_column) {
+ newline = TRUE;
+ break;
+ }
+ s++;
+ }
+
+ if (count)
+ _cairo_output_stream_write (stream->output, data, count);
+
+ if (newline) {
+ _cairo_output_stream_printf (stream->output, "\n");
+ stream->column = 0;
+ }
+
+ return count;
+}
+
+/* Count up to either the end of the string or the number of columns
+ * remaining.
+ */
+static int
+_word_wrap_stream_count_string_up_to (word_wrap_stream_t *stream,
+ const unsigned char *data, int length)
+{
+ const unsigned char *s = data;
+ int count = 0;
+ cairo_bool_t newline = FALSE;
+
+ while (length--) {
+ count++;
+ stream->column++;
+ if (!stream->in_escape) {
+ if (*s == ')') {
+ stream->state = WRAP_STATE_DELIMITER;
+ break;
+ }
+ if (*s == '\\') {
+ stream->in_escape = TRUE;
+ stream->escape_digits = 0;
+ } else if (stream->column > stream->max_column) {
+ newline = TRUE;
+ break;
+ }
+ } else {
+ if (!_cairo_isdigit(*s) || ++stream->escape_digits == 3)
+ stream->in_escape = FALSE;
+ }
+ s++;
+ }
+
+ if (count)
+ _cairo_output_stream_write (stream->output, data, count);
+
+ if (newline) {
+ _cairo_output_stream_printf (stream->output, "\\\n");
+ stream->column = 0;
+ }
+
+ return count;
+}
+
+static cairo_status_t
+_word_wrap_stream_write (cairo_output_stream_t *base,
+ const unsigned char *data,
+ unsigned int length)
+{
+ word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
+ int count;
+
+ while (length) {
+ switch (stream->state) {
+ case WRAP_STATE_WORD:
+ count = _word_wrap_stream_count_word_up_to (stream, data, length);
+ break;
+ case WRAP_STATE_HEXSTRING:
+ count = _word_wrap_stream_count_hexstring_up_to (stream, data, length);
+ break;
+ case WRAP_STATE_STRING:
+ count = _word_wrap_stream_count_string_up_to (stream, data, length);
+ break;
+ case WRAP_STATE_DELIMITER:
+ count = 1;
+ stream->column++;
+ if (*data == '\n' || stream->column >= stream->max_column) {
+ _cairo_output_stream_printf (stream->output, "\n");
+ stream->column = 0;
+ } else if (*data == '<') {
+ stream->state = WRAP_STATE_HEXSTRING;
+ } else if (*data == '(') {
+ stream->state = WRAP_STATE_STRING;
+ } else if (!_cairo_isspace (*data)) {
+ stream->state = WRAP_STATE_WORD;
+ }
+ if (*data != '\n')
+ _cairo_output_stream_write (stream->output, data, 1);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ count = length;
+ break;
+ }
+ data += count;
+ length -= count;
+ }
+
+ return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_status_t
+_word_wrap_stream_close (cairo_output_stream_t *base)
+{
+ word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
+
+ return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_output_stream_t *
+_word_wrap_stream_create (cairo_output_stream_t *output, 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->state = WRAP_STATE_DELIMITER;
+ stream->in_escape = FALSE;
+ stream->escape_digits = 0;
+
+ return &stream->base;
+}
+
+typedef struct _pdf_path_info {
+ cairo_output_stream_t *output;
+ cairo_matrix_t *path_transform;
+ cairo_line_cap_t line_cap;
+ cairo_point_t last_move_to_point;
+ cairo_bool_t has_sub_path;
+} pdf_path_info_t;
+
+static cairo_status_t
+_cairo_pdf_path_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ pdf_path_info_t *info = closure;
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ info->last_move_to_point = *point;
+ info->has_sub_path = FALSE;
+ cairo_matrix_transform_point (info->path_transform, &x, &y);
+ _cairo_output_stream_printf (info->output,
+ "%g %g m ", x, y);
+
+ return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ pdf_path_info_t *info = closure;
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
+ ! info->has_sub_path &&
+ point->x == info->last_move_to_point.x &&
+ point->y == info->last_move_to_point.y)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ info->has_sub_path = TRUE;
+ cairo_matrix_transform_point (info->path_transform, &x, &y);
+ _cairo_output_stream_printf (info->output,
+ "%g %g l ", x, y);
+
+ return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ pdf_path_info_t *info = closure;
+ double bx = _cairo_fixed_to_double (b->x);
+ double by = _cairo_fixed_to_double (b->y);
+ double cx = _cairo_fixed_to_double (c->x);
+ double cy = _cairo_fixed_to_double (c->y);
+ double dx = _cairo_fixed_to_double (d->x);
+ double dy = _cairo_fixed_to_double (d->y);
+
+ info->has_sub_path = TRUE;
+ cairo_matrix_transform_point (info->path_transform, &bx, &by);
+ cairo_matrix_transform_point (info->path_transform, &cx, &cy);
+ cairo_matrix_transform_point (info->path_transform, &dx, &dy);
+ _cairo_output_stream_printf (info->output,
+ "%g %g %g %g %g %g c ",
+ bx, by, cx, cy, dx, dy);
+ return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_close_path (void *closure)
+{
+ pdf_path_info_t *info = closure;
+
+ if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
+ ! info->has_sub_path)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_output_stream_printf (info->output,
+ "h\n");
+
+ return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box)
+{
+ double x1 = _cairo_fixed_to_double (box->p1.x);
+ double y1 = _cairo_fixed_to_double (box->p1.y);
+ double x2 = _cairo_fixed_to_double (box->p2.x);
+ double y2 = _cairo_fixed_to_double (box->p2.y);
+
+ cairo_matrix_transform_point (info->path_transform, &x1, &y1);
+ cairo_matrix_transform_point (info->path_transform, &x2, &y2);
+ _cairo_output_stream_printf (info->output,
+ "%g %g %g %g re ",
+ x1, y1, x2 - x1, y2 - y1);
+
+ return _cairo_output_stream_get_status (info->output);
+}
+
+/* The line cap value is needed to workaround the fact that PostScript
+ * and PDF semantics for stroking degenerate sub-paths do not match
+ * cairo semantics. (PostScript draws something for any line cap
+ * value, while cairo draws something only for round caps).
+ *
+ * When using this function to emit a path to be filled, rather than
+ * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that
+ * the stroke workaround will not modify the path being emitted.
+ */
+static cairo_status_t
+_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t*path,
+ cairo_matrix_t *path_transform,
+ cairo_line_cap_t line_cap)
+{
+ cairo_output_stream_t *word_wrap;
+ cairo_status_t status, status2;
+ pdf_path_info_t info;
+ cairo_box_t box;
+
+ word_wrap = _word_wrap_stream_create (pdf_operators->stream, 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_pdf_path_move_to,
+ _cairo_pdf_path_line_to,
+ _cairo_pdf_path_curve_to,
+ _cairo_pdf_path_close_path,
+ &info);
+ }
+
+ status2 = _cairo_output_stream_destroy (word_wrap);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule)
+{
+ const char *pdf_operator;
+ cairo_status_t status;
+
+ if (pdf_operators->in_text_object) {
+ status = _cairo_pdf_operators_end_text (pdf_operators);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (! path->has_current_point) {
+ /* construct an empty path */
+ _cairo_output_stream_printf (pdf_operators->stream, "0 0 m ");
+ } else {
+ status = _cairo_pdf_operators_emit_path (pdf_operators,
+ path,
+ &pdf_operators->cairo_to_pdf,
+ CAIRO_LINE_CAP_ROUND);
+ if (unlikely (status))
+ return status;
+ }
+
+ switch (fill_rule) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FILL_RULE_WINDING:
+ pdf_operator = "W";
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ pdf_operator = "W*";
+ break;
+ }
+
+ _cairo_output_stream_printf (pdf_operators->stream,
+ "%s n\n",
+ pdf_operator);
+
+ return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+static int
+_cairo_pdf_line_cap (cairo_line_cap_t cap)
+{
+ switch (cap) {
+ case CAIRO_LINE_CAP_BUTT:
+ return 0;
+ case CAIRO_LINE_CAP_ROUND:
+ return 1;
+ case CAIRO_LINE_CAP_SQUARE:
+ return 2;
+ default:
+ ASSERT_NOT_REACHED;
+ return 0;
+ }
+}
+
+static int
+_cairo_pdf_line_join (cairo_line_join_t join)
+{
+ switch (join) {
+ case CAIRO_LINE_JOIN_MITER:
+ return 0;
+ case CAIRO_LINE_JOIN_ROUND:
+ return 1;
+ case CAIRO_LINE_JOIN_BEVEL:
+ return 2;
+ default:
+ ASSERT_NOT_REACHED;
+ return 0;
+ }
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators,
+ const cairo_stroke_style_t *style,
+ double scale)
+{
+ double *dash = style->dash;
+ int num_dashes = style->num_dashes;
+ double dash_offset = style->dash_offset;
+ double line_width = style->line_width * scale;
+
+ /* PostScript has "special needs" when it comes to zero-length
+ * dash segments with butt caps. It apparently (at least
+ * according to ghostscript) draws hairlines for this
+ * case. That's not what the cairo semantics want, so we first
+ * touch up the array to eliminate any 0.0 values that will
+ * result in "on" segments.
+ */
+ if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) {
+ int i;
+
+ /* If there's an odd number of dash values they will each get
+ * interpreted as both on and off. So we first explicitly
+ * expand the array to remove the duplicate usage so that we
+ * can modify some of the values.
+ */
+ if (num_dashes % 2) {
+ dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double));
+ if (unlikely (dash == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (dash, style->dash, num_dashes * sizeof (double));
+ memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double));
+
+ num_dashes *= 2;
+ }
+
+ for (i = 0; i < num_dashes; i += 2) {
+ if (dash[i] == 0.0) {
+ /* Do not modify the dashes in-place, as we may need to also
+ * replay this stroke to an image fallback.
+ */
+ if (dash == style->dash) {
+ dash = _cairo_malloc_ab (num_dashes, sizeof (double));
+ if (unlikely (dash == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ memcpy (dash, style->dash, num_dashes * sizeof (double));
+ }
+
+ /* If we're at the front of the list, we first rotate
+ * two elements from the end of the list to the front
+ * of the list before folding away the 0.0. Or, if
+ * there are only two dash elements, then there is
+ * nothing at all to draw.
+ */
+ if (i == 0) {
+ double last_two[2];
+
+ if (num_dashes == 2) {
+ free (dash);
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ /* The cases of num_dashes == 0, 1, or 3 elements
+ * cannot exist, so the rotation of 2 elements
+ * will always be safe */
+ memcpy (last_two, dash + num_dashes - 2, sizeof (last_two));
+ memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double));
+ memcpy (dash, last_two, sizeof (last_two));
+ dash_offset += dash[0] + dash[1];
+ i = 2;
+ }
+ dash[i-1] += dash[i+1];
+ num_dashes -= 2;
+ memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double));
+ /* If we might have just rotated, it's possible that
+ * we rotated a 0.0 value to the front of the list.
+ * Set i to -2 so it will get incremented to 0. */
+ if (i == 2)
+ i = -2;
+ }
+ }
+ }
+
+ if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) {
+ _cairo_output_stream_printf (pdf_operators->stream,
+ "%f w\n",
+ line_width);
+ pdf_operators->line_width = line_width;
+ }
+
+ if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) {
+ _cairo_output_stream_printf (pdf_operators->stream,
+ "%d J\n",
+ _cairo_pdf_line_cap (style->line_cap));
+ pdf_operators->line_cap = style->line_cap;
+ }
+
+ if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) {
+ _cairo_output_stream_printf (pdf_operators->stream,
+ "%d j\n",
+ _cairo_pdf_line_join (style->line_join));
+ pdf_operators->line_join = style->line_join;
+ }
+
+ if (num_dashes) {
+ int d;
+
+ _cairo_output_stream_printf (pdf_operators->stream, "[");
+ for (d = 0; d < num_dashes; d++)
+ _cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale);
+ _cairo_output_stream_printf (pdf_operators->stream, "] %f d\n",
+ dash_offset * scale);
+ pdf_operators->has_dashes = TRUE;
+ } else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) {
+ _cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n");
+ pdf_operators->has_dashes = FALSE;
+ }
+ if (dash != style->dash)
+ free (dash);
+
+ if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) {
+ _cairo_output_stream_printf (pdf_operators->stream,
+ "%f M ",
+ style->miter_limit < 1.0 ? 1.0 : style->miter_limit);
+ pdf_operators->miter_limit = style->miter_limit;
+ }
+ pdf_operators->has_line_style = TRUE;
+
+ return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Scale the matrix so the largest absolute value of the non
+ * translation components is 1.0. Return the scale required to restore
+ * the matrix to the original values.
+ *
+ * eg the matrix [ 100 0 0 50 20 10 ]
+ *
+ * is rescaled to [ 1 0 0 0.5 0.2 0.1 ]
+ * and the scale returned is 100
+ */
+static void
+_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale)
+{
+ double s;
+
+ s = fabs (m->xx);
+ if (fabs (m->xy) > s)
+ s = fabs (m->xy);
+ if (fabs (m->yx) > s)
+ s = fabs (m->yx);
+ if (fabs (m->yy) > s)
+ s = fabs (m->yy);
+ *scale = s;
+ s = 1.0/s;
+ cairo_matrix_scale (m, s, s);
+}
+
+static cairo_int_status_t
+_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ const char *pdf_operator)
+{
+ cairo_int_status_t status;
+ cairo_matrix_t m, path_transform;
+ cairo_bool_t has_ctm = TRUE;
+ double scale = 1.0;
+
+ if (pdf_operators->in_text_object) {
+ status = _cairo_pdf_operators_end_text (pdf_operators);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Optimize away the stroke ctm when it does not affect the
+ * stroke. There are other ctm cases that could be optimized
+ * however this is the most common.
+ */
+ if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 &&
+ fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0)
+ {
+ has_ctm = FALSE;
+ }
+
+ /* The PDF CTM is transformed to the user space CTM when stroking
+ * so the corect 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,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse)
+{
+ return _cairo_pdf_operators_emit_stroke (pdf_operators,
+ path,
+ style,
+ ctm,
+ ctm_inverse,
+ "S");
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule)
+{
+ const char *pdf_operator;
+ cairo_status_t status;
+
+ if (pdf_operators->in_text_object) {
+ status = _cairo_pdf_operators_end_text (pdf_operators);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_pdf_operators_emit_path (pdf_operators,
+ path,
+ &pdf_operators->cairo_to_pdf,
+ CAIRO_LINE_CAP_ROUND);
+ if (unlikely (status))
+ return status;
+
+ switch (fill_rule) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FILL_RULE_WINDING:
+ pdf_operator = "f";
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ pdf_operator = "f*";
+ break;
+ }
+
+ _cairo_output_stream_printf (pdf_operators->stream,
+ "%s\n",
+ pdf_operator);
+
+ return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse)
+{
+ const char *operator;
+
+ switch (fill_rule) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FILL_RULE_WINDING:
+ operator = "B";
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ operator = "B*";
+ break;
+ }
+
+ return _cairo_pdf_operators_emit_stroke (pdf_operators,
+ path,
+ style,
+ ctm,
+ ctm_inverse,
+ operator);
+}
+
+static void
+_cairo_pdf_operators_emit_glyph_index (cairo_pdf_operators_t *pdf_operators,
+ cairo_output_stream_t *stream,
+ unsigned int glyph)
+{
+ if (pdf_operators->is_latin) {
+ if (glyph == '(' || glyph == ')' || glyph == '\\')
+ _cairo_output_stream_printf (stream, "\\%c", glyph);
+ else if (glyph >= 0x20 && glyph <= 0x7e)
+ _cairo_output_stream_printf (stream, "%c", glyph);
+ else
+ _cairo_output_stream_printf (stream, "\\%03o", glyph);
+ } else {
+ _cairo_output_stream_printf (stream,
+ "%0*x",
+ pdf_operators->hex_width,
+ glyph);
+ }
+}
+
+#define GLYPH_POSITION_TOLERANCE 0.001
+
+/* Emit the string of glyphs using the 'Tj' operator. This requires
+ * that the glyphs are positioned at their natural glyph advances. */
+static cairo_status_t
+_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators,
+ cairo_output_stream_t *stream)
+{
+ int i;
+
+ _cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<");
+ for (i = 0; i < pdf_operators->num_glyphs; i++) {
+ _cairo_pdf_operators_emit_glyph_index (pdf_operators,
+ stream,
+ pdf_operators->glyphs[i].glyph_index);
+ pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+ }
+ _cairo_output_stream_printf (stream, "%sTj\n", pdf_operators->is_latin ? ")" : ">");
+
+ return _cairo_output_stream_get_status (stream);
+}
+
+/* Emit the string of glyphs using the 'TJ' operator.
+ *
+ * The TJ operator takes an array of strings of glyphs. Each string of
+ * glyphs is displayed using the glyph advances of each glyph to
+ * position the glyphs. A relative adjustment to the glyph advance may
+ * be specified by including the adjustment between two strings. The
+ * adjustment is in units of text space * -1000.
+ */
+static cairo_status_t
+_cairo_pdf_operators_emit_glyph_string_with_positioning (
+ cairo_pdf_operators_t *pdf_operators,
+ cairo_output_stream_t *stream)
+{
+ int i;
+
+ _cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<");
+ for (i = 0; i < pdf_operators->num_glyphs; i++) {
+ if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x)
+ {
+ double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x;
+ int rounded_delta;
+
+ delta = -1000.0*delta;
+ /* As the delta is in 1/1000 of a unit of text space,
+ * rounding to an integer should still provide sufficient
+ * precision. We round the delta before adding to Tm_x so
+ * that we keep track of the accumulated rounding error in
+ * the PDF interpreter and compensate for it when
+ * calculating subsequent deltas.
+ */
+ rounded_delta = _cairo_lround (delta);
+ if (abs(rounded_delta) < 3)
+ rounded_delta = 0;
+ if (rounded_delta != 0) {
+ if (pdf_operators->is_latin) {
+ _cairo_output_stream_printf (stream,
+ ")%d(",
+ rounded_delta);
+ } else {
+ _cairo_output_stream_printf (stream,
+ ">%d<",
+ rounded_delta);
+ }
+ }
+
+ /* Convert the rounded delta back to text
+ * space before adding to the current text
+ * position. */
+ delta = rounded_delta/-1000.0;
+ pdf_operators->cur_x += delta;
+ }
+
+ _cairo_pdf_operators_emit_glyph_index (pdf_operators,
+ stream,
+ pdf_operators->glyphs[i].glyph_index);
+ pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+ }
+ _cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">");
+
+ return _cairo_output_stream_get_status (stream);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators)
+{
+ cairo_output_stream_t *word_wrap_stream;
+ cairo_status_t status, status2;
+ int i;
+ double x;
+
+ if (pdf_operators->num_glyphs == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 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;
+ pdf_operators->is_latin = subset_glyph->is_latin;
+
+ if (subset_glyph->is_composite)
+ pdf_operators->hex_width = 4;
+ else
+ pdf_operators->hex_width = 2;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators)
+{
+ _cairo_output_stream_printf (pdf_operators->stream, "BT\n");
+
+ pdf_operators->in_text_object = TRUE;
+ pdf_operators->num_glyphs = 0;
+ pdf_operators->glyph_buf_x_pos = 0;
+
+ return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators)
+{
+ cairo_status_t status;
+
+ status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (pdf_operators->stream, "ET\n");
+
+ pdf_operators->in_text_object = FALSE;
+
+ return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Compare the scale components of two matrices. The translation
+ * components are ignored. */
+static cairo_bool_t
+_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b)
+{
+ return (a->xx == b->xx &&
+ a->xy == b->xy &&
+ a->yx == b->yx &&
+ a->yy == b->yy);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators,
+ const char *utf8,
+ int utf8_len)
+{
+ uint16_t *utf16;
+ int utf16_len;
+ cairo_status_t status;
+ int i;
+
+ _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText <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 = CAIRO_STATUS_SUCCESS;
+ int i;
+
+ /* If the cluster maps 1 glyph to 1 or more unicode characters, we
+ * first try _map_glyph() with the unicode string to see if it can
+ * use toUnicode to map our glyph to the unicode. This will fail
+ * if the glyph is already mapped to a different unicode string.
+ *
+ * We also go through this path if no unicode mapping was
+ * supplied (utf8_len < 0).
+ *
+ * Mapping a glyph to a zero length unicode string requires the
+ * use of ActualText.
+ */
+ if (num_glyphs == 1 && utf8_len != 0) {
+ status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
+ scaled_font,
+ glyphs->index,
+ utf8,
+ utf8_len,
+ &subset_glyph);
+ if (unlikely (status))
+ return status;
+
+ if (subset_glyph.utf8_is_mapped || utf8_len < 0) {
+ status = _cairo_pdf_operators_emit_glyph (pdf_operators,
+ glyphs,
+ &subset_glyph);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ if (pdf_operators->use_actual_text) {
+ /* Fallback to using ActualText to map zero or more glyphs to a
+ * unicode string. */
+ status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len);
+ if (unlikely (status))
+ return status;
+ }
+
+ cur_glyph = glyphs;
+ /* XXX
+ * If no glyphs, we should put *something* here for the text to be selectable. */
+ for (i = 0; i < num_glyphs; i++) {
+ status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
+ scaled_font,
+ cur_glyph->index,
+ NULL, -1,
+ &subset_glyph);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_emit_glyph (pdf_operators,
+ cur_glyph,
+ &subset_glyph);
+ if (unlikely (status))
+ return status;
+
+ if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
+ cur_glyph--;
+ else
+ cur_glyph++;
+ }
+
+ if (pdf_operators->use_actual_text) {
+ status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_end_actualtext (pdf_operators);
+ }
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_status_t status;
+ int i;
+ cairo_matrix_t text_matrix, invert_y_axis;
+ double x, y;
+ const char *cur_text;
+ cairo_glyph_t *cur_glyph;
+
+ pdf_operators->font_matrix_inverse = scaled_font->font_matrix;
+ status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse);
+ if (status == CAIRO_STATUS_INVALID_MATRIX)
+ return CAIRO_STATUS_SUCCESS;
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ pdf_operators->is_new_text_object = FALSE;
+ if (pdf_operators->in_text_object == FALSE) {
+ status = _cairo_pdf_operators_begin_text (pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ /* Force Tm and Tf to be emitted when starting a new text
+ * object.*/
+ pdf_operators->is_new_text_object = TRUE;
+ }
+
+ cairo_matrix_init_scale (&invert_y_axis, 1, -1);
+ text_matrix = scaled_font->scale;
+
+ /* Invert y axis in 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/src/cairo-pdf-shading-private.h b/src/cairo-pdf-shading-private.h
new file mode 100755
index 000000000..0ca8cb7d5
--- /dev/null
+++ b/src/cairo-pdf-shading-private.h
@@ -0,0 +1,100 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_PDF_SHADING_H
+#define CAIRO_PDF_SHADING_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+#include "cairo-pattern-private.h"
+
+
+typedef struct _cairo_pdf_shading {
+ int shading_type;
+ int bits_per_coordinate;
+ int bits_per_component;
+ int bits_per_flag;
+ double *decode_array;
+ int decode_array_length;
+ unsigned char *data;
+ unsigned long data_length;
+} cairo_pdf_shading_t;
+
+
+/**
+ * _cairo_pdf_shading_init_color:
+ * @shading: a #cairo_pdf_shading_t to initialize
+ * @pattern: the #cairo_mesh_pattern_t to initialize from
+ *
+ * Generate the PDF shading dictionary data for the a PDF type 7
+ * shading from RGB part of the specified mesh pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading,
+ const cairo_mesh_pattern_t *pattern);
+
+
+/**
+ * _cairo_pdf_shading_init_alpha:
+ * @shading: a #cairo_pdf_shading_t to initialize
+ * @pattern: the #cairo_mesh_pattern_t to initialize from
+ *
+ * Generate the PDF shading dictionary data for a PDF type 7
+ * shading from alpha part of the specified mesh pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading,
+ const cairo_mesh_pattern_t *pattern);
+
+/**
+ * _cairo_pdf_shading_fini:
+ * @shading: a #cairo_pdf_shading_t
+ *
+ * Free all resources associated with @shading. After this call,
+ * @shading should not be used again without a subsequent call to
+ * _cairo_pdf_shading_init() again first.
+ **/
+cairo_private void
+_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading);
+
+
+#endif /* CAIRO_PDF_SHADING_H */
diff --git a/src/cairo-pdf-shading.c b/src/cairo-pdf-shading.c
new file mode 100755
index 000000000..6a2fe3615
--- /dev/null
+++ b/src/cairo-pdf-shading.c
@@ -0,0 +1,284 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_PDF_OPERATORS
+
+#include "cairo-pdf-shading-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+#include <float.h>
+
+static unsigned char *
+encode_coordinate (unsigned char *p, double c)
+{
+ uint32_t f;
+
+ f = c;
+ *p++ = f >> 24;
+ *p++ = (f >> 16) & 0xff;
+ *p++ = (f >> 8) & 0xff;
+ *p++ = f & 0xff;
+
+ return p;
+}
+
+static unsigned char *
+encode_point (unsigned char *p, const cairo_point_double_t *point)
+{
+ p = encode_coordinate (p, point->x);
+ p = encode_coordinate (p, point->y);
+
+ return p;
+}
+
+static unsigned char *
+encode_color_component (unsigned char *p, double color)
+{
+ uint16_t c;
+
+ c = _cairo_color_double_to_short (color);
+ *p++ = c >> 8;
+ *p++ = c & 0xff;
+
+ return p;
+}
+
+static unsigned char *
+encode_color (unsigned char *p, const cairo_color_t *color)
+{
+ p = encode_color_component (p, color->red);
+ p = encode_color_component (p, color->green);
+ p = encode_color_component (p, color->blue);
+
+ return p;
+}
+
+static unsigned char *
+encode_alpha (unsigned char *p, const cairo_color_t *color)
+{
+ p = encode_color_component (p, color->alpha);
+
+ return p;
+}
+
+static cairo_status_t
+_cairo_pdf_shading_generate_decode_array (cairo_pdf_shading_t *shading,
+ const cairo_mesh_pattern_t *mesh,
+ cairo_bool_t is_alpha)
+{
+ unsigned int num_color_components, i;
+ cairo_bool_t is_valid;
+
+ if (is_alpha)
+ num_color_components = 1;
+ else
+ num_color_components = 3;
+
+ shading->decode_array_length = 4 + num_color_components * 2;
+ shading->decode_array = _cairo_malloc_ab (shading->decode_array_length,
+ sizeof (double));
+ if (unlikely (shading->decode_array == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ is_valid = _cairo_mesh_pattern_coord_box (mesh,
+ &shading->decode_array[0],
+ &shading->decode_array[2],
+ &shading->decode_array[1],
+ &shading->decode_array[3]);
+
+ assert (is_valid);
+ assert (shading->decode_array[1] - shading->decode_array[0] >= DBL_EPSILON);
+ assert (shading->decode_array[3] - shading->decode_array[2] >= DBL_EPSILON);
+
+ for (i = 0; i < num_color_components; i++) {
+ shading->decode_array[4 + 2*i] = 0;
+ shading->decode_array[5 + 2*i] = 1;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* The ISO32000 specification mandates this order for the points which
+ * define the patch. */
+static const int pdf_points_order_i[16] = {
+ 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 };
+static const int pdf_points_order_j[16] = {
+ 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 };
+
+static cairo_status_t
+_cairo_pdf_shading_generate_data (cairo_pdf_shading_t *shading,
+ const cairo_mesh_pattern_t *mesh,
+ cairo_bool_t is_alpha)
+{
+ const cairo_mesh_patch_t *patch;
+ double x_off, y_off, x_scale, y_scale;
+ unsigned int num_patches;
+ unsigned int num_color_components;
+ unsigned char *p;
+ unsigned int i, j;
+
+ if (is_alpha)
+ num_color_components = 1;
+ else
+ num_color_components = 3;
+
+ num_patches = _cairo_array_num_elements (&mesh->patches);
+ patch = _cairo_array_index_const (&mesh->patches, 0);
+ if (patch == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ /* Each patch requires:
+ *
+ * 1 flag - 1 byte
+ * 16 points. Each point is 2 coordinates. Each coordinate is
+ * stored in 4 bytes.
+ *
+ * 4 colors. Each color is stored in 2 bytes * num_color_components.
+ */
+ shading->data_length = num_patches * (1 + 16 * 2 * 4 + 4 * 2 * num_color_components);
+ shading->data = malloc (shading->data_length);
+ if (unlikely (shading->data == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ x_off = shading->decode_array[0];
+ y_off = shading->decode_array[2];
+ x_scale = UINT32_MAX / (shading->decode_array[1] - x_off);
+ y_scale = UINT32_MAX / (shading->decode_array[3] - y_off);
+
+ p = shading->data;
+ for (i = 0; i < num_patches; i++) {
+ /* edge flag */
+ *p++ = 0;
+
+ /* 16 points */
+ for (j = 0; j < 16; j++) {
+ cairo_point_double_t point;
+ int pi, pj;
+
+ pi = pdf_points_order_i[j];
+ pj = pdf_points_order_j[j];
+ point = patch[i].points[pi][pj];
+
+ /* Transform the point as specified in the decode array */
+ point.x -= x_off;
+ point.y -= y_off;
+ point.x *= x_scale;
+ point.y *= y_scale;
+
+ /* Make sure that rounding errors don't cause
+ * wraparounds */
+ point.x = _cairo_restrict_value (point.x, 0, UINT32_MAX);
+ point.y = _cairo_restrict_value (point.y, 0, UINT32_MAX);
+
+ p = encode_point (p, &point);
+ }
+
+ /* 4 colors */
+ for (j = 0; j < 4; j++) {
+ if (is_alpha)
+ p = encode_alpha (p, &patch[i].colors[j]);
+ else
+ p = encode_color (p, &patch[i].colors[j]);
+ }
+ }
+
+ assert (p == shading->data + shading->data_length);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_shading_init (cairo_pdf_shading_t *shading,
+ const cairo_mesh_pattern_t *mesh,
+ cairo_bool_t is_alpha)
+{
+ cairo_status_t status;
+
+ assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
+ assert (mesh->current_patch == NULL);
+
+ shading->shading_type = 7;
+
+ /*
+ * Coordinates from the minimum to the maximum value of the mesh
+ * map to the [0..UINT32_MAX] range and are represented as
+ * uint32_t values.
+ *
+ * Color components are represented as uint16_t (in a 0.16 fixed
+ * point format, as in the rest of cairo).
+ */
+ shading->bits_per_coordinate = 32;
+ shading->bits_per_component = 16;
+ shading->bits_per_flag = 8;
+
+ shading->decode_array = NULL;
+ shading->data = NULL;
+
+ status = _cairo_pdf_shading_generate_decode_array (shading, mesh, is_alpha);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_shading_generate_data (shading, mesh, is_alpha);
+ if (unlikely (status))
+ free (shading->decode_array);
+ return status;
+}
+
+cairo_status_t
+_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading,
+ const cairo_mesh_pattern_t *pattern)
+{
+ return _cairo_pdf_shading_init (shading, pattern, FALSE);
+}
+
+cairo_status_t
+_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading,
+ const cairo_mesh_pattern_t *pattern)
+{
+ return _cairo_pdf_shading_init (shading, pattern, TRUE);
+}
+
+void
+_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading)
+{
+ free (shading->data);
+ free (shading->decode_array);
+}
+
+#endif /* CAIRO_HAS_PDF_OPERATORS */
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
new file mode 100755
index 000000000..d9f65d800
--- /dev/null
+++ b/src/cairo-pdf-surface-private.h
@@ -0,0 +1,206 @@
+/* -*- 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 shadings;
+ cairo_array_t xobjects;
+ cairo_array_t fonts;
+} cairo_pdf_group_resources_t;
+
+typedef struct _cairo_pdf_source_surface_entry {
+ cairo_hash_entry_t base;
+ unsigned int id;
+ unsigned char *unique_id;
+ unsigned long unique_id_length;
+ cairo_bool_t interpolate;
+ cairo_bool_t stencil_mask;
+ cairo_pdf_resource_t surface_res;
+ int width;
+ int height;
+ cairo_rectangle_int_t extents;
+} cairo_pdf_source_surface_entry_t;
+
+typedef struct _cairo_pdf_source_surface {
+ cairo_pattern_type_t type;
+ cairo_surface_t *surface;
+ cairo_pattern_t *raster_pattern;
+ cairo_pdf_source_surface_entry_t *hash_entry;
+} cairo_pdf_source_surface_t;
+
+typedef struct _cairo_pdf_pattern {
+ double width;
+ double height;
+ cairo_rectangle_int_t extents;
+ cairo_pattern_t *pattern;
+ cairo_pdf_resource_t pattern_res;
+ cairo_pdf_resource_t gstate_res;
+ cairo_bool_t is_shading;
+} cairo_pdf_pattern_t;
+
+typedef enum _cairo_pdf_operation {
+ PDF_PAINT,
+ PDF_MASK,
+ PDF_FILL,
+ PDF_STROKE,
+ PDF_SHOW_GLYPHS
+} cairo_pdf_operation_t;
+
+typedef struct _cairo_pdf_smask_group {
+ double width;
+ double height;
+ cairo_rectangle_int_t extents;
+ cairo_pdf_resource_t group_res;
+ cairo_pdf_operation_t operation;
+ cairo_pattern_t *source;
+ cairo_pdf_resource_t source_res;
+ cairo_pattern_t *mask;
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ cairo_stroke_style_t style;
+ cairo_matrix_t ctm;
+ cairo_matrix_t ctm_inverse;
+ char *utf8;
+ int utf8_len;
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+ cairo_text_cluster_t *clusters;
+ int num_clusters;
+ cairo_bool_t cluster_flags;
+ cairo_scaled_font_t *scaled_font;
+} cairo_pdf_smask_group_t;
+
+typedef struct _cairo_pdf_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_box_double_t bbox;
+ 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/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
new file mode 100755
index 000000000..a0bf1c1d5
--- /dev/null
+++ b/src/cairo-pdf-surface.c
@@ -0,0 +1,7367 @@
+/* -*- 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-pdf-shading-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-image-info-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-type3-glyph-surface-private.h"
+
+#include <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.
+ **/
+
+static cairo_bool_t
+_cairo_pdf_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle);
+
+/**
+ * CAIRO_HAS_PDF_SURFACE:
+ *
+ * Defined if the PDF surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.2
+ **/
+
+static const cairo_pdf_version_t _cairo_pdf_versions[] =
+{
+ CAIRO_PDF_VERSION_1_4,
+ CAIRO_PDF_VERSION_1_5
+};
+
+#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions)
+
+static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] =
+{
+ "PDF 1.4",
+ "PDF 1.5"
+};
+
+static const char *_cairo_pdf_supported_mime_types[] =
+{
+ CAIRO_MIME_TYPE_JPEG,
+ CAIRO_MIME_TYPE_JP2,
+ CAIRO_MIME_TYPE_UNIQUE_ID,
+ NULL
+};
+
+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);
+ if (object)
+ 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;
+ }
+
+ _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
+
+ surface->next_available_resource.id = 1;
+ surface->pages_resource = _cairo_pdf_surface_new_object (surface);
+ if (surface->pages_resource.id == 0) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL2;
+ }
+
+ surface->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 */
+ cairo_status_t status;
+
+ if (! _extract_pdf_surface (surface, &pdf_surface))
+ return;
+
+ _cairo_pdf_surface_set_size_internal (pdf_surface,
+ width_in_points,
+ height_in_points);
+ status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface,
+ width_in_points,
+ height_in_points);
+ if (status)
+ status = _cairo_surface_set_error (surface, status);
+}
+
+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);
+ if (pattern)
+ 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);
+ if (src_surface)
+ 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->shadings, sizeof (cairo_pdf_resource_t));
+ _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t));
+ _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t));
+}
+
+static void
+_cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res)
+{
+ _cairo_array_fini (&res->alphas);
+ _cairo_array_fini (&res->smasks);
+ _cairo_array_fini (&res->patterns);
+ _cairo_array_fini (&res->shadings);
+ _cairo_array_fini (&res->xobjects);
+ _cairo_array_fini (&res->fonts);
+}
+
+static void
+_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res)
+{
+ int i;
+
+ for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
+ res->operators[i] = FALSE;
+
+ _cairo_array_truncate (&res->alphas, 0);
+ _cairo_array_truncate (&res->smasks, 0);
+ _cairo_array_truncate (&res->patterns, 0);
+ _cairo_array_truncate (&res->shadings, 0);
+ _cairo_array_truncate (&res->xobjects, 0);
+ _cairo_array_truncate (&res->fonts, 0);
+}
+
+static void
+_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface,
+ cairo_operator_t op)
+{
+ cairo_pdf_group_resources_t *res = &surface->resources;
+
+ res->operators[op] = TRUE;
+}
+
+static cairo_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_shading (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t shading)
+{
+ return _cairo_array_append (&(surface->resources.shadings), &shading);
+}
+
+
+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, *shading, *xobject;
+ cairo_pdf_font_t *font;
+
+ _cairo_output_stream_printf (surface->output, "<<\n");
+
+ num_alphas = _cairo_array_num_elements (&res->alphas);
+ num_smasks = _cairo_array_num_elements (&res->smasks);
+ if (num_alphas > 0 || num_smasks > 0) {
+ _cairo_output_stream_printf (surface->output,
+ " /ExtGState <<\n");
+
+ 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);
+ if (smask == NULL)
+ return;
+ _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);
+ if (pattern == NULL)
+ return;
+ _cairo_output_stream_printf (surface->output,
+ " /p%d %d 0 R",
+ pattern->id, pattern->id);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " >>\n");
+ }
+
+ num_resources = _cairo_array_num_elements (&res->shadings);
+ if (num_resources > 0) {
+ _cairo_output_stream_printf (surface->output,
+ " /Shading <<");
+ for (i = 0; i < num_resources; i++) {
+ shading = _cairo_array_index (&res->shadings, i);
+ if (shading == NULL)
+ return;
+ _cairo_output_stream_printf (surface->output,
+ " /sh%d %d 0 R",
+ shading->id, shading->id);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " >>\n");
+ }
+
+ num_resources = _cairo_array_num_elements (&res->xobjects);
+ if (num_resources > 0) {
+ _cairo_output_stream_printf (surface->output,
+ " /XObject <<");
+
+ for (i = 0; i < num_resources; i++) {
+ xobject = _cairo_array_index (&res->xobjects, i);
+ if (xobject == NULL)
+ return;
+ _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);
+ if (font == NULL)
+ return;
+
+ _cairo_output_stream_printf (surface->output,
+ " /f-%d-%d %d 0 R\n",
+ font->font_id,
+ font->subset_id,
+ font->subset_resource.id);
+ }
+ _cairo_output_stream_printf (surface->output, " >>\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ ">>\n");
+}
+
+static cairo_pdf_smask_group_t *
+_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_pdf_smask_group_t *group;
+
+ group = calloc (1, sizeof (cairo_pdf_smask_group_t));
+ if (unlikely (group == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ group->group_res = _cairo_pdf_surface_new_object (surface);
+ if (group->group_res.id == 0) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ free (group);
+ return NULL;
+ }
+ group->width = surface->width;
+ group->height = surface->height;
+ if (extents != NULL) {
+ group->extents = *extents;
+ } else {
+ group->extents.x = 0;
+ group->extents.y = 0;
+ group->extents.width = surface->width;
+ group->extents.height = surface->height;
+ }
+
+ return group;
+}
+
+static void
+_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group)
+{
+ if (group->operation == PDF_FILL || group->operation == PDF_STROKE)
+ _cairo_path_fixed_fini (&group->path);
+ if (group->source)
+ cairo_pattern_destroy (group->source);
+ if (group->mask)
+ cairo_pattern_destroy (group->mask);
+ free (group->utf8);
+ free (group->glyphs);
+ free (group->clusters);
+ if (group->scaled_font)
+ cairo_scaled_font_destroy (group->scaled_font);
+ free (group);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface,
+ cairo_pdf_smask_group_t *group)
+{
+ return _cairo_array_append (&surface->smask_groups, &group);
+}
+
+static cairo_bool_t
+_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b)
+{
+ const cairo_pdf_source_surface_entry_t *a = key_a;
+ const cairo_pdf_source_surface_entry_t *b = key_b;
+
+ if (a->interpolate != b->interpolate)
+ return FALSE;
+
+ if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length)
+ return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0);
+
+ return (a->id == b->id);
+}
+
+static void
+_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key)
+{
+ if (key->unique_id && key->unique_id_length > 0) {
+ key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
+ key->unique_id, key->unique_id_length);
+ } else {
+ key->base.hash = key->id;
+ }
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ cairo_image_surface_t **image,
+ void **image_extra)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
+ return _cairo_surface_acquire_source_image (surf_pat->surface, image, image_extra);
+ } break;
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
+ cairo_surface_t *surf;
+ surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
+ if (!surf)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ assert (_cairo_surface_is_image (surf));
+ *image = (cairo_image_surface_t *) surf;
+ } break;
+
+ case CAIRO_PATTERN_TYPE_SOLID:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
+ _cairo_surface_release_source_image (surf_pat->surface, image, image_extra);
+ } break;
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ _cairo_raster_source_pattern_release (pattern, &image->base);
+ break;
+
+ case CAIRO_PATTERN_TYPE_SOLID:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ default:
+
+ ASSERT_NOT_REACHED;
+ break;
+ }
+}
+
+static cairo_int_status_t
+_get_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_int_status_t
+_get_source_surface_size (cairo_surface_t *source,
+ int *width,
+ int *height,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_int_status_t status;
+ 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;
+
+ *extents = sub->extents;
+ *width = extents->width;
+ *height = extents->height;
+ } else {
+ cairo_surface_t *free_me = NULL;
+ cairo_rectangle_int_t surf_extents;
+ cairo_box_t box;
+ cairo_bool_t bounded;
+
+ if (_cairo_surface_is_snapshot (source))
+ free_me = source = _cairo_surface_snapshot_get_target (source);
+
+ status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source,
+ &box, NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (free_me);
+ return status;
+ }
+
+ bounded = _cairo_surface_get_extents (source, &surf_extents);
+ cairo_surface_destroy (free_me);
+
+ *width = surf_extents.width;
+ *height = surf_extents.height;
+
+ _cairo_box_round_to_rectangle (&box, extents);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ extents->x = 0;
+ extents->y = 0;
+
+ status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ *width = info.width;
+ *height = info.height;
+ extents->width = info.width;
+ extents->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;
+ extents->width = info.width;
+ extents->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;
+}
+
+/**
+ * _cairo_pdf_surface_add_source_surface:
+ * @surface: the pdf surface
+ * @source_surface: A #cairo_surface_t to use as the source surface
+ * @source_pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source
+ * @filter: filter type of the source pattern
+ * @stencil_mask: if true, the surface will be written to the PDF as an /ImageMask
+ * @extents: extents of the operation that is using this source
+ * @surface_res: return PDF resource number of the surface
+ * @width: returns width of surface
+ * @height: returns height of surface
+ * @x_offset: x offset of surface
+ * @t_offset: y offset of surface
+ * @source_extents: returns extents of source (either ink extents or extents needed to cover @extents)
+ *
+ * Add surface or raster_source pattern to list of surfaces to be
+ * written to the PDF file when the current page is finished. Returns
+ * a PDF resource to reference the image. A hash table of all images
+ * in the PDF files (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or surface
+ * unique_id) to ensure surfaces with the same id are only written
+ * once to the PDF file.
+ *
+ * Only one of @source_pattern or @source_surface is to be
+ * specified. Set the other to NULL.
+ **/
+static cairo_status_t
+_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
+ cairo_surface_t *source_surface,
+ const cairo_pattern_t *source_pattern,
+ cairo_filter_t filter,
+ cairo_bool_t stencil_mask,
+ const cairo_rectangle_int_t *extents,
+ cairo_pdf_resource_t *surface_res,
+ int *width,
+ int *height,
+ double *x_offset,
+ double *y_offset,
+ cairo_rectangle_int_t *source_extents)
+{
+ cairo_pdf_source_surface_t src_surface;
+ cairo_pdf_source_surface_entry_t surface_key;
+ cairo_pdf_source_surface_entry_t *surface_entry;
+ cairo_status_t status;
+ cairo_bool_t interpolate;
+ unsigned char *unique_id;
+ unsigned long unique_id_length = 0;
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ switch (filter) {
+ default:
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ interpolate = TRUE;
+ break;
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ case CAIRO_FILTER_GAUSSIAN:
+ interpolate = FALSE;
+ break;
+ }
+
+ *x_offset = 0;
+ *y_offset = 0;
+ if (source_pattern) {
+ if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
+ status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern,
+ &image, &image_extra);
+ if (unlikely (status))
+ return status;
+ source_surface = &image->base;
+ cairo_surface_get_device_offset (source_surface, x_offset, y_offset);
+ } else {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
+ source_surface = surface_pattern->surface;
+ }
+ }
+
+ surface_key.id = source_surface->unique_id;
+ surface_key.interpolate = interpolate;
+ cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID,
+ (const unsigned char **) &surface_key.unique_id,
+ &surface_key.unique_id_length);
+ _cairo_pdf_source_surface_init_key (&surface_key);
+ surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base);
+ if (surface_entry) {
+ *surface_res = surface_entry->surface_res;
+ *width = surface_entry->width;
+ *height = surface_entry->height;
+ *source_extents = surface_entry->extents;
+ status = CAIRO_STATUS_SUCCESS;
+ } else {
+ status = _get_source_surface_size (source_surface,
+ width,
+ height,
+ source_extents);
+ if (unlikely(status))
+ goto release_source;
+
+ if (surface_key.unique_id && surface_key.unique_id_length > 0) {
+ unique_id = _cairo_malloc (surface_key.unique_id_length);
+ if (unique_id == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto release_source;
+ }
+
+ unique_id_length = surface_key.unique_id_length;
+ memcpy (unique_id, surface_key.unique_id, unique_id_length);
+ } else {
+ unique_id = NULL;
+ unique_id_length = 0;
+ }
+ }
+
+release_source:
+ if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);
+
+ if (status || surface_entry)
+ return status;
+
+ surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t));
+ if (surface_entry == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ surface_entry->id = surface_key.id;
+ surface_entry->interpolate = interpolate;
+ surface_entry->stencil_mask = stencil_mask;
+ surface_entry->unique_id_length = unique_id_length;
+ surface_entry->unique_id = unique_id;
+ surface_entry->width = *width;
+ surface_entry->height = *height;
+ surface_entry->extents = *source_extents;
+ _cairo_pdf_source_surface_init_key (surface_entry);
+
+ src_surface.hash_entry = surface_entry;
+ if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
+ src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE;
+ src_surface.surface = NULL;
+ status = _cairo_pattern_create_copy (&src_surface.raster_pattern, source_pattern);
+ if (unlikely (status))
+ goto fail2;
+
+ } else {
+ src_surface.type = CAIRO_PATTERN_TYPE_SURFACE;
+ src_surface.surface = cairo_surface_reference (source_surface);
+ src_surface.raster_pattern = NULL;
+ }
+
+ surface_entry->surface_res = _cairo_pdf_surface_new_object (surface);
+ if (surface_entry->surface_res.id == 0) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+
+ status = _cairo_array_append (&surface->page_surfaces, &src_surface);
+ if (unlikely (status))
+ goto fail3;
+
+ status = _cairo_hash_table_insert (surface->all_surfaces,
+ &surface_entry->base);
+ if (unlikely(status))
+ goto fail3;
+
+ *surface_res = surface_entry->surface_res;
+
+ return status;
+
+fail3:
+ if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ cairo_pattern_destroy (src_surface.raster_pattern);
+ else
+ cairo_surface_destroy (src_surface.surface);
+
+fail2:
+ free (surface_entry);
+
+fail1:
+ free (unique_id);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t is_shading,
+ cairo_pdf_resource_t *pattern_res,
+ cairo_pdf_resource_t *gstate_res)
+{
+ cairo_pdf_pattern_t pdf_pattern;
+ cairo_status_t status;
+
+ pdf_pattern.is_shading = is_shading;
+
+ /* 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 ||
+ pattern->type == CAIRO_PATTERN_TYPE_MESH)
+ {
+ double min_alpha;
+
+ _cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
+ if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) {
+ pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface);
+ if (pdf_pattern.gstate_res.id == 0) {
+ cairo_pattern_destroy (pdf_pattern.pattern);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+ }
+
+ pdf_pattern.width = surface->width;
+ pdf_pattern.height = surface->height;
+ if (extents != NULL) {
+ pdf_pattern.extents = *extents;
+ } else {
+ pdf_pattern.extents.x = 0;
+ pdf_pattern.extents.y = 0;
+ pdf_pattern.extents.width = surface->width;
+ pdf_pattern.extents.height = surface->height;
+ }
+
+ *pattern_res = pdf_pattern.pattern_res;
+ *gstate_res = pdf_pattern.gstate_res;
+
+ status = _cairo_array_append (&surface->page_patterns, &pdf_pattern);
+ if (unlikely (status)) {
+ cairo_pattern_destroy (pdf_pattern.pattern);
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Get BBox in PDF coordinates from extents in cairo coordinates */
+static void
+_get_bbox_from_extents (double surface_height,
+ const cairo_rectangle_int_t *extents,
+ cairo_box_double_t *bbox)
+{
+ bbox->p1.x = extents->x;
+ bbox->p1.y = surface_height - (extents->y + extents->height);
+ bbox->p2.x = extents->x + extents->width;
+ bbox->p2.y = surface_height - extents->y;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_pdf_resource_t *shading_res,
+ cairo_pdf_resource_t *gstate_res)
+{
+ return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface,
+ pattern,
+ extents,
+ TRUE,
+ shading_res,
+ gstate_res);
+}
+
+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)
+{
+ return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface,
+ pattern,
+ extents,
+ FALSE,
+ pattern_res,
+ gstate_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,
+ ...)
+{
+ 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,
+ const cairo_box_double_t *bbox)
+{
+ _cairo_pdf_surface_update_object (surface, resource);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /XObject\n"
+ " /Length %d\n",
+ resource.id,
+ _cairo_memory_stream_length (mem_stream));
+
+ if (surface->compress_content) {
+ _cairo_output_stream_printf (surface->output,
+ " /Filter /FlateDecode\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Subtype /Form\n"
+ " /BBox [ %f %f %f %f ]\n"
+ " /Group <<\n"
+ " /Type /Group\n"
+ " /S /Transparency\n"
+ " /I true\n"
+ " /CS /DeviceRGB\n",
+ bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y);
+
+ if (is_knockout_group)
+ _cairo_output_stream_printf (surface->output,
+ " /K true\n");
+
+ _cairo_output_stream_printf (surface->output,
+ " >>\n"
+ " /Resources\n");
+ _cairo_pdf_surface_emit_group_resources (surface, resources);
+ _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,
+ const cairo_box_double_t *bbox,
+ 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;
+ surface->group_stream.bbox = *bbox;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface,
+ const cairo_box_double_t *bbox)
+{
+ cairo_status_t status;
+
+ status = _cairo_pdf_surface_open_group (surface, bbox, NULL);
+ if (unlikely (status))
+ return status;
+
+ surface->group_stream.is_knockout = TRUE;
+
+ return CAIRO_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,
+ &surface->group_stream.bbox);
+ if (group)
+ *group = surface->group_stream.resource;
+
+ status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ 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,
+ const cairo_box_double_t *bbox,
+ 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) {
+ assert (bbox != NULL);
+
+ status =
+ _cairo_pdf_surface_open_stream (surface,
+ resource,
+ surface->compress_content,
+ " /Type /XObject\n"
+ " /Subtype /Form\n"
+ " /BBox [ %f %f %f %f ]\n"
+ " /Group <<\n"
+ " /Type /Group\n"
+ " /S /Transparency\n"
+ " /I true\n"
+ " /CS /DeviceRGB\n"
+ " >>\n"
+ " /Resources %d 0 R\n",
+ bbox->p1.x,
+ bbox->p1.y,
+ bbox->p2.x,
+ bbox->p2.y,
+ surface->content_resources.id);
+ } else {
+ status =
+ _cairo_pdf_surface_open_stream (surface,
+ resource,
+ surface->compress_content,
+ 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->unique_id);
+
+ free (surface_entry);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_finish (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ long offset;
+ cairo_pdf_resource_t 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);
+ if (offset == -1)
+ status = _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+ _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;
+ cairo_box_double_t bbox;
+
+ surface->has_fallback_images = has_fallbacks;
+ bbox.p1.x = 0;
+ bbox.p1.y = 0;
+ bbox.p2.x = surface->width;
+ bbox.p2.y = surface->height;
+ status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface)
+{
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *source,
+ const cairo_rectangle_int_t *extents,
+ cairo_pdf_resource_t *surface_res,
+ int *width,
+ int *height,
+ double *x_offset,
+ double *y_offset)
+{
+ cairo_image_surface_t *image;
+ cairo_surface_t *pad_image;
+ void *image_extra;
+ cairo_int_status_t status;
+ int w, h;
+ cairo_rectangle_int_t extents2;
+ cairo_box_t box;
+ cairo_rectangle_int_t rect;
+ cairo_surface_pattern_t pad_pattern;
+
+ status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source,
+ &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ pad_image = &image->base;
+
+ /* get the operation extents in pattern space */
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_matrix_transform_bounding_box_fixed (&source->matrix, &box, NULL);
+ _cairo_box_round_to_rectangle (&box, &rect);
+
+ /* Check if image needs padding to fill extents */
+ w = image->width;
+ h = image->height;
+ if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
+ _cairo_fixed_integer_ceil(box.p1.y) < 0 ||
+ _cairo_fixed_integer_floor(box.p2.y) > w ||
+ _cairo_fixed_integer_floor(box.p2.y) > h)
+ {
+ pad_image = _cairo_image_surface_create_with_content (image->base.content,
+ rect.width,
+ rect.height);
+ if (pad_image->status) {
+ status = pad_image->status;
+ goto BAIL;
+ }
+
+ _cairo_pattern_init_for_surface (&pad_pattern, &image->base);
+ cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y);
+ pad_pattern.base.extend = CAIRO_EXTEND_PAD;
+ status = _cairo_surface_paint (pad_image,
+ CAIRO_OPERATOR_SOURCE, &pad_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pad_pattern.base);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ status = _cairo_pdf_surface_add_source_surface (surface,
+ pad_image,
+ NULL,
+ source->filter,
+ FALSE,
+ extents,
+ surface_res,
+ width,
+ height,
+ x_offset,
+ y_offset,
+ &extents2);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (pad_image != &image->base) {
+ /* If using a padded image, replace _add_source_surface
+ * x/y_offset with padded image offset. Note:
+ * _add_source_surface only sets a non zero x/y_offset for
+ * RASTER_SOURCE patterns. _add_source_surface will always set
+ * x/y_offset to 0 for surfaces so we can ignore the returned
+ * offset and replace it with the offset required for the
+ * padded image */
+ *x_offset = rect.x;
+ *y_offset = rect.y;
+ }
+
+BAIL:
+ if (pad_image != &image->base)
+ cairo_surface_destroy (pad_image);
+
+ _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra);
+
+ return status;
+}
+
+/* Emit alpha channel from the image into 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.
+ *
+ * When stencil_mask is TRUE, stream_res is an an input specifying the
+ * resource to use. When stencil_mask is FALSE, a new resource will be
+ * created and returned in stream_res.
+ */
+static cairo_status_t
+_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface,
+ cairo_image_surface_t *image,
+ cairo_bool_t stencil_mask,
+ const char *interpolate,
+ cairo_pdf_resource_t *stream_res)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ char *alpha;
+ unsigned long alpha_size;
+ uint32_t *pixel32;
+ uint8_t *pixel8;
+ int i, x, y, bit, a;
+ cairo_image_transparency_t transparency;
+
+ /* This is the only image format we support, which simplifies things. */
+ assert (image->format == CAIRO_FORMAT_ARGB32 ||
+ image->format == CAIRO_FORMAT_A8 ||
+ image->format == CAIRO_FORMAT_A1 );
+
+ transparency = _cairo_image_analyze_transparency (image);
+ if (stencil_mask) {
+ assert (transparency == CAIRO_IMAGE_IS_OPAQUE ||
+ transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA);
+ } else {
+ if (transparency == CAIRO_IMAGE_IS_OPAQUE)
+ return status;
+ }
+
+ if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA) {
+ alpha_size = (image->width + 7) / 8 * image->height;
+ alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height);
+ } else {
+ alpha_size = image->height * image->width;
+ alpha = _cairo_malloc_ab (image->height, image->width);
+ }
+
+ if (unlikely (alpha == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ i = 0;
+ for (y = 0; y < image->height; y++) {
+ if (image->format == CAIRO_FORMAT_A1) {
+ pixel8 = (uint8_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) {
+ a = *pixel8;
+ a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
+ alpha[i++] = a;
+ }
+ } else {
+ pixel8 = (uint8_t *) (image->data + y * image->stride);
+ pixel32 = (uint32_t *) (image->data + y * image->stride);
+ bit = 7;
+ for (x = 0; x < image->width; x++) {
+ if (image->format == CAIRO_FORMAT_ARGB32) {
+ a = (*pixel32 & 0xff000000) >> 24;
+ pixel32++;
+ } else {
+ a = *pixel8;
+ pixel8++;
+ }
+
+ if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
+ alpha[i++] = a;
+ } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
+ if (bit == 7)
+ alpha[i] = 0;
+ if (a != 0)
+ alpha[i] |= (1 << bit);
+ bit--;
+ if (bit < 0) {
+ bit = 7;
+ i++;
+ }
+ }
+ }
+ if (bit != 7)
+ i++;
+ }
+ }
+
+ if (stencil_mask) {
+ status = _cairo_pdf_surface_open_stream (surface,
+ stream_res,
+ TRUE,
+ " /Type /XObject\n"
+ " /Subtype /Image\n"
+ " /ImageMask true\n"
+ " /Width %d\n"
+ " /Height %d\n"
+ " /Interpolate %s\n"
+ " /BitsPerComponent 1\n"
+ " /Decode [1 0]\n",
+ image->width, image->height, interpolate);
+ } else {
+ stream_res->id = 0;
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ TRUE,
+ " /Type /XObject\n"
+ " /Subtype /Image\n"
+ " /Width %d\n"
+ " /Height %d\n"
+ " /ColorSpace /DeviceGray\n"
+ " /Interpolate %s\n"
+ " /BitsPerComponent %d\n",
+ image->width, image->height, interpolate,
+ transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1);
+ }
+ if (unlikely (status))
+ goto CLEANUP_ALPHA;
+
+ if (!stencil_mask)
+ *stream_res = 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_surf,
+ cairo_pdf_resource_t *image_res,
+ cairo_filter_t filter,
+ cairo_bool_t stencil_mask)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ char *data;
+ unsigned long data_size;
+ uint32_t *pixel;
+ int i, x, y, bit;
+ cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */
+ cairo_bool_t need_smask;
+ const char *interpolate = "true";
+ cairo_image_color_t color;
+ cairo_image_surface_t *image;
+
+ image = image_surf;
+ if (image->format != CAIRO_FORMAT_RGB24 &&
+ image->format != CAIRO_FORMAT_ARGB32 &&
+ image->format != CAIRO_FORMAT_A8 &&
+ image->format != CAIRO_FORMAT_A1)
+ {
+ cairo_surface_t *surf;
+ cairo_surface_pattern_t pattern;
+
+ surf = _cairo_image_surface_create_with_content (image_surf->base.content,
+ image_surf->width,
+ image_surf->height);
+ image = (cairo_image_surface_t *) surf;
+ if (surf->status) {
+ status = surf->status;
+ goto CLEANUP;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, &image_surf->base);
+ status = _cairo_surface_paint (surf,
+ CAIRO_OPERATOR_SOURCE, &pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+
+ 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;
+ }
+
+ if (stencil_mask)
+ return _cairo_pdf_surface_emit_smask (surface, image, stencil_mask, interpolate, image_res);
+
+ color = _cairo_image_analyze_color (image);
+ switch (color) {
+ case CAIRO_IMAGE_IS_COLOR:
+ case CAIRO_IMAGE_UNKNOWN_COLOR:
+ data_size = image->height * image->width * 3;
+ data = _cairo_malloc_abc (image->width, image->height, 3);
+ break;
+
+ case CAIRO_IMAGE_IS_GRAYSCALE:
+ data_size = image->height * image->width;
+ data = _cairo_malloc_ab (image->width, image->height);
+ break;
+ case CAIRO_IMAGE_IS_MONOCHROME:
+ data_size = (image->width + 7) / 8 * image->height;
+ data = _cairo_malloc_ab ((image->width+7) / 8, image->height);
+ break;
+ }
+ if (unlikely (data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ i = 0;
+ for (y = 0; y < image->height; y++) {
+ pixel = (uint32_t *) (image->data + y * image->stride);
+
+ bit = 7;
+ for (x = 0; x < image->width; x++, pixel++) {
+ int r, g, b;
+
+ /* XXX: We're un-premultiplying alpha here. My reading of the PDF
+ * specification suggests that we should be able to avoid having
+ * to do this by filling in the SMask's Matte dictionary
+ * appropriately, but my attempts to do that so far have
+ * failed. */
+ if (image->format == CAIRO_FORMAT_ARGB32) {
+ uint8_t a;
+ a = (*pixel & 0xff000000) >> 24;
+ if (a == 0) {
+ r = g = b = 0;
+ } else {
+ r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
+ g = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a;
+ b = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a;
+ }
+ } else if (image->format == CAIRO_FORMAT_RGB24) {
+ r = (*pixel & 0x00ff0000) >> 16;
+ g = (*pixel & 0x0000ff00) >> 8;
+ b = (*pixel & 0x000000ff) >> 0;
+ } else {
+ r = g = b = 0;
+ }
+
+ switch (color) {
+ case CAIRO_IMAGE_IS_COLOR:
+ case CAIRO_IMAGE_UNKNOWN_COLOR:
+ data[i++] = r;
+ data[i++] = g;
+ data[i++] = b;
+ break;
+
+ case CAIRO_IMAGE_IS_GRAYSCALE:
+ data[i++] = r;
+ break;
+
+ case CAIRO_IMAGE_IS_MONOCHROME:
+ if (bit == 7)
+ data[i] = 0;
+ if (r != 0)
+ data[i] |= (1 << bit);
+ bit--;
+ if (bit < 0) {
+ bit = 7;
+ i++;
+ }
+ break;
+ }
+ }
+ if (bit != 7)
+ i++;
+ }
+
+ 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, FALSE, interpolate, &smask);
+ if (unlikely (status))
+ goto CLEANUP_RGB;
+
+ if (smask.id)
+ need_smask = TRUE;
+ }
+
+#define IMAGE_DICTIONARY " /Type /XObject\n" \
+ " /Subtype /Image\n" \
+ " /Width %d\n" \
+ " /Height %d\n" \
+ " /ColorSpace %s\n" \
+ " /Interpolate %s\n" \
+ " /BitsPerComponent %d\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,
+ color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+ interpolate,
+ color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8,
+ smask.id);
+ else
+ status = _cairo_pdf_surface_open_stream (surface,
+ image_res,
+ TRUE,
+ IMAGE_DICTIONARY,
+ image->width, image->height,
+ color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+ interpolate,
+ color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8);
+ if (unlikely (status))
+ goto CLEANUP_RGB;
+
+#undef IMAGE_DICTIONARY
+
+ _cairo_output_stream_write (surface->output, data, data_size);
+ status = _cairo_pdf_surface_close_stream (surface);
+
+CLEANUP_RGB:
+ free (data);
+CLEANUP:
+ if (image != image_surf)
+ cairo_surface_destroy (&image->base);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_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;
+ const char *colorspace;
+
+ 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;
+
+ switch (info.num_components) {
+ case 1:
+ colorspace = "/DeviceGray";
+ break;
+ case 3:
+ colorspace = "/DeviceRGB";
+ break;
+ case 4:
+ colorspace = "/DeviceCMYK";
+ break;
+ default:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ 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,
+ colorspace,
+ 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_pdf_source_surface_t *source)
+{
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_int_status_t status;
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra);
+ } else {
+ status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source->raster_pattern,
+ &image, &image_extra);
+ }
+ if (unlikely (status))
+ return status;
+
+ if (!source->hash_entry->stencil_mask) {
+ status = _cairo_pdf_surface_emit_jpx_image (surface, &image->base, source->hash_entry->surface_res);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto release_source;
+
+ status = _cairo_pdf_surface_emit_jpeg_image (surface, &image->base, source->hash_entry->surface_res);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto release_source;
+ }
+
+ status = _cairo_pdf_surface_emit_image (surface, image,
+ &source->hash_entry->surface_res,
+ source->hash_entry->interpolate,
+ source->hash_entry->stencil_mask);
+
+release_source:
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
+ _cairo_surface_release_source_image (source->surface, image, image_extra);
+ else
+ _cairo_pdf_surface_release_source_image_from_pattern (surface, source->raster_pattern,
+ image, image_extra);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
+ cairo_pdf_source_surface_t *pdf_source)
+{
+ double old_width, old_height;
+ cairo_paginated_mode_t old_paginated_mode;
+ cairo_surface_clipper_t old_clipper;
+ cairo_box_double_t bbox;
+ cairo_int_status_t status;
+ int alpha = 0;
+ cairo_surface_t *free_me = NULL;
+ cairo_surface_t *source;
+ const cairo_rectangle_int_t *extents;
+ int width;
+ int height;
+ cairo_bool_t is_subsurface;
+
+ assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE);
+ extents = &pdf_source->hash_entry->extents;
+ width = pdf_source->hash_entry->width;
+ height = pdf_source->hash_entry->height;
+ is_subsurface = FALSE;
+ source = pdf_source->surface;
+ if (_cairo_surface_is_snapshot (source)) {
+ free_me = source = _cairo_surface_snapshot_get_target (source);
+ } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+
+ source = sub->target;
+ extents = &sub->extents;
+ width = extents->width;
+ height = extents->height;
+ is_subsurface = TRUE;
+ }
+
+ old_width = surface->width;
+ old_height = surface->height;
+ old_paginated_mode = surface->paginated_mode;
+ old_clipper = surface->clipper;
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_pdf_surface_clipper_intersect_clip_path);
+
+ _cairo_pdf_surface_set_size_internal (surface, width, 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);
+ _get_bbox_from_extents (height, extents, &bbox);
+ status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res, TRUE);
+ if (unlikely (status))
+ goto err;
+
+ if (source->content == CAIRO_CONTENT_COLOR) {
+ status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+ if (unlikely (status))
+ goto err;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n",
+ alpha,
+ surface->width,
+ surface->height);
+ }
+
+ status = _cairo_recording_surface_replay_region (source,
+ is_subsurface ? extents : NULL,
+ &surface->base,
+ CAIRO_RECORDING_REGION_NATIVE);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ if (unlikely (status))
+ goto err;
+
+ status = _cairo_pdf_surface_close_content_stream (surface);
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+ surface->clipper = old_clipper;
+ _cairo_pdf_surface_set_size_internal (surface,
+ old_width,
+ old_height);
+ surface->paginated_mode = old_paginated_mode;
+
+err:
+ cairo_surface_destroy (free_me);
+ 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->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+ return _cairo_pdf_surface_emit_recording_surface (surface, src_surface);
+
+ return _cairo_pdf_surface_emit_image_surface (surface, src_surface);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
+ cairo_pdf_pattern_t *pdf_pattern)
+{
+ cairo_pattern_t *pattern = pdf_pattern->pattern;
+ cairo_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);
+ double xstep, ystep;
+ cairo_rectangle_int_t pattern_extents;
+ int pattern_width = 0; /* squelch bogus compiler warning */
+ int pattern_height = 0; /* squelch bogus compiler warning */
+ double x_offset;
+ double y_offset;
+ char draw_surface[200];
+ cairo_box_double_t bbox;
+
+ if (pattern->extend == CAIRO_EXTEND_PAD) {
+ status = _cairo_pdf_surface_add_padded_image_surface (surface,
+ pattern,
+ &pdf_pattern->extents,
+ &pattern_resource,
+ &pattern_width,
+ &pattern_height,
+ &x_offset,
+ &y_offset);
+ pattern_extents.x = 0;
+ pattern_extents.y = 0;
+ pattern_extents.width = pattern_width;
+ pattern_extents.height = pattern_height;
+ } else {
+ status = _cairo_pdf_surface_add_source_surface (surface,
+ NULL,
+ pattern,
+ pattern->filter,
+ FALSE,
+ &pdf_pattern->extents,
+ &pattern_resource,
+ &pattern_width,
+ &pattern_height,
+ &x_offset,
+ &y_offset,
+ &pattern_extents);
+ }
+ if (unlikely (status))
+ return status;
+
+ 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->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:
+ pattern_extents.x = 0;
+ pattern_extents.y = 0;
+ pattern_extents.width = pattern_width*2;
+ pattern_extents.height = pattern_height*2;
+ xstep = pattern_width*2;
+ ystep = pattern_height*2;
+ break;
+ /* All the rest (if any) should have been analyzed away, so this
+ * case should be unreachable. */
+ default:
+ ASSERT_NOT_REACHED;
+ xstep = 0;
+ ystep = 0;
+ }
+
+ /* At this point, (that is, within the surface backend interface),
+ * the pattern's matrix maps from cairo's device space to cairo's
+ * pattern space, (both with their origin at the upper-left, and
+ * cairo's pattern space of size width,height).
+ *
+ * Then, we must emit a PDF pattern object that maps from its own
+ * pattern space, (which has a size that we establish in the BBox
+ * dictionary entry), to the PDF page's *initial* space, (which
+ * does not benefit from the Y-axis flipping matrix that we emit
+ * on each page). So the PDF patterns matrix maps from a
+ * (width,height) pattern space to a device space with the origin
+ * in the lower-left corner.
+ *
+ * So to handle all of that, we start with an identity matrix for
+ * the PDF pattern to device matrix. We translate it up by the
+ * image height then flip it in the Y direction, (moving us from
+ * the PDF origin to cairo's origin). We then multiply in the
+ * inverse of the cairo pattern matrix, (since it maps from device
+ * to pattern, while we're setting up pattern to device). Finally,
+ * we translate back down by the image height and flip again to
+ * end up at the lower-left origin that PDF expects.
+ *
+ * Additionally, within the stream that paints the pattern itself,
+ * we are using a PDF image object that has a size of (1,1) so we
+ * have to scale it up by the image width and height to fill our
+ * pattern cell.
+ */
+ cairo_p2d = pattern->matrix;
+ status = cairo_matrix_invert (&cairo_p2d);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf);
+ cairo_matrix_translate (&pdf_p2d, -x_offset, -y_offset);
+ cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height);
+ cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
+
+ _get_bbox_from_extents (pattern_height, &pattern_extents, &bbox);
+ _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+ status = _cairo_pdf_surface_open_stream (surface,
+ &pdf_pattern->pattern_res,
+ FALSE,
+ " /PatternType 1\n"
+ " /BBox [ %f %f %f %f ]\n"
+ " /XStep %f\n"
+ " /YStep %f\n"
+ " /TilingType 1\n"
+ " /PaintType 1\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Resources << /XObject << /x%d %d 0 R >> >>\n",
+ bbox.p1.x, bbox.p1.y, bbox.p2.x, bbox.p2.y,
+ xstep, ystep,
+ pdf_p2d.xx, pdf_p2d.yx,
+ pdf_p2d.xy, pdf_p2d.yy,
+ pdf_p2d.x0, pdf_p2d.y0,
+ pattern_resource.id,
+ pattern_resource.id);
+ if (unlikely (status))
+ return status;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ ((cairo_surface_pattern_t *) 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 (stops[0].offset == stops[n_stops - 1].offset) {
+ /*
+ * The first and the last stops have the same offset, but we
+ * don't want a function with an empty domain, because that
+ * would provoke underdefined behaviour from rasterisers.
+ * This can only happen with EXTEND_PAD, because EXTEND_NONE
+ * is optimised into a clear pattern in cairo-gstate, and
+ * REFLECT/REPEAT are always transformed to have the first
+ * stop at t=0 and the last stop at t=1. Thus we want a step
+ * function going from the first color to the last one.
+ *
+ * This can be accomplished by stitching three functions:
+ * - a constant first color function,
+ * - a step from the first color to the last color (with empty domain)
+ * - a constant last color function
+ */
+ cairo_pdf_color_stop_t pad_stops[4];
+
+ assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+
+ pad_stops[0] = pad_stops[1] = stops[0];
+ pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
+
+ pad_stops[0].offset = 0;
+ pad_stops[3].offset = 1;
+
+ status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+ 4,
+ pad_stops,
+ FALSE,
+ color_function);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (emit_alpha) {
+ status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+ 4,
+ pad_stops,
+ TRUE,
+ alpha_function);
+ if (unlikely (status))
+ goto BAIL;
+ }
+ } else if (n_stops == 2) {
+ /* no need for stitched function */
+ status = cairo_pdf_surface_emit_rgb_linear_function (surface,
+ &stops[0],
+ &stops[n_stops - 1],
+ color_function);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (emit_alpha) {
+ status = cairo_pdf_surface_emit_alpha_linear_function (surface,
+ &stops[0],
+ &stops[n_stops - 1],
+ alpha_function);
+ if (unlikely (status))
+ goto BAIL;
+ }
+ } else {
+ /* multiple stops: stitch. XXX possible optimization: regularly spaced
+ * stops do not require stitching. XXX */
+ status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+ n_stops,
+ stops,
+ FALSE,
+ color_function);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (emit_alpha) {
+ status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+ n_stops,
+ stops,
+ TRUE,
+ alpha_function);
+ if (unlikely (status))
+ goto BAIL;
+ }
+ }
+
+BAIL:
+ free (allstops);
+ return status;
+}
+
+static cairo_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_pattern_t *pdf_pattern,
+ cairo_pdf_resource_t gstate_resource,
+ cairo_pdf_resource_t gradient_mask)
+{
+ cairo_pdf_resource_t smask_resource;
+ cairo_status_t status;
+ char buf[100];
+ double x1, y1, x2, y2;
+
+ if (pdf_pattern->is_shading) {
+ snprintf(buf, sizeof(buf),
+ " /Shading\n"
+ " << /sh%d %d 0 R >>\n",
+ gradient_mask.id,
+ gradient_mask.id);
+ } else {
+ snprintf(buf, sizeof(buf),
+ " /Pattern\n"
+ " << /p%d %d 0 R >>\n",
+ gradient_mask.id,
+ gradient_mask.id);
+ }
+
+ if (pdf_pattern->is_shading) {
+ cairo_box_t box;
+
+ /* When emitting a shading operator we are in cairo pattern
+ * coordinates. _cairo_pdf_surface_paint_gradient has set the
+ * ctm to the pattern matrix (including the convertion from
+ * pdf to cairo coordinates) */
+ _cairo_box_from_rectangle (&box, &pdf_pattern->extents);
+ _cairo_box_to_doubles (&box, &x1, &y1, &x2, &y2);
+ _cairo_matrix_transform_bounding_box (&pdf_pattern->pattern->matrix, &x1, &y1, &x2, &y2, NULL);
+ } else {
+ cairo_box_double_t box;
+
+ /* When emitting a shading pattern we are in pdf page
+ * coordinates. The color and alpha shading patterns painted
+ * in the XObject below contain the cairo pattern to pdf page
+ * matrix in the /Matrix entry of the pattern. */
+ _get_bbox_from_extents (pdf_pattern->height, &pdf_pattern->extents, &box);
+ x1 = box.p1.x;
+ y1 = box.p1.y;
+ x2 = box.p2.x;
+ y2 = box.p2.y;
+ }
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ surface->compress_content,
+ " /Type /XObject\n"
+ " /Subtype /Form\n"
+ " /FormType 1\n"
+ " /BBox [ %f %f %f %f ]\n"
+ " /Resources\n"
+ " << /ExtGState\n"
+ " << /a0 << /ca 1 /CA 1 >>"
+ " >>\n"
+ "%s"
+ " >>\n"
+ " /Group\n"
+ " << /Type /Group\n"
+ " /S /Transparency\n"
+ " /I true\n"
+ " /CS /DeviceGray\n"
+ " >>\n",
+ x1,y1,x2,y2,
+ buf);
+ if (unlikely (status))
+ return status;
+
+ if (pdf_pattern->is_shading) {
+ _cairo_output_stream_printf (surface->output,
+ "/a0 gs /sh%d sh\n",
+ gradient_mask.id);
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ "q\n"
+ "/a0 gs\n"
+ "/Pattern cs /p%d scn\n"
+ "0 0 %f %f re\n"
+ "f\n"
+ "Q\n",
+ gradient_mask.id,
+ surface->width,
+ surface->height);
+ }
+
+ status = _cairo_pdf_surface_close_stream (surface);
+ if (unlikely (status))
+ return status;
+
+ smask_resource = _cairo_pdf_surface_new_object (surface);
+ if (smask_resource.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Mask\n"
+ " /S /Luminosity\n"
+ " /G %d 0 R\n"
+ ">>\n"
+ "endobj\n",
+ smask_resource.id,
+ surface->pdf_stream.self.id);
+
+ /* Create GState which uses the transparency group as an SMask. */
+ _cairo_pdf_surface_update_object (surface, gstate_resource);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /ExtGState\n"
+ " /SMask %d 0 R\n"
+ " /ca 1\n"
+ " /CA 1\n"
+ " /AIS false\n"
+ ">>\n"
+ "endobj\n",
+ gstate_resource.id,
+ smask_resource.id);
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static void
+_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface,
+ const cairo_pdf_pattern_t *pdf_pattern,
+ cairo_pdf_resource_t pattern_resource,
+ const cairo_matrix_t *pat_to_pdf,
+ const cairo_circle_double_t*start,
+ const cairo_circle_double_t*end,
+ const double *domain,
+ const char *colorspace,
+ cairo_pdf_resource_t color_function)
+{
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n",
+ pattern_resource.id);
+
+ if (!pdf_pattern->is_shading) {
+ _cairo_output_stream_printf (surface->output,
+ "<< /Type /Pattern\n"
+ " /PatternType 2\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Shading\n",
+ pat_to_pdf->xx, pat_to_pdf->yx,
+ pat_to_pdf->xy, pat_to_pdf->yy,
+ pat_to_pdf->x0, pat_to_pdf->y0);
+ }
+
+ if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ _cairo_output_stream_printf (surface->output,
+ " << /ShadingType 2\n"
+ " /ColorSpace %s\n"
+ " /Coords [ %f %f %f %f ]\n",
+ colorspace,
+ start->center.x, start->center.y,
+ end->center.x, end->center.y);
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ " << /ShadingType 3\n"
+ " /ColorSpace %s\n"
+ " /Coords [ %f %f %f %f %f %f ]\n",
+ colorspace,
+ start->center.x, start->center.y,
+ MAX (start->radius, 0),
+ end->center.x, end->center.y,
+ MAX (end->radius, 0));
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Domain [ %f %f ]\n",
+ domain[0], domain[1]);
+
+ if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ false false ]\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Function %d 0 R\n"
+ " >>\n",
+ color_function.id);
+
+ if (!pdf_pattern->is_shading) {
+ _cairo_output_stream_printf (surface->output,
+ ">>\n"
+ "endobj\n");
+ }
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface,
+ cairo_pdf_pattern_t *pdf_pattern)
+{
+ cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern;
+ cairo_pdf_resource_t color_function, alpha_function;
+ cairo_matrix_t pat_to_pdf;
+ cairo_circle_double_t start, end;
+ double domain[2];
+ cairo_status_t status;
+
+ assert (pattern->n_stops != 0);
+
+ status = _cairo_pdf_surface_emit_pattern_stops (surface,
+ pattern,
+ &color_function,
+ &alpha_function);
+ if (unlikely (status))
+ return status;
+
+ pat_to_pdf = pattern->base.matrix;
+ status = cairo_matrix_invert (&pat_to_pdf);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+ cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+
+ if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.extend == CAIRO_EXTEND_REFLECT)
+ {
+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+ double x_scale, y_scale, tolerance;
+
+ /* TODO: use tighter extents */
+ bounds_x1 = 0;
+ bounds_y1 = 0;
+ bounds_x2 = surface->width;
+ bounds_y2 = surface->height;
+ _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+ &bounds_x1, &bounds_y1,
+ &bounds_x2, &bounds_y2,
+ NULL);
+
+ x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution;
+ y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution;
+
+ tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix));
+ tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1);
+ tolerance *= MIN (x_scale, y_scale);
+
+ _cairo_gradient_pattern_box_to_parameter (pattern,
+ bounds_x1, bounds_y1,
+ bounds_x2, bounds_y2,
+ tolerance, domain);
+ } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
+ /*
+ * If the first and the last stop offset are the same, then
+ * the color function is a step function.
+ * _cairo_ps_surface_emit_pattern_stops emits it as a stitched
+ * function no matter how many stops the pattern has. The
+ * domain of the stitched function will be [0 1] in this case.
+ *
+ * This is done to avoid emitting degenerate gradients for
+ * EXTEND_PAD patterns having a step color function.
+ */
+ domain[0] = 0.0;
+ domain[1] = 1.0;
+
+ assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+ } else {
+ domain[0] = pattern->stops[0].offset;
+ domain[1] = pattern->stops[pattern->n_stops - 1].offset;
+ }
+
+ /* PDF requires the first and last stop to be the same as the
+ * extreme coordinates. For repeating patterns this moves the
+ * extreme coordinates out to the begin/end of the repeating
+ * function. For non repeating patterns this may move the extreme
+ * coordinates in if there are not stops at offset 0 and 1. */
+ _cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
+ _cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
+
+ if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.extend == CAIRO_EXTEND_REFLECT)
+ {
+ int repeat_begin, repeat_end;
+
+ repeat_begin = floor (domain[0]);
+ repeat_end = ceil (domain[1]);
+
+ status = _cairo_pdf_surface_emit_repeating_function (surface,
+ pattern,
+ &color_function,
+ repeat_begin,
+ repeat_end);
+ if (unlikely (status))
+ return status;
+
+ if (alpha_function.id != 0) {
+ status = _cairo_pdf_surface_emit_repeating_function (surface,
+ pattern,
+ &alpha_function,
+ repeat_begin,
+ repeat_end);
+ if (unlikely (status))
+ return status;
+ }
+ } else if (pattern->n_stops <= 2) {
+ /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+ * Type 2 function is used by itself without a stitching
+ * function. Type 2 functions always have the domain [0 1] */
+ domain[0] = 0.0;
+ domain[1] = 1.0;
+ }
+
+ _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+ _cairo_pdf_surface_output_gradient (surface, pdf_pattern,
+ pdf_pattern->pattern_res,
+ &pat_to_pdf, &start, &end, domain,
+ "/DeviceRGB", color_function);
+
+ if (alpha_function.id != 0) {
+ cairo_pdf_resource_t mask_resource;
+
+ assert (pdf_pattern->gstate_res.id != 0);
+
+ /* Create pattern for SMask. */
+ mask_resource = _cairo_pdf_surface_new_object (surface);
+ if (mask_resource.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_pdf_surface_output_gradient (surface, pdf_pattern,
+ mask_resource,
+ &pat_to_pdf, &start, &end, domain,
+ "/DeviceGray", alpha_function);
+
+ status = cairo_pdf_surface_emit_transparency_group (surface,
+ pdf_pattern,
+ pdf_pattern->gstate_res,
+ mask_resource);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface,
+ cairo_pdf_pattern_t *pdf_pattern)
+{
+ cairo_matrix_t pat_to_pdf;
+ cairo_status_t status;
+ cairo_pattern_t *pattern = pdf_pattern->pattern;
+ cairo_pdf_shading_t shading;
+ int i;
+ cairo_pdf_resource_t res;
+
+ pat_to_pdf = pattern->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);
+
+ status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern);
+ if (unlikely (status))
+ return status;
+
+ res = _cairo_pdf_surface_new_object (surface);
+ if (unlikely (res.id == 0))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /ShadingType %d\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /BitsPerCoordinate %d\n"
+ " /BitsPerComponent %d\n"
+ " /BitsPerFlag %d\n"
+ " /Decode [",
+ res.id,
+ shading.shading_type,
+ shading.bits_per_coordinate,
+ shading.bits_per_component,
+ shading.bits_per_flag);
+
+ for (i = 0; i < shading.decode_array_length; i++)
+ _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
+
+ _cairo_output_stream_printf (surface->output,
+ "]\n"
+ " /Length %ld\n"
+ ">>\n"
+ "stream\n",
+ shading.data_length);
+
+ _cairo_output_stream_write (surface->output, shading.data, shading.data_length);
+
+ _cairo_output_stream_printf (surface->output,
+ "\nendstream\n"
+ "endobj\n");
+
+ _cairo_pdf_shading_fini (&shading);
+
+ _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Pattern\n"
+ " /PatternType 2\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Shading %d 0 R\n"
+ ">>\n"
+ "endobj\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,
+ res.id);
+
+ if (pdf_pattern->gstate_res.id != 0) {
+ cairo_pdf_resource_t mask_resource;
+
+ /* Create pattern for SMask. */
+ res = _cairo_pdf_surface_new_object (surface);
+ if (unlikely (res.id == 0))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /ShadingType %d\n"
+ " /ColorSpace /DeviceGray\n"
+ " /BitsPerCoordinate %d\n"
+ " /BitsPerComponent %d\n"
+ " /BitsPerFlag %d\n"
+ " /Decode [",
+ res.id,
+ shading.shading_type,
+ shading.bits_per_coordinate,
+ shading.bits_per_component,
+ shading.bits_per_flag);
+
+ for (i = 0; i < shading.decode_array_length; i++)
+ _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
+
+ _cairo_output_stream_printf (surface->output,
+ "]\n"
+ " /Length %ld\n"
+ ">>\n"
+ "stream\n",
+ shading.data_length);
+
+ _cairo_output_stream_write (surface->output, shading.data, shading.data_length);
+
+ _cairo_output_stream_printf (surface->output,
+ "\nendstream\n"
+ "endobj\n");
+ _cairo_pdf_shading_fini (&shading);
+
+ mask_resource = _cairo_pdf_surface_new_object (surface);
+ if (unlikely (mask_resource.id == 0))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Pattern\n"
+ " /PatternType 2\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Shading %d 0 R\n"
+ ">>\n"
+ "endobj\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,
+ res.id);
+
+ status = cairo_pdf_surface_emit_transparency_group (surface,
+ pdf_pattern,
+ pdf_pattern->gstate_res,
+ mask_resource);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_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:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern);
+ break;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern);
+ break;
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ break;
+ }
+
+ _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,
+ const cairo_pattern_t *source,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t stencil_mask)
+{
+ cairo_pdf_resource_t surface_res;
+ int width, height;
+ cairo_matrix_t cairo_p2d, pdf_p2d;
+ cairo_status_t status;
+ int alpha;
+ cairo_rectangle_int_t extents2;
+ double x_offset;
+ double y_offset;
+
+ if (source->extend == CAIRO_EXTEND_PAD &&
+ !(source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ ((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING))
+ {
+ status = _cairo_pdf_surface_add_padded_image_surface (surface,
+ source,
+ extents,
+ &surface_res,
+ &width,
+ &height,
+ &x_offset,
+ &y_offset);
+ } else {
+ status = _cairo_pdf_surface_add_source_surface (surface,
+ NULL,
+ source,
+ source->filter,
+ stencil_mask,
+ extents,
+ &surface_res,
+ &width,
+ &height,
+ &x_offset,
+ &y_offset,
+ &extents2);
+ }
+ if (unlikely (status))
+ return status;
+
+ cairo_p2d = source->matrix;
+ status = cairo_matrix_invert (&cairo_p2d);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ pdf_p2d = surface->cairo_to_pdf;
+ cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d);
+ cairo_matrix_translate (&pdf_p2d, x_offset, y_offset);
+ cairo_matrix_translate (&pdf_p2d, 0.0, height);
+ cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
+ if (!(source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ ((cairo_surface_pattern_t *)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;
+
+ if (stencil_mask) {
+ _cairo_output_stream_printf (surface->output,
+ "/x%d Do\n",
+ surface_res.id);
+ } else {
+ _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_paint_gradient (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *source,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_pdf_resource_t shading_res, gstate_res;
+ cairo_matrix_t pat_to_pdf;
+ cairo_status_t status;
+ int alpha;
+
+ status = _cairo_pdf_surface_add_pdf_shading (surface, source,
+ extents,
+ &shading_res, &gstate_res);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ if (unlikely (status))
+ return status;
+
+ pat_to_pdf = source->matrix;
+ status = cairo_matrix_invert (&pat_to_pdf);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+ cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_matrix_is_identity (&pat_to_pdf)) {
+ _cairo_output_stream_printf (surface->output,
+ "%f %f %f %f %f %f cm\n",
+ pat_to_pdf.xx, pat_to_pdf.yx,
+ pat_to_pdf.xy, pat_to_pdf.yy,
+ pat_to_pdf.x0, pat_to_pdf.y0);
+ }
+
+ status = _cairo_pdf_surface_add_shading (surface, shading_res);
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "/s%d gs /sh%d sh\n",
+ gstate_res.id,
+ shading_res.id);
+ } else {
+ status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "/a%d gs /sh%d sh\n",
+ alpha,
+ shading_res.id);
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *source,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t mask)
+{
+ switch (source->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _cairo_pdf_surface_paint_surface_pattern (surface,
+ source,
+ extents,
+ mask);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _cairo_pdf_surface_paint_gradient (surface,
+ source,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_SOLID:
+ default:
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+ }
+}
+
+static cairo_bool_t
+_can_paint_pattern (const cairo_pattern_t *pattern)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return FALSE;
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return (pattern->extend == CAIRO_EXTEND_NONE ||
+ pattern->extend == CAIRO_EXTEND_PAD);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return TRUE;
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ return FALSE;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
+
+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->current_pattern_is_solid_color = FALSE;
+ }
+ surface->select_pattern_gstate_saved = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_show_page (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ status = _cairo_pdf_surface_close_content_stream (surface);
+ if (unlikely (status))
+ return status;
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ status = _cairo_pdf_surface_write_page (surface);
+ if (unlikely (status))
+ return status;
+
+ _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);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+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
+_utf8_to_pdf_string (const char *utf8, char **str_out)
+{
+ int i;
+ int len;
+ cairo_bool_t ascii;
+ char *str;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ ascii = TRUE;
+ len = strlen (utf8);
+ for (i = 0; i < len; i++) {
+ unsigned c = utf8[i];
+ if (c < 32 || c > 126 || c == '(' || c == ')' || c == '\\') {
+ ascii = FALSE;
+ break;
+ }
+ }
+
+ if (ascii) {
+ str = malloc (len + 3);
+ if (str == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ str[0] = '(';
+ for (i = 0; i < len; i++)
+ str[i+1] = utf8[i];
+ str[i+1] = ')';
+ str[i+2] = 0;
+ } else {
+ uint16_t *utf16 = NULL;
+ int utf16_len = 0;
+
+ status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
+ if (unlikely (status))
+ return status;
+
+ str = malloc (utf16_len*4 + 7);
+ if (str == NULL) {
+ free (utf16);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ strcpy (str, "<FEFF");
+ for (i = 0; i < utf16_len; i++)
+ snprintf (str + 4*i + 5, 5, "%04X", utf16[i]);
+
+ strcat (str, ">");
+ free (utf16);
+ }
+ *str_out = str;
+
+ return status;
+}
+
+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, ">");
+
+ 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_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 (font_subset->is_composite && !font_subset->is_latin) {
+ _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 (font_subset->is_latin)
+ _cairo_output_stream_printf (surface->output, "<%02x> ", font_subset->to_latin_char[i + 1]);
+ else if (font_subset->is_composite)
+ _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1);
+ else
+ _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1);
+
+ status = _cairo_pdf_surface_emit_unicode_for_glyph (surface,
+ font_subset->utf8[i + 1]);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "\n");
+ }
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ "endbfchar\n");
+
+ _cairo_output_stream_printf (surface->output,
+ "endcmap\n"
+ "CMapName currentdict /CMap defineresource pop\n"
+ "end\n"
+ "end\n");
+
+ *stream = surface->pdf_stream.self;
+ return _cairo_pdf_surface_close_stream (surface);
+}
+
+#define PDF_UNITS_PER_EM 1000
+
+static cairo_status_t
+_cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset,
+ cairo_cff_subset_t *subset)
+{
+ cairo_pdf_resource_t stream, descriptor, cidfont_dict;
+ cairo_pdf_resource_t subset_resource, to_unicode_stream;
+ cairo_pdf_font_t font;
+ unsigned int i, last_glyph;
+ cairo_status_t status;
+ char tag[10];
+
+ _create_font_subset_tag (font_subset, subset->ps_name, tag);
+
+ subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+ font_subset->font_id,
+ font_subset->subset_id);
+ if (subset_resource.id == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ TRUE,
+ font_subset->is_latin ?
+ " /Subtype /Type1C\n" :
+ " /Subtype /CIDFontType0C\n");
+ if (unlikely (status))
+ return status;
+
+ stream = surface->pdf_stream.self;
+ _cairo_output_stream_write (surface->output,
+ subset->data, subset->data_length);
+ status = _cairo_pdf_surface_close_stream (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
+ font_subset,
+ &to_unicode_stream);
+ if (_cairo_status_is_error (status))
+ return status;
+
+ descriptor = _cairo_pdf_surface_new_object (surface);
+ if (descriptor.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /FontDescriptor\n"
+ " /FontName /%s+%s\n",
+ descriptor.id,
+ tag,
+ subset->ps_name);
+
+ if (subset->family_name_utf8) {
+ char *pdf_str;
+
+ status = _utf8_to_pdf_string (subset->family_name_utf8, &pdf_str);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ " /FontFamily %s\n",
+ pdf_str);
+ free (pdf_str);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Flags 4\n"
+ " /FontBBox [ %ld %ld %ld %ld ]\n"
+ " /ItalicAngle 0\n"
+ " /Ascent %ld\n"
+ " /Descent %ld\n"
+ " /CapHeight %ld\n"
+ " /StemV 80\n"
+ " /StemH 80\n"
+ " /FontFile3 %u 0 R\n"
+ ">>\n"
+ "endobj\n",
+ (long)(subset->x_min*PDF_UNITS_PER_EM),
+ (long)(subset->y_min*PDF_UNITS_PER_EM),
+ (long)(subset->x_max*PDF_UNITS_PER_EM),
+ (long)(subset->y_max*PDF_UNITS_PER_EM),
+ (long)(subset->ascent*PDF_UNITS_PER_EM),
+ (long)(subset->descent*PDF_UNITS_PER_EM),
+ (long)(subset->y_max*PDF_UNITS_PER_EM),
+ stream.id);
+
+ if (font_subset->is_latin) {
+ /* find last glyph used */
+ for (i = 255; i >= 32; i--)
+ if (font_subset->latin_to_subset_glyph_index[i] > 0)
+ break;
+
+ last_glyph = i;
+ _cairo_pdf_surface_update_object (surface, subset_resource);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /Type1\n"
+ " /BaseFont /%s+%s\n"
+ " /FirstChar 32\n"
+ " /LastChar %d\n"
+ " /FontDescriptor %d 0 R\n"
+ " /Encoding /WinAnsiEncoding\n"
+ " /Widths [",
+ subset_resource.id,
+ tag,
+ subset->ps_name,
+ last_glyph,
+ descriptor.id);
+
+ for (i = 32; i < last_glyph + 1; i++) {
+ int glyph = font_subset->latin_to_subset_glyph_index[i];
+ if (glyph > 0) {
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset->widths[glyph]*PDF_UNITS_PER_EM));
+ } else {
+ _cairo_output_stream_printf (surface->output, " 0");
+ }
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " ]\n");
+
+ if (to_unicode_stream.id != 0)
+ _cairo_output_stream_printf (surface->output,
+ " /ToUnicode %d 0 R\n",
+ to_unicode_stream.id);
+
+ _cairo_output_stream_printf (surface->output,
+ ">>\n"
+ "endobj\n");
+ } else {
+ cidfont_dict = _cairo_pdf_surface_new_object (surface);
+ if (cidfont_dict.id == 0)
+ 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];
+
+ /* CFF fallback subsetting does not work with 8-bit glyphs unless
+ * they are a latin subset */
+ if (!font_subset->is_composite && !font_subset->is_latin)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ snprintf (name, sizeof name, "CairoFont-%d-%d",
+ font_subset->font_id, font_subset->subset_id);
+ status = _cairo_cff_fallback_init (&subset, name, font_subset);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset);
+
+ _cairo_cff_fallback_fini (&subset);
+
+ return status;
+}
+
+static cairo_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, last_glyph;
+ char tag[10];
+
+ _create_font_subset_tag (font_subset, subset->base_font, tag);
+
+ subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+ font_subset->font_id,
+ font_subset->subset_id);
+ if (subset_resource.id == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ length = subset->header_length + subset->data_length + subset->trailer_length;
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ TRUE,
+ " /Length1 %lu\n"
+ " /Length2 %lu\n"
+ " /Length3 %lu\n",
+ subset->header_length,
+ subset->data_length,
+ subset->trailer_length);
+ if (unlikely (status))
+ return status;
+
+ stream = surface->pdf_stream.self;
+ _cairo_output_stream_write (surface->output, subset->data, length);
+ status = _cairo_pdf_surface_close_stream (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
+ font_subset,
+ &to_unicode_stream);
+ if (_cairo_status_is_error (status))
+ return status;
+
+ last_glyph = font_subset->num_glyphs - 1;
+ if (font_subset->is_latin) {
+ /* find last glyph used */
+ for (i = 255; i >= 32; i--)
+ if (font_subset->latin_to_subset_glyph_index[i] > 0)
+ break;
+
+ last_glyph = i;
+ }
+
+ descriptor = _cairo_pdf_surface_new_object (surface);
+ if (descriptor.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /FontDescriptor\n"
+ " /FontName /%s+%s\n"
+ " /Flags 4\n"
+ " /FontBBox [ %ld %ld %ld %ld ]\n"
+ " /ItalicAngle 0\n"
+ " /Ascent %ld\n"
+ " /Descent %ld\n"
+ " /CapHeight %ld\n"
+ " /StemV 80\n"
+ " /StemH 80\n"
+ " /FontFile %u 0 R\n"
+ ">>\n"
+ "endobj\n",
+ descriptor.id,
+ tag,
+ subset->base_font,
+ (long)(subset->x_min*PDF_UNITS_PER_EM),
+ (long)(subset->y_min*PDF_UNITS_PER_EM),
+ (long)(subset->x_max*PDF_UNITS_PER_EM),
+ (long)(subset->y_max*PDF_UNITS_PER_EM),
+ (long)(subset->ascent*PDF_UNITS_PER_EM),
+ (long)(subset->descent*PDF_UNITS_PER_EM),
+ (long)(subset->y_max*PDF_UNITS_PER_EM),
+ stream.id);
+
+ _cairo_pdf_surface_update_object (surface, subset_resource);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /Type1\n"
+ " /BaseFont /%s+%s\n"
+ " /FirstChar %d\n"
+ " /LastChar %d\n"
+ " /FontDescriptor %d 0 R\n",
+ subset_resource.id,
+ tag,
+ subset->base_font,
+ font_subset->is_latin ? 32 : 0,
+ last_glyph,
+ descriptor.id);
+
+ if (font_subset->is_latin)
+ _cairo_output_stream_printf (surface->output, " /Encoding /WinAnsiEncoding\n");
+
+ _cairo_output_stream_printf (surface->output, " /Widths [");
+ if (font_subset->is_latin) {
+ for (i = 32; i < last_glyph + 1; i++) {
+ int glyph = font_subset->latin_to_subset_glyph_index[i];
+ if (glyph > 0) {
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset->widths[glyph]*PDF_UNITS_PER_EM));
+ } else {
+ _cairo_output_stream_printf (surface->output, " 0");
+ }
+ }
+ } else {
+ for (i = 0; i < font_subset->num_glyphs; i++)
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset->widths[i]*PDF_UNITS_PER_EM));
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " ]\n");
+
+ 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_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];
+
+ /* 16-bit glyphs not compatible with Type 1 fonts */
+ if (font_subset->is_composite && !font_subset->is_latin)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ snprintf (name, sizeof name, "CairoFont-%d-%d",
+ font_subset->font_id, font_subset->subset_id);
+ status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
+
+ _cairo_type1_subset_fini (&subset);
+ return status;
+}
+
+static cairo_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];
+
+ /* 16-bit glyphs not compatible with Type 1 fonts */
+ if (font_subset->is_composite && !font_subset->is_latin)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ snprintf (name, sizeof name, "CairoFont-%d-%d",
+ font_subset->font_id, font_subset->subset_id);
+ status = _cairo_type1_fallback_init_binary (&subset, name, font_subset);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
+
+ _cairo_type1_fallback_fini (&subset);
+ return status;
+}
+
+static cairo_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, last_glyph;
+ char tag[10];
+
+ subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+ font_subset->font_id,
+ font_subset->subset_id);
+ if (subset_resource.id == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_truetype_subset_init_pdf (&subset, font_subset);
+ if (unlikely (status))
+ return status;
+
+ _create_font_subset_tag (font_subset, subset.ps_name, tag);
+
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ TRUE,
+ " /Length1 %lu\n",
+ subset.data_length);
+ if (unlikely (status)) {
+ _cairo_truetype_subset_fini (&subset);
+ return status;
+ }
+
+ stream = surface->pdf_stream.self;
+ _cairo_output_stream_write (surface->output,
+ subset.data, subset.data_length);
+ status = _cairo_pdf_surface_close_stream (surface);
+ if (unlikely (status)) {
+ _cairo_truetype_subset_fini (&subset);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
+ font_subset,
+ &to_unicode_stream);
+ if (_cairo_status_is_error (status)) {
+ _cairo_truetype_subset_fini (&subset);
+ return status;
+ }
+
+ descriptor = _cairo_pdf_surface_new_object (surface);
+ if (descriptor.id == 0) {
+ _cairo_truetype_subset_fini (&subset);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /FontDescriptor\n"
+ " /FontName /%s+%s\n",
+ descriptor.id,
+ tag,
+ subset.ps_name);
+
+ if (subset.family_name_utf8) {
+ char *pdf_str;
+
+ status = _utf8_to_pdf_string (subset.family_name_utf8, &pdf_str);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ " /FontFamily %s\n",
+ pdf_str);
+ free (pdf_str);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Flags %d\n"
+ " /FontBBox [ %ld %ld %ld %ld ]\n"
+ " /ItalicAngle 0\n"
+ " /Ascent %ld\n"
+ " /Descent %ld\n"
+ " /CapHeight %ld\n"
+ " /StemV 80\n"
+ " /StemH 80\n"
+ " /FontFile2 %u 0 R\n"
+ ">>\n"
+ "endobj\n",
+ font_subset->is_latin ? 32 : 4,
+ (long)(subset.x_min*PDF_UNITS_PER_EM),
+ (long)(subset.y_min*PDF_UNITS_PER_EM),
+ (long)(subset.x_max*PDF_UNITS_PER_EM),
+ (long)(subset.y_max*PDF_UNITS_PER_EM),
+ (long)(subset.ascent*PDF_UNITS_PER_EM),
+ (long)(subset.descent*PDF_UNITS_PER_EM),
+ (long)(subset.y_max*PDF_UNITS_PER_EM),
+ stream.id);
+
+ if (font_subset->is_latin) {
+ /* find last glyph used */
+ for (i = 255; i >= 32; i--)
+ if (font_subset->latin_to_subset_glyph_index[i] > 0)
+ break;
+
+ last_glyph = i;
+ _cairo_pdf_surface_update_object (surface, subset_resource);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /TrueType\n"
+ " /BaseFont /%s+%s\n"
+ " /FirstChar 32\n"
+ " /LastChar %d\n"
+ " /FontDescriptor %d 0 R\n"
+ " /Encoding /WinAnsiEncoding\n"
+ " /Widths [",
+ subset_resource.id,
+ tag,
+ subset.ps_name,
+ last_glyph,
+ descriptor.id);
+
+ for (i = 32; i < last_glyph + 1; i++) {
+ int glyph = font_subset->latin_to_subset_glyph_index[i];
+ if (glyph > 0) {
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset.widths[glyph]*PDF_UNITS_PER_EM));
+ } else {
+ _cairo_output_stream_printf (surface->output, " 0");
+ }
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " ]\n");
+
+ if (to_unicode_stream.id != 0)
+ _cairo_output_stream_printf (surface->output,
+ " /ToUnicode %d 0 R\n",
+ to_unicode_stream.id);
+
+ _cairo_output_stream_printf (surface->output,
+ ">>\n"
+ "endobj\n");
+ } else {
+ cidfont_dict = _cairo_pdf_surface_new_object (surface);
+ if (cidfont_dict.id == 0) {
+ _cairo_truetype_subset_fini (&subset);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /CIDFontType2\n"
+ " /BaseFont /%s+%s\n"
+ " /CIDSystemInfo\n"
+ " << /Registry (Adobe)\n"
+ " /Ordering (Identity)\n"
+ " /Supplement 0\n"
+ " >>\n"
+ " /FontDescriptor %d 0 R\n"
+ " /W [0 [",
+ cidfont_dict.id,
+ tag,
+ subset.ps_name,
+ descriptor.id);
+
+ for (i = 0; i < font_subset->num_glyphs; i++)
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset.widths[i]*PDF_UNITS_PER_EM));
+
+ _cairo_output_stream_printf (surface->output,
+ " ]]\n"
+ ">>\n"
+ "endobj\n");
+
+ _cairo_pdf_surface_update_object (surface, subset_resource);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /Type0\n"
+ " /BaseFont /%s+%s\n"
+ " /Encoding /Identity-H\n"
+ " /DescendantFonts [ %d 0 R]\n",
+ subset_resource.id,
+ tag,
+ subset.ps_name,
+ cidfont_dict.id);
+
+ if (to_unicode_stream.id != 0)
+ _cairo_output_stream_printf (surface->output,
+ " /ToUnicode %d 0 R\n",
+ to_unicode_stream.id);
+
+ _cairo_output_stream_printf (surface->output,
+ ">>\n"
+ "endobj\n");
+ }
+
+ 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_int_status_t
+_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_pdf_surface_t *surface = closure;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_status_t status2;
+ 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);
+ status = type3_surface->status;
+ cairo_surface_destroy (type3_surface);
+ return 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_int_status_t
+_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_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);
+ status = type3_surface->status;
+ cairo_surface_destroy (type3_surface);
+ return 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,
+ &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_int_status_t
+_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_pdf_surface_t *surface = closure;
+ cairo_int_status_t status;
+
+ status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_pdf_surface_t *surface = closure;
+ cairo_int_status_t status;
+
+ status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_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);
+ if (object == NULL)
+ return -1;
+ 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;
+ cairo_box_double_t bbox;
+
+ /* Create mask group */
+ _get_bbox_from_extents (group->height, &group->extents, &bbox);
+ status = _cairo_pdf_surface_open_group (surface, &bbox, NULL);
+ if (unlikely (status))
+ return status;
+
+ if (_can_paint_pattern (group->mask)) {
+ _cairo_output_stream_printf (surface->output, "q\n");
+ status = _cairo_pdf_surface_paint_pattern (surface,
+ group->mask,
+ &group->extents,
+ FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output, "Q\n");
+ } else {
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
+ if (unlikely (smask_group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ smask_group->width = group->width;
+ smask_group->height = group->height;
+ smask_group->operation = PDF_PAINT;
+ smask_group->source = cairo_pattern_reference (group->mask);
+ smask_group->source_res = pattern_res;
+ status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (smask_group);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ gstate_res.id,
+ smask_group->group_res.id);
+ } else {
+ status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "%f %f %f %f re f\n",
+ bbox.p1.x,
+ bbox.p1.y,
+ bbox.p2.x - bbox.p1.x,
+ bbox.p2.y - bbox.p1.y);
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ status = _cairo_pdf_surface_close_group (surface, &mask_group);
+ if (unlikely (status))
+ return status;
+
+ /* Create source group */
+ status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res);
+ if (unlikely (status))
+ return status;
+
+ if (_can_paint_pattern (group->source)) {
+ _cairo_output_stream_printf (surface->output, "q\n");
+ status = _cairo_pdf_surface_paint_pattern (surface,
+ group->source,
+ &group->extents,
+ FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output, "Q\n");
+ } else {
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
+ if (unlikely (smask_group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ smask_group->operation = PDF_PAINT;
+ smask_group->source = cairo_pattern_reference (group->source);
+ smask_group->source_res = pattern_res;
+ status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (smask_group);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ gstate_res.id,
+ smask_group->group_res.id);
+ } else {
+ status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "%f %f %f %f re f\n",
+ bbox.p1.x,
+ bbox.p1.y,
+ bbox.p2.x - bbox.p1.x,
+ bbox.p2.y - bbox.p1.y);
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ status = _cairo_pdf_surface_close_group (surface, NULL);
+ if (unlikely (status))
+ return status;
+
+ /* Create an smask based on the alpha component of mask_group */
+ smask = _cairo_pdf_surface_new_object (surface);
+ if (smask.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Mask\n"
+ " /S /Alpha\n"
+ " /G %d 0 R\n"
+ ">>\n"
+ "endobj\n",
+ smask.id,
+ mask_group.id);
+
+ /* Create a GState that uses the smask */
+ _cairo_pdf_surface_update_object (surface, group->group_res);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /ExtGState\n"
+ " /SMask %d 0 R\n"
+ " /ca 1\n"
+ " /CA 1\n"
+ " /AIS false\n"
+ ">>\n"
+ "endobj\n",
+ group->group_res.id,
+ smask.id);
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface,
+ cairo_pdf_smask_group_t *group)
+{
+ double old_width, old_height;
+ cairo_status_t status;
+ cairo_box_double_t bbox;
+
+ 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;
+ }
+
+ _get_bbox_from_extents (group->height, &group->extents, &bbox);
+ status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_select_pattern (surface,
+ group->source,
+ group->source_res,
+ group->operation == PDF_STROKE);
+ if (unlikely (status))
+ return status;
+
+ switch (group->operation) {
+ case PDF_PAINT:
+ _cairo_output_stream_printf (surface->output,
+ "0 0 %f %f re f\n",
+ surface->width, surface->height);
+ break;
+ case PDF_MASK:
+ ASSERT_NOT_REACHED;
+ break;
+ case PDF_FILL:
+ status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+ &group->path,
+ group->fill_rule);
+ break;
+ case PDF_STROKE:
+ status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
+ &group->path,
+ &group->style,
+ &group->ctm,
+ &group->ctm_inverse);
+ break;
+ case PDF_SHOW_GLYPHS:
+ status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+ group->utf8, group->utf8_len,
+ group->glyphs, group->num_glyphs,
+ group->clusters, group->num_clusters,
+ group->cluster_flags,
+ group->scaled_font);
+ break;
+ }
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_close_group (surface, NULL);
+
+RESTORE_SIZE:
+ _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;
+ unsigned 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;
+ unsigned int i, len;
+
+ _cairo_pdf_group_resources_clear (&surface->resources);
+ if (surface->has_fallback_images) {
+ cairo_rectangle_int_t extents;
+ cairo_box_double_t bbox;
+
+ extents.x = 0;
+ extents.y = 0;
+ extents.width = ceil (surface->width);
+ extents.height = ceil (surface->height);
+ _get_bbox_from_extents (surface->height, &extents, &bbox);
+ status = _cairo_pdf_surface_open_knockout_group (surface, &bbox);
+ if (unlikely (status))
+ return status;
+
+ len = _cairo_array_num_elements (&surface->knockout_group);
+ for (i = 0; i < len; i++) {
+ _cairo_array_copy_element (&surface->knockout_group, i, &res);
+ _cairo_output_stream_printf (surface->output,
+ "/x%d Do\n",
+ res.id);
+ status = _cairo_pdf_surface_add_xobject (surface, res);
+ if (unlikely (status))
+ return status;
+ }
+ _cairo_output_stream_printf (surface->output,
+ "/x%d Do\n",
+ surface->content.id);
+ status = _cairo_pdf_surface_add_xobject (surface, surface->content);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_close_group (surface, &knockout);
+ if (unlikely (status))
+ return status;
+
+ _cairo_pdf_group_resources_clear (&surface->resources);
+ status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE);
+ 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"
+ " /I true\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...
+ if (pattern->surface->content == CAIRO_CONTENT_ALPHA)
+ return FALSE;
+*/
+
+ extend = cairo_pattern_get_extend (&pattern->base);
+ switch (extend) {
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_REPEAT:
+ case CAIRO_EXTEND_REFLECT:
+ /* There's no point returning FALSE for EXTEND_PAD, as the image
+ * surface does not currently implement it either */
+ case CAIRO_EXTEND_PAD:
+ return TRUE;
+ }
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+static cairo_bool_t
+_pattern_supported (const cairo_pattern_t *pattern)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return TRUE;
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
+
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+_pdf_operator_supported (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return TRUE;
+
+ default:
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+ return FALSE;
+ }
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ if (surface->force_fallbacks &&
+ surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (! _pattern_supported (pattern))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (_pdf_operator_supported (op)) {
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+ if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (pattern->extend == CAIRO_EXTEND_PAD) {
+ cairo_box_t box;
+ cairo_rectangle_int_t rect;
+ cairo_rectangle_int_t rec_extents;
+
+ /* get the operation extents in pattern space */
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
+ _cairo_box_round_to_rectangle (&box, &rect);
+
+ /* Check if surface needs padding to fill extents */
+ if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) {
+ if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x ||
+ _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y ||
+ _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width ||
+ _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+ return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+
+ /* The SOURCE operator is supported if the pattern is opaque or if
+ * there is nothing painted underneath. */
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+ if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (_cairo_pattern_is_opaque (pattern, extents)) {
+ return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+ } else {
+ /* FIXME: The analysis surface does not yet have
+ * the capability to analyze a non opaque recording
+ * surface and mark it supported if there is
+ * nothing underneath. For now recording surfaces of
+ * type CONTENT_COLOR_ALPHA painted with
+ * OPERATOR_SOURCE will result in a fallback
+ * image. */
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ } else {
+ return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface,
+ surface_pattern);
+ }
+ }
+
+ if (_cairo_pattern_is_opaque (pattern, extents))
+ return CAIRO_STATUS_SUCCESS;
+ else
+ return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_bool_t
+_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface)
+{
+ cairo_box_double_t bbox;
+ cairo_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);
+ bbox.p1.x = 0;
+ bbox.p1.y = 0;
+ bbox.p2.x = surface->width;
+ bbox.p2.y = surface->height;
+ return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE);
+}
+
+/* A PDF stencil mask is an A1 mask used with the current color */
+static cairo_int_status_t
+_cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_image_transparency_t transparency;
+ cairo_pdf_resource_t pattern_res = {0};
+
+ if (! (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+ (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ ((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask,
+ &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ if (image->base.status)
+ return image->base.status;
+
+ transparency = _cairo_image_analyze_transparency (image);
+ if (transparency != CAIRO_IMAGE_IS_OPAQUE &&
+ transparency != CAIRO_IMAGE_HAS_BILEVEL_ALPHA)
+ {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_select_pattern (surface, source,
+ pattern_res, FALSE);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output, "q\n");
+ status = _cairo_pdf_surface_paint_surface_pattern (surface, mask, NULL, TRUE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output, "Q\n");
+
+ status = _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+ _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_set_clip (cairo_pdf_surface_t *surface,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_clip_t *clip = composite->clip;
+
+ if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
+ clip = NULL;
+
+ if (clip == NULL) {
+ if (_cairo_composite_rectangles_can_reduce_clip (composite,
+ surface->clipper.clip))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ &surface->base,
+ op, source, clip);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ goto cleanup;
+ } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ status = _cairo_pdf_surface_start_fallback (surface);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_pdf_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup;
+
+ if (_can_paint_pattern (source)) {
+ _cairo_output_stream_printf (surface->output, "q\n");
+ status = _cairo_pdf_surface_paint_pattern (surface,
+ source,
+ &extents.bounded,
+ FALSE);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output, "Q\n");
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_output_stream_get_status (surface->output);
+ }
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+ if (unlikely (group == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+
+ group->operation = PDF_PAINT;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+ group->source_res = pattern_res;
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ gstate_res.id,
+ group->group_res.id);
+ } else {
+ status = _cairo_pdf_surface_select_pattern (surface, source,
+ pattern_res, FALSE);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output,
+ "0 0 %f %f re f\n",
+ surface->width, surface->height);
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_smask_group_t *group;
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+ cairo_rectangle_int_t r;
+ cairo_box_t box;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ &surface->base,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ cairo_status_t source_status, mask_status;
+
+ status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ if (_cairo_int_status_is_error (status))
+ goto cleanup;
+ source_status = status;
+
+ if (mask->has_component_alpha) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded);
+ if (_cairo_int_status_is_error (status))
+ goto cleanup;
+ }
+ mask_status = status;
+
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_analysis_surface_merge_status (source_status,
+ mask_status);
+ } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ status = _cairo_pdf_surface_start_fallback (surface);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+ assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded));
+
+ /* get the accurate extents */
+ status = _cairo_pattern_get_ink_extents (source, &r);
+ if (unlikely (status))
+ goto cleanup;
+
+ /* XXX slight impedance mismatch */
+ _cairo_box_from_rectangle (&box, &r);
+ status = _cairo_composite_rectangles_intersect_source_extents (&extents,
+ &box);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pattern_get_ink_extents (mask, &r);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_box_from_rectangle (&box, &r);
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+ &box);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ goto cleanup;
+
+ /* Check if we can use a stencil mask */
+ status = _cairo_pdf_surface_emit_stencil_mask (surface, source, mask, &extents.bounded);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto cleanup;
+
+ group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+ if (unlikely (group == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+
+ group->operation = PDF_MASK;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+ status = _cairo_pattern_create_copy (&group->mask, mask);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+ group->source_res = _cairo_pdf_surface_new_object (surface);
+ if (group->source_res.id == 0) {
+ _cairo_pdf_smask_group_destroy (group);
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, group->group_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->source_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ group->group_res.id,
+ group->source_res.id);
+
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &surface->base,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t mask;
+ cairo_box_t box;
+
+ status = _cairo_path_fixed_stroke_extents (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &mask);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_box_from_rectangle (&box, &mask);
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+ &box);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ goto cleanup;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_pdf_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup;
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ goto cleanup;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+ if (unlikely (group == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+
+ group->operation = PDF_STROKE;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+ group->source_res = pattern_res;
+ status = _cairo_path_fixed_init_copy (&group->path, path);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+
+ group->style = *style;
+ group->ctm = *ctm;
+ group->ctm_inverse = *ctm_inverse;
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ gstate_res.id,
+ group->group_res.id);
+ } else {
+ status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
+ path,
+ style,
+ ctm,
+ ctm_inverse);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t*path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &surface->base,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t mask;
+ cairo_box_t box;
+
+ _cairo_path_fixed_fill_extents (path,
+ fill_rule,
+ tolerance,
+ &mask);
+
+ _cairo_box_from_rectangle (&box, &mask);
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+ &box);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ goto cleanup;
+ } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ status = _cairo_pdf_surface_start_fallback (surface);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_pdf_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ goto cleanup;
+
+ if (_can_paint_pattern (source)) {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output, "q\n");
+ status = _cairo_pdf_operators_clip (&surface->pdf_operators,
+ path,
+ fill_rule);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_paint_pattern (surface,
+ source,
+ &extents.bounded,
+ FALSE);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output, "Q\n");
+ status = _cairo_output_stream_get_status (surface->output);
+ goto cleanup;
+ }
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+ if (unlikely (group == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+
+ group->operation = PDF_FILL;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+ group->source_res = pattern_res;
+ status = _cairo_path_fixed_init_copy (&group->path, path);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+
+ group->fill_rule = fill_rule;
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ gstate_res.id,
+ group->group_res.id);
+ } else {
+ status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+ path,
+ fill_rule);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_fill_stroke (void *abstract_surface,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ const cairo_path_fixed_t*path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+
+ /* During analysis we return unsupported and let the _fill and
+ * _stroke functions that are on the fallback path do the analysis
+ * for us. During render we may still encounter unsupported
+ * combinations of fill/stroke patterns. However we can return
+ * unsupported anytime to let the _fill and _stroke functions take
+ * over.
+ */
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* PDF rendering of fill-stroke is not the same as cairo when
+ * either the fill or stroke is not opaque.
+ */
+ if ( !_cairo_pattern_is_opaque (fill_source, NULL) ||
+ !_cairo_pattern_is_opaque (stroke_source, NULL))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (fill_op != stroke_op)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Compute the operation extents using the stroke which will naturally
+ * be larger than the fill extents.
+ */
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &surface->base,
+ stroke_op, stroke_source,
+ path, stroke_style, stroke_ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t mask;
+ cairo_box_t box;
+
+ status = _cairo_path_fixed_stroke_extents (path, stroke_style,
+ stroke_ctm, stroke_ctm_inverse,
+ stroke_tolerance,
+ &mask);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_box_from_rectangle (&box, &mask);
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+ &box);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_select_operator (surface, fill_op);
+ if (unlikely (status))
+ goto cleanup;
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t mask;
+ cairo_box_t box;
+
+ _cairo_path_fixed_fill_extents (path,
+ fill_rule,
+ fill_tolerance,
+ &mask);
+
+ _cairo_box_from_rectangle (&box, &mask);
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+ &box);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ fill_pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source,
+ &extents.bounded,
+ &fill_pattern_res,
+ &gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ assert (gstate_res.id == 0);
+
+ stroke_pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface,
+ stroke_source,
+ &extents.bounded,
+ &stroke_pattern_res,
+ &gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ assert (gstate_res.id == 0);
+
+ /* As PDF has separate graphics state for fill and stroke we can
+ * select both at the same time */
+ status = _cairo_pdf_surface_select_pattern (surface, fill_source,
+ fill_pattern_res, FALSE);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_select_pattern (surface, stroke_source,
+ stroke_pattern_res, TRUE);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators,
+ path,
+ fill_rule,
+ stroke_style,
+ stroke_ctm,
+ stroke_ctm_inverse);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_bool_t
+_cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+ cairo_bool_t overlap;
+ cairo_int_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ &surface->base,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ goto cleanup;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_pdf_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup;
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ goto cleanup;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+ if (unlikely (group == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+
+ group->operation = PDF_SHOW_GLYPHS;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+ group->source_res = pattern_res;
+
+ if (utf8_len) {
+ group->utf8 = malloc (utf8_len);
+ if (unlikely (group->utf8 == NULL)) {
+ _cairo_pdf_smask_group_destroy (group);
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+ memcpy (group->utf8, utf8, utf8_len);
+ }
+ group->utf8_len = utf8_len;
+
+ if (num_glyphs) {
+ group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (group->glyphs == NULL)) {
+ _cairo_pdf_smask_group_destroy (group);
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+ memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+ }
+ group->num_glyphs = num_glyphs;
+
+ if (num_clusters) {
+ group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
+ if (unlikely (group->clusters == NULL)) {
+ _cairo_pdf_smask_group_destroy (group);
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto cleanup;
+ }
+ memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters);
+ }
+ group->num_clusters = num_clusters;
+
+ group->scaled_font = cairo_scaled_font_reference (scaled_font);
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ gstate_res.id,
+ group->group_res.id);
+ } else {
+ status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
+ if (unlikely (status))
+ goto cleanup;
+
+ /* Each call to show_glyphs() with a transclucent pattern must
+ * be in a separate text object otherwise overlapping text
+ * from separate calls to show_glyphs will not composite with
+ * each other. */
+ if (! _cairo_pattern_is_opaque (source, &extents.bounded)) {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+ return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static const char **
+_cairo_pdf_surface_get_supported_mime_types (void *abstract_surface)
+{
+ return _cairo_pdf_supported_mime_types;
+}
+
+static 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,
+ _cairo_pdf_surface_finish,
+
+ _cairo_default_context_create,
+
+ NULL, /* create similar: handled by wrapper */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* _cairo_pdf_surface_copy_page */
+ _cairo_pdf_surface_show_page,
+
+ _cairo_pdf_surface_get_extents,
+ _cairo_pdf_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ /* Here are the drawing functions */
+ _cairo_pdf_surface_paint,
+ _cairo_pdf_surface_mask,
+ _cairo_pdf_surface_stroke,
+ _cairo_pdf_surface_fill,
+ _cairo_pdf_surface_fill_stroke,
+ NULL, /* show_glyphs */
+ _cairo_pdf_surface_has_show_text_glyphs,
+ _cairo_pdf_surface_show_text_glyphs,
+ _cairo_pdf_surface_get_supported_mime_types,
+};
+
+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/src/cairo-pdf.h b/src/cairo-pdf.h
new file mode 100755
index 000000000..1bc8524f2
--- /dev/null
+++ b/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. (Since 1.10)
+ * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. (Since 1.10)
+ *
+ * #cairo_pdf_version_t is used to describe the version number of the PDF
+ * specification that a generated PDF file will conform to.
+ *
+ * Since: 1.10
+ **/
+typedef enum _cairo_pdf_version {
+ CAIRO_PDF_VERSION_1_4,
+ CAIRO_PDF_VERSION_1_5
+} cairo_pdf_version_t;
+
+cairo_public cairo_surface_t *
+cairo_pdf_surface_create (const char *filename,
+ double width_in_points,
+ double height_in_points);
+
+cairo_public cairo_surface_t *
+cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func,
+ void *closure,
+ double width_in_points,
+ double height_in_points);
+
+cairo_public void
+cairo_pdf_surface_restrict_to_version (cairo_surface_t *surface,
+ cairo_pdf_version_t version);
+
+cairo_public void
+cairo_pdf_get_versions (cairo_pdf_version_t const **versions,
+ int *num_versions);
+
+cairo_public const char *
+cairo_pdf_version_to_string (cairo_pdf_version_t version);
+
+cairo_public void
+cairo_pdf_surface_set_size (cairo_surface_t *surface,
+ double width_in_points,
+ double height_in_points);
+
+CAIRO_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/src/cairo-pen.c b/src/cairo-pen.c
new file mode 100755
index 000000000..61be0e829
--- /dev/null
+++ b/src/cairo-pen.c
@@ -0,0 +1,475 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-slope-private.h"
+
+static void
+_cairo_pen_compute_slopes (cairo_pen_t *pen);
+
+cairo_status_t
+_cairo_pen_init (cairo_pen_t *pen,
+ double radius,
+ double tolerance,
+ const cairo_matrix_t *ctm)
+{
+ int i;
+ int reflect;
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t)));
+
+ pen->radius = radius;
+ pen->tolerance = tolerance;
+
+ reflect = _cairo_matrix_compute_determinant (ctm) < 0.;
+
+ pen->num_vertices = _cairo_pen_vertices_needed (tolerance,
+ radius,
+ ctm);
+
+ if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) {
+ pen->vertices = _cairo_malloc_ab (pen->num_vertices,
+ sizeof (cairo_pen_vertex_t));
+ if (unlikely (pen->vertices == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else {
+ pen->vertices = pen->vertices_embedded;
+ }
+
+ /*
+ * Compute pen coordinates. To generate the right ellipse, compute points around
+ * a circle in user space and transform them to device space. To get a consistent
+ * orientation in device space, flip the pen if the transformation matrix
+ * is reflecting
+ */
+ for (i=0; i < pen->num_vertices; i++) {
+ cairo_pen_vertex_t *v = &pen->vertices[i];
+ double theta = 2 * M_PI * i / (double) pen->num_vertices, dx, dy;
+ if (reflect)
+ theta = -theta;
+ dx = radius * cos (theta);
+ dy = radius * sin (theta);
+ cairo_matrix_transform_distance (ctm, &dx, &dy);
+ v->point.x = _cairo_fixed_from_double (dx);
+ v->point.y = _cairo_fixed_from_double (dy);
+ }
+
+ _cairo_pen_compute_slopes (pen);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pen_fini (cairo_pen_t *pen)
+{
+ if (pen->vertices != pen->vertices_embedded)
+ free (pen->vertices);
+
+
+ VG (VALGRIND_MAKE_MEM_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.
+*/
+
+int
+_cairo_pen_vertices_needed (double tolerance,
+ double radius,
+ const cairo_matrix_t *matrix)
+{
+ /*
+ * the pen is a circle that gets transformed to an ellipse by matrix.
+ * compute major axis length for a pen with the specified radius.
+ * we don't need the minor axis length.
+ */
+ double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix,
+ radius);
+ int num_vertices;
+
+ if (tolerance >= 4*major_axis) { /* XXX relaxed from 2*major for inkscape */
+ num_vertices = 1;
+ } else if (tolerance >= major_axis) {
+ num_vertices = 4;
+ } else {
+ num_vertices = ceil (2*M_PI / acos (1 - tolerance / major_axis));
+
+ /* number of vertices must be even */
+ if (num_vertices % 2)
+ num_vertices++;
+
+ /* And we must always have at least 4 vertices. */
+ if (num_vertices < 4)
+ num_vertices = 4;
+ }
+
+ return num_vertices;
+}
+
+static void
+_cairo_pen_compute_slopes (cairo_pen_t *pen)
+{
+ int i, i_prev;
+ cairo_pen_vertex_t *prev, *v, *next;
+
+ for (i=0, i_prev = pen->num_vertices - 1;
+ i < pen->num_vertices;
+ i_prev = i++) {
+ prev = &pen->vertices[i_prev];
+ v = &pen->vertices[i];
+ next = &pen->vertices[(i + 1) % pen->num_vertices];
+
+ _cairo_slope_init (&v->slope_cw, &prev->point, &v->point);
+ _cairo_slope_init (&v->slope_ccw, &v->point, &next->point);
+ }
+}
+/*
+ * Find active pen vertex for clockwise edge of stroke at the given slope.
+ *
+ * The strictness of the inequalities here is delicate. The issue is
+ * that the slope_ccw member of one pen vertex will be equivalent to
+ * the slope_cw member of the next pen vertex in a counterclockwise
+ * order. However, for this function, we care strongly about which
+ * vertex is returned.
+ *
+ * [I think the "care strongly" above has to do with ensuring that the
+ * pen's "extra points" from the spline's initial and final slopes are
+ * properly found when beginning the spline stroking.]
+ */
+int
+_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen,
+ const cairo_slope_t *slope)
+{
+ int i;
+
+ for (i=0; i < pen->num_vertices; i++) {
+ if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) &&
+ (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0))
+ break;
+ }
+
+ /* If the desired slope cannot be found between any of the pen
+ * vertices, then we must have a degenerate pen, (such as a pen
+ * that's been transformed to a line). In that case, we consider
+ * the first pen vertex as the appropriate clockwise vertex.
+ */
+ if (i == pen->num_vertices)
+ i = 0;
+
+ return i;
+}
+
+/* Find active pen vertex for counterclockwise edge of stroke at the given slope.
+ *
+ * Note: See the comments for _cairo_pen_find_active_cw_vertex_index
+ * for some details about the strictness of the inequalities here.
+ */
+int
+_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
+ const cairo_slope_t *slope)
+{
+ cairo_slope_t slope_reverse;
+ int i;
+
+ slope_reverse = *slope;
+ slope_reverse.dx = -slope_reverse.dx;
+ slope_reverse.dy = -slope_reverse.dy;
+
+ for (i=pen->num_vertices-1; i >= 0; i--) {
+ if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) &&
+ (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0))
+ break;
+ }
+
+ /* If the desired slope cannot be found between any of the pen
+ * vertices, then we must have a degenerate pen, (such as a pen
+ * that's been transformed to a line). In that case, we consider
+ * the last pen vertex as the appropriate counterclockwise vertex.
+ */
+ if (i < 0)
+ i = pen->num_vertices - 1;
+
+ return i;
+}
+
+void
+_cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen,
+ const cairo_slope_t *in,
+ const cairo_slope_t *out,
+ int *start, int *stop)
+{
+
+ int lo = 0, hi = pen->num_vertices;
+ int i;
+
+ i = (lo + hi) >> 1;
+ do {
+ if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0)
+ lo = i;
+ else
+ hi = i;
+ i = (lo + hi) >> 1;
+ } while (hi - lo > 1);
+ if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0)
+ if (++i == pen->num_vertices)
+ i = 0;
+ *start = i;
+
+ if (_cairo_slope_compare (out, &pen->vertices[i].slope_ccw) >= 0) {
+ lo = i;
+ hi = i + pen->num_vertices;
+ i = (lo + hi) >> 1;
+ do {
+ int j = i;
+ if (j >= pen->num_vertices)
+ j -= pen->num_vertices;
+ if (_cairo_slope_compare (&pen->vertices[j].slope_cw, out) > 0)
+ hi = i;
+ else
+ lo = i;
+ i = (lo + hi) >> 1;
+ } while (hi - lo > 1);
+ if (i >= pen->num_vertices)
+ i -= pen->num_vertices;
+ }
+ *stop = i;
+}
+
+void
+_cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen,
+ const cairo_slope_t *in,
+ const cairo_slope_t *out,
+ int *start, int *stop)
+{
+ int lo = 0, hi = pen->num_vertices;
+ int i;
+
+ i = (lo + hi) >> 1;
+ do {
+ if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0)
+ lo = i;
+ else
+ hi = i;
+ i = (lo + hi) >> 1;
+ } while (hi - lo > 1);
+ if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0)
+ if (++i == pen->num_vertices)
+ i = 0;
+ *start = i;
+
+ if (_cairo_slope_compare (&pen->vertices[i].slope_cw, out) <= 0) {
+ lo = i;
+ hi = i + pen->num_vertices;
+ i = (lo + hi) >> 1;
+ do {
+ int j = i;
+ if (j >= pen->num_vertices)
+ j -= pen->num_vertices;
+ if (_cairo_slope_compare (out, &pen->vertices[j].slope_ccw) > 0)
+ hi = i;
+ else
+ lo = i;
+ i = (lo + hi) >> 1;
+ } while (hi - lo > 1);
+ if (i >= pen->num_vertices)
+ i -= pen->num_vertices;
+ }
+ *stop = i;
+}
diff --git a/src/cairo-png.c b/src/cairo-png.c
new file mode 100755
index 000000000..e74a4a8bc
--- /dev/null
+++ b/src/cairo-png.c
@@ -0,0 +1,819 @@
+/* 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-image-surface-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.
+ *
+ * It is a toy API. It only offers very simple support for reading and
+ * writing PNG files, which is sufficient for testing and
+ * demonstration purposes. Applications which need more control over
+ * the generated PNG file should access the pixel data directly, using
+ * cairo_image_surface_get_data() or a backend-specific access
+ * function, and process it with another library, e.g. gdk-pixbuf or
+ * libpng.
+ **/
+
+/**
+ * CAIRO_HAS_PNG_FUNCTIONS:
+ *
+ * Defined if the PNG functions are available.
+ * This macro can be used to conditionally compile code using the cairo
+ * PNG functions.
+ *
+ * Since: 1.0
+ **/
+
+struct png_read_closure_t {
+ cairo_read_func_t read_func;
+ void *closure;
+ cairo_output_stream_t *png_data;
+};
+
+
+/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
+static void
+unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
+{
+ unsigned int i;
+
+ for (i = 0; i < row_info->rowbytes; i += 4) {
+ uint8_t *b = &data[i];
+ uint32_t pixel;
+ uint8_t alpha;
+
+ memcpy (&pixel, b, sizeof (uint32_t));
+ alpha = (pixel & 0xff000000) >> 24;
+ if (alpha == 0) {
+ b[0] = b[1] = b[2] = b[3] = 0;
+ } else {
+ b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+ b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
+ b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
+ b[3] = alpha;
+ }
+ }
+}
+
+/* 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_int_status_t status;
+ cairo_image_surface_t *image;
+ cairo_image_surface_t * volatile clone;
+ void *image_extra;
+ png_struct *png;
+ png_info *info;
+ png_byte **volatile rows = NULL;
+ png_color_16 white;
+ int png_color_type;
+ int bpc;
+
+ 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:
+ bpc = 8;
+ if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE)
+ png_color_type = PNG_COLOR_TYPE_RGB;
+ else
+ png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ case CAIRO_FORMAT_RGB30:
+ bpc = 10;
+ png_color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ bpc = 8;
+ png_color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case CAIRO_FORMAT_A8:
+ bpc = 8;
+ png_color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ case CAIRO_FORMAT_A1:
+ bpc = 1;
+ png_color_type = PNG_COLOR_TYPE_GRAY;
+#ifndef WORDS_BIGENDIAN
+ png_set_packswap (png);
+#endif
+ break;
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_RGB16_565:
+ default:
+ status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ goto BAIL4;
+ }
+
+ png_set_IHDR (png, info,
+ clone->width,
+ clone->height, bpc,
+ png_color_type,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ white.gray = (1 << bpc) - 1;
+ white.red = white.blue = white.green = white.gray;
+ png_set_bKGD (png, info, &white);
+
+ if (0) { /* XXX extract meta-data from surface (i.e. creation date) */
+ png_time pt;
+
+ png_convert_from_time_t (&pt, time (NULL));
+ png_set_tIME (png, info, &pt);
+ }
+
+ /* We have to call png_write_info() before setting up the write
+ * transformation, since it stores data internally in 'png'
+ * that is needed for the write transformation functions to work.
+ */
+ png_write_info (png, info);
+
+ if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ 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.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_surface_write_to_png (cairo_surface_t *surface,
+ const char *filename)
+{
+ FILE *fp;
+ cairo_status_t status;
+
+ if (surface->status)
+ return surface->status;
+
+ if (surface->finished)
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ 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.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_surface_write_to_png_stream (cairo_surface_t *surface,
+ cairo_write_func_t write_func,
+ void *closure)
+{
+ struct png_write_closure_t png_closure;
+
+ if (surface->status)
+ return surface->status;
+
+ if (surface->finished)
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ png_closure.write_func = write_func;
+ png_closure.closure = closure;
+
+ return write_png (surface, stream_write_func, &png_closure);
+}
+slim_hidden_def (cairo_surface_write_to_png_stream);
+
+static inline int
+multiply_alpha (int alpha, int color)
+{
+ int temp = (alpha * color) + 0x80;
+ return ((temp + (temp >> 8)) >> 8);
+}
+
+/* Premultiplies data and converts RGBA bytes => native endian */
+static void
+premultiply_data (png_structp png,
+ png_row_infop row_info,
+ png_bytep data)
+{
+ unsigned int i;
+
+ for (i = 0; i < row_info->rowbytes; i += 4) {
+ uint8_t *base = &data[i];
+ uint8_t alpha = base[3];
+ uint32_t p;
+
+ if (alpha == 0) {
+ p = 0;
+ } else {
+ uint8_t red = base[0];
+ uint8_t green = base[1];
+ uint8_t blue = base[2];
+
+ if (alpha != 0xff) {
+ red = multiply_alpha (alpha, red);
+ green = multiply_alpha (alpha, green);
+ blue = multiply_alpha (alpha, blue);
+ }
+ p = (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:
+ free (row_pointers);
+ free (data);
+ if (png != NULL)
+ png_destroy_read_struct (&png, &info, NULL);
+ if (png_closure->png_data != NULL) {
+ cairo_status_t status_ignored;
+
+ status_ignored = _cairo_output_stream_destroy (png_closure->png_data);
+ }
+
+ return surface;
+}
+
+/**
+ * cairo_image_surface_create_from_png:
+ * @filename: name of PNG file to load
+ *
+ * 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().
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_image_surface_create_from_png (const char *filename)
+{
+ struct png_read_closure_t png_closure;
+ cairo_surface_t *surface;
+
+ 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().
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
+ void *closure)
+{
+ struct png_read_closure_t png_closure;
+
+ png_closure.read_func = read_func;
+ png_closure.closure = closure;
+
+ return read_png (&png_closure);
+}
diff --git a/src/cairo-polygon-intersect.c b/src/cairo-polygon-intersect.c
new file mode 100755
index 000000000..2cd73d2e5
--- /dev/null
+++ b/src/cairo-polygon-intersect.c
@@ -0,0 +1,1532 @@
+/*
+ * 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-inline.h"
+
+typedef cairo_point_t cairo_bo_point32_t;
+
+typedef struct _cairo_bo_intersect_ordinate {
+ int32_t ordinate;
+ enum { EXACT, INEXACT } exactness;
+} cairo_bo_intersect_ordinate_t;
+
+typedef struct _cairo_bo_intersect_point {
+ cairo_bo_intersect_ordinate_t x;
+ cairo_bo_intersect_ordinate_t y;
+} cairo_bo_intersect_point_t;
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+
+typedef struct _cairo_bo_deferred {
+ cairo_bo_edge_t *other;
+ int32_t top;
+} cairo_bo_deferred_t;
+
+struct _cairo_bo_edge {
+ int a_or_b;
+ cairo_edge_t edge;
+ cairo_bo_edge_t *prev;
+ cairo_bo_edge_t *next;
+ cairo_bo_deferred_t deferred;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef enum {
+ CAIRO_BO_EVENT_TYPE_STOP,
+ CAIRO_BO_EVENT_TYPE_INTERSECTION,
+ CAIRO_BO_EVENT_TYPE_START
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_start_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+ cairo_bo_edge_t edge;
+} cairo_bo_start_event_t;
+
+typedef struct _cairo_bo_queue_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+ cairo_bo_edge_t *e1;
+ cairo_bo_edge_t *e2;
+} cairo_bo_queue_event_t;
+
+typedef struct _pqueue {
+ int size, max_size;
+
+ cairo_bo_event_t **elements;
+ cairo_bo_event_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _cairo_bo_event_queue {
+ cairo_freepool_t pool;
+ pqueue_t pqueue;
+ cairo_bo_event_t **start_events;
+} cairo_bo_event_queue_t;
+
+typedef struct _cairo_bo_sweep_line {
+ cairo_bo_edge_t *head;
+ int32_t current_y;
+ cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+ cairo_fixed_t y)
+{
+ cairo_fixed_t x, dy;
+
+ if (y == line->p1.y)
+ return line->p1.x;
+ if (y == line->p2.y)
+ return line->p2.x;
+
+ x = line->p1.x;
+ dy = line->p2.y - line->p1.y;
+ if (dy != 0) {
+ x += _cairo_fixed_mul_div_floor (y - line->p1.y,
+ line->p2.x - line->p1.x,
+ dy);
+ }
+
+ return x;
+}
+
+static inline int
+_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
+ cairo_bo_point32_t const *b)
+{
+ int cmp;
+
+ cmp = a->y - b->y;
+ if (cmp)
+ return cmp;
+
+ return a->x - b->x;
+}
+
+/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
+ * slope a is respectively greater than, equal to, or less than the
+ * slope of b.
+ *
+ * For each edge, consider the direction vector formed from:
+ *
+ * top -> bottom
+ *
+ * which is:
+ *
+ * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
+ *
+ * We then define the slope of each edge as dx/dy, (which is the
+ * inverse of the slope typically used in math instruction). We never
+ * compute a slope directly as the value approaches infinity, but we
+ * can derive a slope comparison without division as follows, (where
+ * the ? represents our compare operator).
+ *
+ * 1. slope(a) ? slope(b)
+ * 2. adx/ady ? bdx/bdy
+ * 3. (adx * bdy) ? (bdx * ady)
+ *
+ * Note that from step 2 to step 3 there is no change needed in the
+ * sign of the result since both ady and bdy are guaranteed to be
+ * greater than or equal to 0.
+ *
+ * When using this slope comparison to sort edges, some care is needed
+ * when interpreting the results. Since the slope compare operates on
+ * distance vectors from top to bottom it gives a correct left to
+ * right sort for edges that have a common top point, (such as two
+ * edges with start events at the same location). On the other hand,
+ * the sense of the result will be exactly reversed for two edges that
+ * have a common stop point.
+ */
+static inline int
+_slope_compare (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b)
+{
+ /* XXX: We're assuming here that dx and dy will still fit in 32
+ * bits. That's not true in general as there could be overflow. We
+ * should prevent that before the tessellation algorithm
+ * begins.
+ */
+ int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
+ int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+
+ /* Since the dy's are all positive by construction we can fast
+ * path several common cases.
+ */
+
+ /* First check for vertical lines. */
+ if (adx == 0)
+ return -bdx;
+ if (bdx == 0)
+ return adx;
+
+ /* Then where the two edges point in different directions wrt x. */
+ if ((adx ^ bdx) < 0)
+ return adx;
+
+ /* Finally we actually need to do the general comparison. */
+ {
+ int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
+ int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+ cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+ cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+ return _cairo_int64_cmp (adx_bdy, bdx_ady);
+ }
+}
+
+/*
+ * We need to compare the x-coordinates of a pair of lines for a particular y,
+ * without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ * X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy,
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy
+ * - (Y - A_y) * A_dx * B_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 128 bit arithmetic. For certain, but common,
+ * input we can reduce this down to a single 32 bit compare by inspecting the
+ * deltas.
+ *
+ * (And put the burden of the work on developing fast 128 bit ops, which are
+ * required throughout the tessellator.)
+ *
+ * See the similar discussion for _slope_compare().
+ */
+static int
+edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b,
+ int32_t y)
+{
+ /* XXX: We're assuming here that dx and dy will still fit in 32
+ * bits. That's not true in general as there could be overflow. We
+ * should prevent that before the tessellation algorithm
+ * begins.
+ */
+ int32_t dx;
+ int32_t adx, ady;
+ int32_t bdx, bdy;
+ enum {
+ HAVE_NONE = 0x0,
+ HAVE_DX = 0x1,
+ HAVE_ADX = 0x2,
+ HAVE_DX_ADX = HAVE_DX | HAVE_ADX,
+ HAVE_BDX = 0x4,
+ HAVE_DX_BDX = HAVE_DX | HAVE_BDX,
+ HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
+ HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX
+ } have_dx_adx_bdx = HAVE_ALL;
+
+ /* don't bother solving for abscissa if the edges' bounding boxes
+ * can be used to order them. */
+ {
+ int32_t amin, amax;
+ int32_t bmin, bmax;
+ if (a->edge.line.p1.x < a->edge.line.p2.x) {
+ amin = a->edge.line.p1.x;
+ amax = a->edge.line.p2.x;
+ } else {
+ amin = a->edge.line.p2.x;
+ amax = a->edge.line.p1.x;
+ }
+ if (b->edge.line.p1.x < b->edge.line.p2.x) {
+ bmin = b->edge.line.p1.x;
+ bmax = b->edge.line.p2.x;
+ } else {
+ bmin = b->edge.line.p2.x;
+ bmax = b->edge.line.p1.x;
+ }
+ if (amax < bmin) return -1;
+ if (amin > bmax) return +1;
+ }
+
+ ady = a->edge.line.p2.y - a->edge.line.p1.y;
+ adx = a->edge.line.p2.x - a->edge.line.p1.x;
+ if (adx == 0)
+ have_dx_adx_bdx &= ~HAVE_ADX;
+
+ bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+ bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+ if (bdx == 0)
+ have_dx_adx_bdx &= ~HAVE_BDX;
+
+ dx = a->edge.line.p1.x - b->edge.line.p1.x;
+ if (dx == 0)
+ have_dx_adx_bdx &= ~HAVE_DX;
+
+#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
+#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y)
+#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y)
+ switch (have_dx_adx_bdx) {
+ default:
+ case HAVE_NONE:
+ return 0;
+ case HAVE_DX:
+ /* A_dy * B_dy * (A_x - B_x) ∘ 0 */
+ return dx; /* ady * bdy is positive definite */
+ case HAVE_ADX:
+ /* 0 ∘ - (Y - A_y) * A_dx * B_dy */
+ return adx; /* bdy * (y - a->top.y) is positive definite */
+ case HAVE_BDX:
+ /* 0 ∘ (Y - B_y) * B_dx * A_dy */
+ return -bdx; /* ady * (y - b->top.y) is positive definite */
+ case HAVE_ADX_BDX:
+ /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
+ if ((adx ^ bdx) < 0) {
+ return adx;
+ } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */
+ cairo_int64_t adx_bdy, bdx_ady;
+
+ /* ∴ A_dx * B_dy ∘ B_dx * A_dy */
+
+ adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+ bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+ return _cairo_int64_cmp (adx_bdy, bdx_ady);
+ } else
+ return _cairo_int128_cmp (A, B);
+ case HAVE_DX_ADX:
+ /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */
+ if ((-adx ^ dx) < 0) {
+ return dx;
+ } else {
+ cairo_int64_t ady_dx, dy_adx;
+
+ ady_dx = _cairo_int32x32_64_mul (ady, dx);
+ dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
+
+ return _cairo_int64_cmp (ady_dx, dy_adx);
+ }
+ case HAVE_DX_BDX:
+ /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */
+ if ((bdx ^ dx) < 0) {
+ return dx;
+ } else {
+ cairo_int64_t bdy_dx, dy_bdx;
+
+ bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
+ dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx);
+
+ return _cairo_int64_cmp (bdy_dx, dy_bdx);
+ }
+ case HAVE_ALL:
+ /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */
+ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
+ }
+#undef B
+#undef A
+#undef L
+}
+
+/*
+ * We need to compare the x-coordinate of a line for a particular y wrt to a
+ * given x, without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ * X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ * A_x + (Y - A_y) * A_dx / A_dy ∘ X
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy (and (Y - A_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 64 bit arithmetic.
+ *
+ * See the similar discussion for _slope_compare() and
+ * edges_compare_x_for_y_general().
+ */
+static int
+edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
+ int32_t y,
+ int32_t x)
+{
+ int32_t adx, ady;
+ int32_t dx, dy;
+ cairo_int64_t L, R;
+
+ if (x < a->edge.line.p1.x && x < a->edge.line.p2.x)
+ return 1;
+ if (x > a->edge.line.p1.x && x > a->edge.line.p2.x)
+ return -1;
+
+ adx = a->edge.line.p2.x - a->edge.line.p1.x;
+ dx = x - a->edge.line.p1.x;
+
+ if (adx == 0)
+ return -dx;
+ if (dx == 0 || (adx ^ dx) < 0)
+ return adx;
+
+ dy = y - a->edge.line.p1.y;
+ ady = a->edge.line.p2.y - a->edge.line.p1.y;
+
+ L = _cairo_int32x32_64_mul (dy, adx);
+ R = _cairo_int32x32_64_mul (dx, ady);
+
+ return _cairo_int64_cmp (L, R);
+}
+
+static int
+edges_compare_x_for_y (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b,
+ int32_t y)
+{
+ /* If the sweep-line is currently on an end-point of a line,
+ * then we know its precise x value (and considering that we often need to
+ * compare events at end-points, this happens frequently enough to warrant
+ * special casing).
+ */
+ enum {
+ HAVE_NEITHER = 0x0,
+ HAVE_AX = 0x1,
+ HAVE_BX = 0x2,
+ HAVE_BOTH = HAVE_AX | HAVE_BX
+ } have_ax_bx = HAVE_BOTH;
+ int32_t ax, 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
+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
+event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue,
+ cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right)
+{
+ cairo_bo_point32_t intersection;
+
+ if (_line_equal (&left->edge.line, &right->edge.line))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* The names "left" and "right" here are correct descriptions of
+ * the order of the two edges within the active edge list. So if a
+ * slope comparison also puts left less than right, then we know
+ * that the intersection of these two segments has already
+ * occurred before the current sweep line position. */
+ if (_slope_compare (left, right) <= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! _cairo_bo_edge_intersect (left, right, &intersection))
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_bo_event_queue_insert (event_queue,
+ CAIRO_BO_EVENT_TYPE_INTERSECTION,
+ left, right,
+ &intersection);
+}
+
+static void
+_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
+{
+ sweep_line->head = NULL;
+ sweep_line->current_y = INT32_MIN;
+ sweep_line->current_edge = NULL;
+}
+
+static cairo_status_t
+sweep_line_insert (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (sweep_line->current_edge != NULL) {
+ cairo_bo_edge_t *prev, *next;
+ int cmp;
+
+ cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
+ sweep_line->current_edge,
+ edge);
+ if (cmp < 0) {
+ prev = sweep_line->current_edge;
+ next = prev->next;
+ while (next != NULL &&
+ _cairo_bo_sweep_line_compare_edges (sweep_line,
+ next, edge) < 0)
+ {
+ prev = next, next = prev->next;
+ }
+
+ prev->next = edge;
+ edge->prev = prev;
+ edge->next = next;
+ if (next != NULL)
+ next->prev = edge;
+ } else if (cmp > 0) {
+ next = sweep_line->current_edge;
+ prev = next->prev;
+ while (prev != NULL &&
+ _cairo_bo_sweep_line_compare_edges (sweep_line,
+ prev, edge) > 0)
+ {
+ next = prev, prev = next->prev;
+ }
+
+ next->prev = edge;
+ edge->next = next;
+ edge->prev = prev;
+ if (prev != NULL)
+ prev->next = edge;
+ else
+ sweep_line->head = edge;
+ } else {
+ prev = sweep_line->current_edge;
+ edge->prev = prev;
+ edge->next = prev->next;
+ if (prev->next != NULL)
+ prev->next->prev = edge;
+ prev->next = edge;
+ }
+ } else {
+ sweep_line->head = edge;
+ }
+
+ sweep_line->current_edge = edge;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (edge->prev != NULL)
+ edge->prev->next = edge->next;
+ else
+ sweep_line->head = edge->next;
+
+ if (edge->next != NULL)
+ edge->next->prev = edge->prev;
+
+ if (sweep_line->current_edge == edge)
+ sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static void
+_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right)
+{
+ if (left->prev != NULL)
+ left->prev->next = right;
+ else
+ sweep_line->head = right;
+
+ if (right->next != NULL)
+ right->next->prev = left;
+
+ left->next = right->next;
+ right->next = left;
+
+ right->prev = left->prev;
+ left->prev = right;
+}
+
+static inline cairo_bool_t
+edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+ if (_line_equal (&a->edge.line, &b->edge.line))
+ return TRUE;
+
+ if (_slope_compare (a, b))
+ return FALSE;
+
+ /* The choice of y is not truly arbitrary since we must guarantee that it
+ * is greater than the start of either line.
+ */
+ if (a->edge.line.p1.y == b->edge.line.p1.y) {
+ return a->edge.line.p1.x == b->edge.line.p1.x;
+ } else if (a->edge.line.p1.y < b->edge.line.p1.y) {
+ return edge_compare_for_y_against_x (b,
+ a->edge.line.p1.y,
+ a->edge.line.p1.x) == 0;
+ } else {
+ return edge_compare_for_y_against_x (a,
+ b->edge.line.p1.y,
+ b->edge.line.p1.x) == 0;
+ }
+}
+
+static void
+edges_end (cairo_bo_edge_t *left,
+ int32_t bot,
+ cairo_polygon_t *polygon)
+{
+ cairo_bo_deferred_t *l = &left->deferred;
+ cairo_bo_edge_t *right = l->other;
+
+ assert(right->deferred.other == NULL);
+ if (likely (l->top < bot)) {
+ _cairo_polygon_add_line (polygon, &left->edge.line, l->top, bot, 1);
+ _cairo_polygon_add_line (polygon, &right->edge.line, l->top, bot, -1);
+ }
+
+ l->other = NULL;
+}
+
+static inline void
+edges_start_or_continue (cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right,
+ int top,
+ cairo_polygon_t *polygon)
+{
+ assert (right->deferred.other == NULL);
+
+ if (left->deferred.other == right)
+ return;
+
+ if (left->deferred.other != NULL) {
+ if (right != NULL && edges_colinear (left->deferred.other, right)) {
+ cairo_bo_edge_t *old = left->deferred.other;
+
+ /* continuation on right, extend right to cover both */
+ assert (old->deferred.other == NULL);
+ assert (old->edge.line.p2.y > old->edge.line.p1.y);
+
+ if (old->edge.line.p1.y < right->edge.line.p1.y)
+ right->edge.line.p1 = old->edge.line.p1;
+ if (old->edge.line.p2.y > right->edge.line.p2.y)
+ right->edge.line.p2 = old->edge.line.p2;
+ left->deferred.other = right;
+ return;
+ }
+
+ edges_end (left, top, polygon);
+ }
+
+ if (right != NULL && ! edges_colinear (left, right)) {
+ left->deferred.top = top;
+ left->deferred.other = right;
+ }
+}
+
+#define is_zero(w) ((w)[0] == 0 || (w)[1] == 0)
+
+static inline void
+active_edges (cairo_bo_edge_t *left,
+ int32_t top,
+ cairo_polygon_t *polygon)
+{
+ cairo_bo_edge_t *right;
+ int winding[2] = {0, 0};
+
+ /* Yes, this is naive. Consider this a placeholder. */
+
+ while (left != NULL) {
+ assert (is_zero (winding));
+
+ do {
+ winding[left->a_or_b] += left->edge.dir;
+ if (! is_zero (winding))
+ break;
+
+ if unlikely ((left->deferred.other))
+ edges_end (left, top, polygon);
+
+ left = left->next;
+ if (! left)
+ return;
+ } while (1);
+
+ right = left->next;
+ do {
+ if unlikely ((right->deferred.other))
+ edges_end (right, top, polygon);
+
+ winding[right->a_or_b] += right->edge.dir;
+ if (is_zero (winding)) {
+ if (right->next == NULL ||
+ ! edges_colinear (right, right->next))
+ break;
+ }
+
+ right = right->next;
+ } while (1);
+
+ edges_start_or_continue (left, right, top, polygon);
+
+ left = right->next;
+ }
+}
+
+static cairo_status_t
+intersection_sweep (cairo_bo_event_t **start_events,
+ int num_events,
+ cairo_polygon_t *polygon)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
+ cairo_bo_event_queue_t event_queue;
+ cairo_bo_sweep_line_t sweep_line;
+ cairo_bo_event_t *event;
+ cairo_bo_edge_t *left, *right;
+ cairo_bo_edge_t *e1, *e2;
+
+ _cairo_bo_event_queue_init (&event_queue, start_events, num_events);
+ _cairo_bo_sweep_line_init (&sweep_line);
+
+ while ((event = _cairo_bo_event_dequeue (&event_queue))) {
+ if (event->point.y != sweep_line.current_y) {
+ active_edges (sweep_line.head,
+ sweep_line.current_y,
+ polygon);
+ sweep_line.current_y = event->point.y;
+ }
+
+ switch (event->type) {
+ case CAIRO_BO_EVENT_TYPE_START:
+ e1 = &((cairo_bo_start_event_t *) event)->edge;
+
+ status = sweep_line_insert (&sweep_line, e1);
+ if (unlikely (status))
+ goto unwind;
+
+ status = event_queue_insert_stop (&event_queue, e1);
+ if (unlikely (status))
+ goto unwind;
+
+ left = e1->prev;
+ right = e1->next;
+
+ if (left != NULL) {
+ status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ if (right != NULL) {
+ status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+
+ case CAIRO_BO_EVENT_TYPE_STOP:
+ e1 = ((cairo_bo_queue_event_t *) event)->e1;
+ _cairo_bo_event_queue_delete (&event_queue, event);
+
+ if (e1->deferred.other)
+ edges_end (e1, sweep_line.current_y, polygon);
+
+ left = e1->prev;
+ right = e1->next;
+
+ _cairo_bo_sweep_line_delete (&sweep_line, e1);
+
+ if (left != NULL && right != NULL) {
+ status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+
+ case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+ e1 = ((cairo_bo_queue_event_t *) event)->e1;
+ e2 = ((cairo_bo_queue_event_t *) event)->e2;
+ _cairo_bo_event_queue_delete (&event_queue, event);
+
+ /* skip this intersection if its edges are not adjacent */
+ if (e2 != e1->next)
+ break;
+
+ if (e1->deferred.other)
+ edges_end (e1, sweep_line.current_y, polygon);
+ if (e2->deferred.other)
+ edges_end (e2, sweep_line.current_y, polygon);
+
+ left = e1->prev;
+ right = e2->next;
+
+ _cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
+
+ /* after the swap e2 is left of e1 */
+
+ if (left != NULL) {
+ status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ if (right != NULL) {
+ status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+ }
+ }
+
+ unwind:
+ _cairo_bo_event_queue_fini (&event_queue);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a,
+ cairo_polygon_t *b, int winding_b)
+{
+ cairo_status_t status;
+ cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
+ cairo_bo_start_event_t *events;
+ cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+ cairo_bo_event_t **event_ptrs;
+ int num_events;
+ int i, j;
+
+ /* XXX lazy */
+ if (winding_a != CAIRO_FILL_RULE_WINDING) {
+ status = _cairo_polygon_reduce (a, winding_a);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (winding_b != CAIRO_FILL_RULE_WINDING) {
+ status = _cairo_polygon_reduce (b, winding_b);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (unlikely (0 == a->num_edges))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (unlikely (0 == b->num_edges)) {
+ a->num_edges = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ events = stack_events;
+ event_ptrs = stack_event_ptrs;
+ num_events = a->num_edges + b->num_edges;
+ if (num_events > ARRAY_LENGTH (stack_events)) {
+ events = _cairo_malloc_ab_plus_c (num_events,
+ sizeof (cairo_bo_start_event_t) +
+ sizeof (cairo_bo_event_t *),
+ sizeof (cairo_bo_event_t *));
+ if (unlikely (events == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ event_ptrs = (cairo_bo_event_t **) (events + num_events);
+ }
+
+ j = 0;
+ for (i = 0; i < a->num_edges; i++) {
+ event_ptrs[j] = (cairo_bo_event_t *) &events[j];
+
+ events[j].type = CAIRO_BO_EVENT_TYPE_START;
+ events[j].point.y = a->edges[i].top;
+ events[j].point.x =
+ _line_compute_intersection_x_for_y (&a->edges[i].line,
+ events[j].point.y);
+
+ events[j].edge.a_or_b = 0;
+ events[j].edge.edge = a->edges[i];
+ events[j].edge.deferred.other = NULL;
+ events[j].edge.prev = NULL;
+ events[j].edge.next = NULL;
+ j++;
+ }
+
+ for (i = 0; i < b->num_edges; i++) {
+ event_ptrs[j] = (cairo_bo_event_t *) &events[j];
+
+ events[j].type = CAIRO_BO_EVENT_TYPE_START;
+ events[j].point.y = b->edges[i].top;
+ events[j].point.x =
+ _line_compute_intersection_x_for_y (&b->edges[i].line,
+ events[j].point.y);
+
+ events[j].edge.a_or_b = 1;
+ events[j].edge.edge = b->edges[i];
+ events[j].edge.deferred.other = NULL;
+ events[j].edge.prev = NULL;
+ events[j].edge.next = NULL;
+ j++;
+ }
+ assert (j == num_events);
+
+#if 0
+ {
+ FILE *file = fopen ("clip_a.txt", "w");
+ _cairo_debug_print_polygon (file, a);
+ fclose (file);
+ }
+ {
+ FILE *file = fopen ("clip_b.txt", "w");
+ _cairo_debug_print_polygon (file, b);
+ fclose (file);
+ }
+#endif
+
+ a->num_edges = 0;
+ status = intersection_sweep (event_ptrs, num_events, a);
+ if (events != stack_events)
+ free (events);
+
+#if 0
+ {
+ FILE *file = fopen ("clip_result.txt", "w");
+ _cairo_debug_print_polygon (file, a);
+ fclose (file);
+ }
+#endif
+
+ return status;
+}
+
+cairo_status_t
+_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon,
+ cairo_fill_rule_t *winding,
+ cairo_box_t *boxes,
+ int num_boxes)
+{
+ cairo_polygon_t b;
+ cairo_status_t status;
+ int n;
+
+ if (num_boxes == 0) {
+ polygon->num_edges = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ for (n = 0; n < num_boxes; n++) {
+ if (polygon->extents.p1.x >= boxes[n].p1.x &&
+ polygon->extents.p2.x <= boxes[n].p2.x &&
+ polygon->extents.p1.y >= boxes[n].p1.y &&
+ polygon->extents.p2.y <= boxes[n].p2.y)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ _cairo_polygon_init (&b, NULL, 0);
+ for (n = 0; n < num_boxes; n++) {
+ if (boxes[n].p2.x > polygon->extents.p1.x &&
+ boxes[n].p1.x < polygon->extents.p2.x &&
+ boxes[n].p2.y > polygon->extents.p1.y &&
+ boxes[n].p1.y < polygon->extents.p2.y)
+ {
+ cairo_point_t p1, p2;
+
+ p1.y = boxes[n].p1.y;
+ p2.y = boxes[n].p2.y;
+
+ p2.x = p1.x = boxes[n].p1.x;
+ _cairo_polygon_add_external_edge (&b, &p1, &p2);
+
+ p2.x = p1.x = boxes[n].p2.x;
+ _cairo_polygon_add_external_edge (&b, &p2, &p1);
+ }
+ }
+
+ status = _cairo_polygon_intersect (polygon, *winding,
+ &b, CAIRO_FILL_RULE_WINDING);
+ _cairo_polygon_fini (&b);
+
+ *winding = CAIRO_FILL_RULE_WINDING;
+ return status;
+}
diff --git a/src/cairo-polygon-reduce.c b/src/cairo-polygon-reduce.c
new file mode 100755
index 000000000..ea457fe4e
--- /dev/null
+++ b/src/cairo-polygon-reduce.c
@@ -0,0 +1,1438 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ * Carl D. Worth <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-inline.h"
+
+#define DEBUG_POLYGON 0
+
+typedef cairo_point_t cairo_bo_point32_t;
+
+typedef struct _cairo_bo_intersect_ordinate {
+ int32_t ordinate;
+ enum { EXACT, INEXACT } exactness;
+} cairo_bo_intersect_ordinate_t;
+
+typedef struct _cairo_bo_intersect_point {
+ cairo_bo_intersect_ordinate_t x;
+ cairo_bo_intersect_ordinate_t y;
+} cairo_bo_intersect_point_t;
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+
+typedef struct _cairo_bo_deferred {
+ cairo_bo_edge_t *right;
+ int32_t top;
+} cairo_bo_deferred_t;
+
+struct _cairo_bo_edge {
+ cairo_edge_t edge;
+ cairo_bo_edge_t *prev;
+ cairo_bo_edge_t *next;
+ cairo_bo_deferred_t deferred;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef enum {
+ CAIRO_BO_EVENT_TYPE_STOP,
+ CAIRO_BO_EVENT_TYPE_INTERSECTION,
+ CAIRO_BO_EVENT_TYPE_START
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_start_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+ cairo_bo_edge_t edge;
+} cairo_bo_start_event_t;
+
+typedef struct _cairo_bo_queue_event {
+ cairo_bo_event_type_t type;
+ cairo_point_t point;
+ cairo_bo_edge_t *e1;
+ cairo_bo_edge_t *e2;
+} cairo_bo_queue_event_t;
+
+typedef struct _pqueue {
+ int size, max_size;
+
+ cairo_bo_event_t **elements;
+ cairo_bo_event_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _cairo_bo_event_queue {
+ cairo_freepool_t pool;
+ pqueue_t pqueue;
+ cairo_bo_event_t **start_events;
+} cairo_bo_event_queue_t;
+
+typedef struct _cairo_bo_sweep_line {
+ cairo_bo_edge_t *head;
+ int32_t current_y;
+ cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+ cairo_fixed_t y)
+{
+ cairo_fixed_t x, dy;
+
+ if (y == line->p1.y)
+ return line->p1.x;
+ if (y == line->p2.y)
+ return line->p2.x;
+
+ x = line->p1.x;
+ dy = line->p2.y - line->p1.y;
+ if (dy != 0) {
+ x += _cairo_fixed_mul_div_floor (y - line->p1.y,
+ line->p2.x - line->p1.x,
+ dy);
+ }
+
+ return x;
+}
+
+static inline int
+_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
+ cairo_bo_point32_t const *b)
+{
+ int cmp;
+
+ cmp = a->y - b->y;
+ if (cmp)
+ return cmp;
+
+ return a->x - b->x;
+}
+
+/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
+ * slope a is respectively greater than, equal to, or less than the
+ * slope of b.
+ *
+ * For each edge, consider the direction vector formed from:
+ *
+ * top -> bottom
+ *
+ * which is:
+ *
+ * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
+ *
+ * We then define the slope of each edge as dx/dy, (which is the
+ * inverse of the slope typically used in math instruction). We never
+ * compute a slope directly as the value approaches infinity, but we
+ * can derive a slope comparison without division as follows, (where
+ * the ? represents our compare operator).
+ *
+ * 1. slope(a) ? slope(b)
+ * 2. adx/ady ? bdx/bdy
+ * 3. (adx * bdy) ? (bdx * ady)
+ *
+ * Note that from step 2 to step 3 there is no change needed in the
+ * sign of the result since both ady and bdy are guaranteed to be
+ * greater than or equal to 0.
+ *
+ * When using this slope comparison to sort edges, some care is needed
+ * when interpreting the results. Since the slope compare operates on
+ * distance vectors from top to bottom it gives a correct left to
+ * right sort for edges that have a common top point, (such as two
+ * edges with start events at the same location). On the other hand,
+ * the sense of the result will be exactly reversed for two edges that
+ * have a common stop point.
+ */
+static inline int
+_slope_compare (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b)
+{
+ /* XXX: We're assuming here that dx and dy will still fit in 32
+ * bits. That's not true in general as there could be overflow. We
+ * should prevent that before the tessellation algorithm
+ * begins.
+ */
+ int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
+ int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+
+ /* Since the dy's are all positive by construction we can fast
+ * path several common cases.
+ */
+
+ /* First check for vertical lines. */
+ if (adx == 0)
+ return -bdx;
+ if (bdx == 0)
+ return adx;
+
+ /* Then where the two edges point in different directions wrt x. */
+ if ((adx ^ bdx) < 0)
+ return adx;
+
+ /* Finally we actually need to do the general comparison. */
+ {
+ int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
+ int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+ cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+ cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+ return _cairo_int64_cmp (adx_bdy, bdx_ady);
+ }
+}
+
+/*
+ * We need to compare the x-coordinates of a pair of lines for a particular y,
+ * without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ * X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy,
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy
+ * - (Y - A_y) * A_dx * B_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 128 bit arithmetic. For certain, but common,
+ * input we can reduce this down to a single 32 bit compare by inspecting the
+ * deltas.
+ *
+ * (And put the burden of the work on developing fast 128 bit ops, which are
+ * required throughout the tessellator.)
+ *
+ * See the similar discussion for _slope_compare().
+ */
+static int
+edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b,
+ int32_t y)
+{
+ /* XXX: We're assuming here that dx and dy will still fit in 32
+ * bits. That's not true in general as there could be overflow. We
+ * should prevent that before the tessellation algorithm
+ * begins.
+ */
+ int32_t dx;
+ int32_t adx, ady;
+ int32_t bdx, bdy;
+ enum {
+ HAVE_NONE = 0x0,
+ HAVE_DX = 0x1,
+ HAVE_ADX = 0x2,
+ HAVE_DX_ADX = HAVE_DX | HAVE_ADX,
+ HAVE_BDX = 0x4,
+ HAVE_DX_BDX = HAVE_DX | HAVE_BDX,
+ HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
+ HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX
+ } have_dx_adx_bdx = HAVE_ALL;
+
+ /* don't bother solving for abscissa if the edges' bounding boxes
+ * can be used to order them. */
+ {
+ int32_t amin, amax;
+ int32_t bmin, bmax;
+ if (a->edge.line.p1.x < a->edge.line.p2.x) {
+ amin = a->edge.line.p1.x;
+ amax = a->edge.line.p2.x;
+ } else {
+ amin = a->edge.line.p2.x;
+ amax = a->edge.line.p1.x;
+ }
+ if (b->edge.line.p1.x < b->edge.line.p2.x) {
+ bmin = b->edge.line.p1.x;
+ bmax = b->edge.line.p2.x;
+ } else {
+ bmin = b->edge.line.p2.x;
+ bmax = b->edge.line.p1.x;
+ }
+ if (amax < bmin) return -1;
+ if (amin > bmax) return +1;
+ }
+
+ ady = a->edge.line.p2.y - a->edge.line.p1.y;
+ adx = a->edge.line.p2.x - a->edge.line.p1.x;
+ if (adx == 0)
+ have_dx_adx_bdx &= ~HAVE_ADX;
+
+ bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+ bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+ if (bdx == 0)
+ have_dx_adx_bdx &= ~HAVE_BDX;
+
+ dx = a->edge.line.p1.x - b->edge.line.p1.x;
+ if (dx == 0)
+ have_dx_adx_bdx &= ~HAVE_DX;
+
+#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
+#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y)
+#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y)
+ switch (have_dx_adx_bdx) {
+ default:
+ case HAVE_NONE:
+ return 0;
+ case HAVE_DX:
+ /* A_dy * B_dy * (A_x - B_x) ∘ 0 */
+ return dx; /* ady * bdy is positive definite */
+ case HAVE_ADX:
+ /* 0 ∘ - (Y - A_y) * A_dx * B_dy */
+ return adx; /* bdy * (y - a->top.y) is positive definite */
+ case HAVE_BDX:
+ /* 0 ∘ (Y - B_y) * B_dx * A_dy */
+ return -bdx; /* ady * (y - b->top.y) is positive definite */
+ case HAVE_ADX_BDX:
+ /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
+ if ((adx ^ bdx) < 0) {
+ return adx;
+ } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */
+ cairo_int64_t adx_bdy, bdx_ady;
+
+ /* ∴ A_dx * B_dy ∘ B_dx * A_dy */
+
+ adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+ bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+ return _cairo_int64_cmp (adx_bdy, bdx_ady);
+ } else
+ return _cairo_int128_cmp (A, B);
+ case HAVE_DX_ADX:
+ /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */
+ if ((-adx ^ dx) < 0) {
+ return dx;
+ } else {
+ cairo_int64_t ady_dx, dy_adx;
+
+ ady_dx = _cairo_int32x32_64_mul (ady, dx);
+ dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
+
+ return _cairo_int64_cmp (ady_dx, dy_adx);
+ }
+ case HAVE_DX_BDX:
+ /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */
+ if ((bdx ^ dx) < 0) {
+ return dx;
+ } else {
+ cairo_int64_t bdy_dx, dy_bdx;
+
+ bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
+ dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx);
+
+ return _cairo_int64_cmp (bdy_dx, dy_bdx);
+ }
+ case HAVE_ALL:
+ /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */
+ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
+ }
+#undef B
+#undef A
+#undef L
+}
+
+/*
+ * We need to compare the x-coordinate of a line for a particular y wrt to a
+ * given x, without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ * X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ * A_x + (Y - A_y) * A_dx / A_dy ∘ X
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy (and (Y - A_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 64 bit arithmetic.
+ *
+ * See the similar discussion for _slope_compare() and
+ * edges_compare_x_for_y_general().
+ */
+static int
+edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
+ int32_t y,
+ int32_t x)
+{
+ int32_t adx, ady;
+ int32_t dx, dy;
+ cairo_int64_t L, R;
+
+ if (x < a->edge.line.p1.x && x < a->edge.line.p2.x)
+ return 1;
+ if (x > a->edge.line.p1.x && x > a->edge.line.p2.x)
+ return -1;
+
+ adx = a->edge.line.p2.x - a->edge.line.p1.x;
+ dx = x - a->edge.line.p1.x;
+
+ if (adx == 0)
+ return -dx;
+ if (dx == 0 || (adx ^ dx) < 0)
+ return adx;
+
+ dy = y - a->edge.line.p1.y;
+ ady = a->edge.line.p2.y - a->edge.line.p1.y;
+
+ L = _cairo_int32x32_64_mul (dy, adx);
+ R = _cairo_int32x32_64_mul (dx, ady);
+
+ return _cairo_int64_cmp (L, R);
+}
+
+static int
+edges_compare_x_for_y (const cairo_bo_edge_t *a,
+ const cairo_bo_edge_t *b,
+ int32_t y)
+{
+ /* If the sweep-line is currently on an end-point of a line,
+ * then we know its precise x value (and considering that we often need to
+ * compare events at end-points, this happens frequently enough to warrant
+ * special casing).
+ */
+ enum {
+ HAVE_NEITHER = 0x0,
+ HAVE_AX = 0x1,
+ HAVE_BX = 0x2,
+ HAVE_BOTH = HAVE_AX | HAVE_BX
+ } have_ax_bx = HAVE_BOTH;
+ int32_t ax, 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->current_y = INT32_MIN;
+ sweep_line->current_edge = NULL;
+}
+
+static cairo_status_t
+_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (sweep_line->current_edge != NULL) {
+ cairo_bo_edge_t *prev, *next;
+ int cmp;
+
+ cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
+ sweep_line->current_edge,
+ edge);
+ if (cmp < 0) {
+ prev = sweep_line->current_edge;
+ next = prev->next;
+ while (next != NULL &&
+ _cairo_bo_sweep_line_compare_edges (sweep_line,
+ next, edge) < 0)
+ {
+ prev = next, next = prev->next;
+ }
+
+ prev->next = edge;
+ edge->prev = prev;
+ edge->next = next;
+ if (next != NULL)
+ next->prev = edge;
+ } else if (cmp > 0) {
+ next = sweep_line->current_edge;
+ prev = next->prev;
+ while (prev != NULL &&
+ _cairo_bo_sweep_line_compare_edges (sweep_line,
+ prev, edge) > 0)
+ {
+ next = prev, prev = next->prev;
+ }
+
+ next->prev = edge;
+ edge->next = next;
+ edge->prev = prev;
+ if (prev != NULL)
+ prev->next = edge;
+ else
+ sweep_line->head = edge;
+ } else {
+ prev = sweep_line->current_edge;
+ edge->prev = prev;
+ edge->next = prev->next;
+ if (prev->next != NULL)
+ prev->next->prev = edge;
+ prev->next = edge;
+ }
+ } else {
+ sweep_line->head = edge;
+ }
+
+ sweep_line->current_edge = edge;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *edge)
+{
+ if (edge->prev != NULL)
+ edge->prev->next = edge->next;
+ else
+ sweep_line->head = edge->next;
+
+ if (edge->next != NULL)
+ edge->next->prev = edge->prev;
+
+ if (sweep_line->current_edge == edge)
+ sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static void
+_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line,
+ cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right)
+{
+ if (left->prev != NULL)
+ left->prev->next = right;
+ else
+ sweep_line->head = right;
+
+ if (right->next != NULL)
+ right->next->prev = left;
+
+ left->next = right->next;
+ right->next = left;
+
+ right->prev = left->prev;
+ left->prev = right;
+}
+
+static inline cairo_bool_t
+edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+ if (_line_equal (&a->edge.line, &b->edge.line))
+ return TRUE;
+
+ if (_slope_compare (a, b))
+ return FALSE;
+
+ /* The choice of y is not truly arbitrary since we must guarantee that it
+ * is greater than the start of either line.
+ */
+ if (a->edge.line.p1.y == b->edge.line.p1.y) {
+ return a->edge.line.p1.x == b->edge.line.p1.x;
+ } else if (a->edge.line.p2.y == b->edge.line.p2.y) {
+ return a->edge.line.p2.x == b->edge.line.p2.x;
+ } else if (a->edge.line.p1.y < b->edge.line.p1.y) {
+ return edge_compare_for_y_against_x (b,
+ a->edge.line.p1.y,
+ a->edge.line.p1.x) == 0;
+ } else {
+ return edge_compare_for_y_against_x (a,
+ b->edge.line.p1.y,
+ b->edge.line.p1.x) == 0;
+ }
+}
+
+static void
+_cairo_bo_edge_end (cairo_bo_edge_t *left,
+ int32_t bot,
+ cairo_polygon_t *polygon)
+{
+ cairo_bo_deferred_t *d = &left->deferred;
+
+ if (likely (d->top < bot)) {
+ _cairo_polygon_add_line (polygon,
+ &left->edge.line,
+ d->top, bot,
+ 1);
+ _cairo_polygon_add_line (polygon,
+ &d->right->edge.line,
+ d->top, bot,
+ -1);
+ }
+
+ d->right = NULL;
+}
+
+
+static inline void
+_cairo_bo_edge_start_or_continue (cairo_bo_edge_t *left,
+ cairo_bo_edge_t *right,
+ int top,
+ cairo_polygon_t *polygon)
+{
+ if (left->deferred.right == right)
+ return;
+
+ if (left->deferred.right != NULL) {
+ if (right != NULL && edges_colinear (left->deferred.right, right))
+ {
+ /* continuation on right, so just swap edges */
+ left->deferred.right = right;
+ return;
+ }
+
+ _cairo_bo_edge_end (left, top, polygon);
+ }
+
+ if (right != NULL && ! edges_colinear (left, right)) {
+ left->deferred.top = top;
+ left->deferred.right = right;
+ }
+}
+
+static inline void
+_active_edges_to_polygon (cairo_bo_edge_t *left,
+ int32_t top,
+ cairo_fill_rule_t fill_rule,
+ cairo_polygon_t *polygon)
+{
+ cairo_bo_edge_t *right;
+ unsigned int mask;
+
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ mask = ~0;
+ else
+ mask = 1;
+
+ while (left != NULL) {
+ int in_out = left->edge.dir;
+
+ right = left->next;
+ if (left->deferred.right == NULL) {
+ while (right != NULL && right->deferred.right == NULL)
+ right = right->next;
+
+ if (right != NULL && edges_colinear (left, right)) {
+ /* continuation on left */
+ left->deferred = right->deferred;
+ right->deferred.right = NULL;
+ }
+ }
+
+ right = left->next;
+ while (right != NULL) {
+ if (right->deferred.right != NULL)
+ _cairo_bo_edge_end (right, top, polygon);
+
+ in_out += right->edge.dir;
+ if ((in_out & mask) == 0) {
+ /* skip co-linear edges */
+ if (right->next == NULL || !edges_colinear (right, right->next))
+ break;
+ }
+
+ right = right->next;
+ }
+
+ _cairo_bo_edge_start_or_continue (left, right, top, polygon);
+
+ left = right;
+ if (left != NULL)
+ left = left->next;
+ }
+}
+
+
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events,
+ int num_events,
+ cairo_fill_rule_t fill_rule,
+ cairo_polygon_t *polygon)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
+ cairo_bo_event_queue_t event_queue;
+ cairo_bo_sweep_line_t sweep_line;
+ cairo_bo_event_t *event;
+ cairo_bo_edge_t *left, *right;
+ cairo_bo_edge_t *e1, *e2;
+
+ _cairo_bo_event_queue_init (&event_queue, start_events, num_events);
+ _cairo_bo_sweep_line_init (&sweep_line);
+
+ while ((event = _cairo_bo_event_dequeue (&event_queue))) {
+ if (event->point.y != sweep_line.current_y) {
+ _active_edges_to_polygon (sweep_line.head,
+ sweep_line.current_y,
+ fill_rule, polygon);
+
+ sweep_line.current_y = event->point.y;
+ }
+
+ switch (event->type) {
+ case CAIRO_BO_EVENT_TYPE_START:
+ e1 = &((cairo_bo_start_event_t *) event)->edge;
+
+ status = _cairo_bo_sweep_line_insert (&sweep_line, e1);
+ if (unlikely (status))
+ goto unwind;
+
+ status = _cairo_bo_event_queue_insert_stop (&event_queue, e1);
+ if (unlikely (status))
+ goto unwind;
+
+ left = e1->prev;
+ right = e1->next;
+
+ if (left != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ if (right != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+
+ case CAIRO_BO_EVENT_TYPE_STOP:
+ e1 = ((cairo_bo_queue_event_t *) event)->e1;
+ _cairo_bo_event_queue_delete (&event_queue, event);
+
+ left = e1->prev;
+ right = e1->next;
+
+ _cairo_bo_sweep_line_delete (&sweep_line, e1);
+
+ if (e1->deferred.right != NULL)
+ _cairo_bo_edge_end (e1, e1->edge.bottom, polygon);
+
+ if (left != NULL && right != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+
+ case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+ e1 = ((cairo_bo_queue_event_t *) event)->e1;
+ e2 = ((cairo_bo_queue_event_t *) event)->e2;
+ _cairo_bo_event_queue_delete (&event_queue, event);
+
+ /* skip this intersection if its edges are not adjacent */
+ if (e2 != e1->next)
+ break;
+
+ left = e1->prev;
+ right = e2->next;
+
+ _cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
+
+ /* after the swap e2 is left of e1 */
+
+ if (left != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ if (right != NULL) {
+ status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+ if (unlikely (status))
+ goto unwind;
+ }
+
+ break;
+ }
+ }
+
+ unwind:
+ _cairo_bo_event_queue_fini (&event_queue);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_polygon_reduce (cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_status_t status;
+ cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
+ cairo_bo_start_event_t *events;
+ cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+ cairo_bo_event_t **event_ptrs;
+ int num_limits;
+ int num_events;
+ int i;
+
+ num_events = polygon->num_edges;
+ if (unlikely (0 == num_events))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (DEBUG_POLYGON) {
+ FILE *file = fopen ("reduce_in.txt", "w");
+ _cairo_debug_print_polygon (file, polygon);
+ fclose (file);
+ }
+
+ events = stack_events;
+ event_ptrs = stack_event_ptrs;
+ if (num_events > ARRAY_LENGTH (stack_events)) {
+ events = _cairo_malloc_ab_plus_c (num_events,
+ sizeof (cairo_bo_start_event_t) +
+ sizeof (cairo_bo_event_t *),
+ sizeof (cairo_bo_event_t *));
+ if (unlikely (events == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ event_ptrs = (cairo_bo_event_t **) (events + num_events);
+ }
+
+ for (i = 0; i < num_events; i++) {
+ event_ptrs[i] = (cairo_bo_event_t *) &events[i];
+
+ events[i].type = CAIRO_BO_EVENT_TYPE_START;
+ events[i].point.y = polygon->edges[i].top;
+ events[i].point.x =
+ _line_compute_intersection_x_for_y (&polygon->edges[i].line,
+ events[i].point.y);
+
+ events[i].edge.edge = polygon->edges[i];
+ events[i].edge.deferred.right = NULL;
+ events[i].edge.prev = NULL;
+ events[i].edge.next = NULL;
+ }
+
+ num_limits = polygon->num_limits; polygon->num_limits = 0;
+ polygon->num_edges = 0;
+
+ status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs,
+ num_events,
+ fill_rule,
+ polygon);
+ polygon->num_limits = num_limits;
+
+ if (events != stack_events)
+ free (events);
+
+ if (DEBUG_POLYGON) {
+ FILE *file = fopen ("reduce_out.txt", "w");
+ _cairo_debug_print_polygon (file, polygon);
+ fclose (file);
+ }
+
+ return status;
+}
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
new file mode 100755
index 000000000..b0424f6e7
--- /dev/null
+++ b/src/cairo-polygon.c
@@ -0,0 +1,608 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-contour-private.h"
+#include "cairo-error-private.h"
+
+#define DEBUG_POLYGON 0
+
+#if DEBUG_POLYGON && !NDEBUG
+static void
+assert_last_edge_is_valid(cairo_polygon_t *polygon,
+ const cairo_box_t *limit)
+{
+ cairo_edge_t *edge;
+ cairo_fixed_t x;
+
+ edge = &polygon->edges[polygon->num_edges-1];
+
+ assert (edge->bottom > edge->top);
+ assert (edge->top >= limit->p1.y);
+ assert (edge->bottom <= limit->p2.y);
+
+ x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1,
+ &edge->line.p2,
+ edge->top);
+ assert (x >= limit->p1.x);
+ assert (x <= limit->p2.x);
+
+ x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1,
+ &edge->line.p2,
+ edge->bottom);
+ assert (x >= limit->p1.x);
+ assert (x <= limit->p2.x);
+}
+#else
+#define assert_last_edge_is_valid(p, l)
+#endif
+
+static void
+_cairo_polygon_add_edge (cairo_polygon_t *polygon,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ int dir);
+
+void
+_cairo_polygon_limit (cairo_polygon_t *polygon,
+ const cairo_box_t *limits,
+ int num_limits)
+{
+ int n;
+
+ polygon->limits = limits;
+ polygon->num_limits = num_limits;
+
+ if (polygon->num_limits) {
+ polygon->limit = limits[0];
+ for (n = 1; n < num_limits; n++) {
+ if (limits[n].p1.x < polygon->limit.p1.x)
+ polygon->limit.p1.x = limits[n].p1.x;
+
+ if (limits[n].p1.y < polygon->limit.p1.y)
+ polygon->limit.p1.y = limits[n].p1.y;
+
+ if (limits[n].p2.x > polygon->limit.p2.x)
+ polygon->limit.p2.x = limits[n].p2.x;
+
+ if (limits[n].p2.y > polygon->limit.p2.y)
+ polygon->limit.p2.y = limits[n].p2.y;
+ }
+ }
+}
+
+void
+_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon,
+ const cairo_clip_t *clip)
+{
+ if (clip)
+ _cairo_polygon_limit (polygon, clip->boxes, clip->num_boxes);
+ else
+ _cairo_polygon_limit (polygon, 0, 0);
+}
+
+void
+_cairo_polygon_init (cairo_polygon_t *polygon,
+ const cairo_box_t *limits,
+ int num_limits)
+{
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
+
+ polygon->status = CAIRO_STATUS_SUCCESS;
+
+ polygon->num_edges = 0;
+
+ polygon->edges = polygon->edges_embedded;
+ polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
+
+ polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+ polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+
+ _cairo_polygon_limit (polygon, limits, num_limits);
+}
+
+void
+_cairo_polygon_init_with_clip (cairo_polygon_t *polygon,
+ const cairo_clip_t *clip)
+{
+ if (clip)
+ _cairo_polygon_init (polygon, clip->boxes, clip->num_boxes);
+ else
+ _cairo_polygon_init (polygon, 0, 0);
+}
+
+cairo_status_t
+_cairo_polygon_init_boxes (cairo_polygon_t *polygon,
+ const cairo_boxes_t *boxes)
+{
+ const struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
+
+ polygon->status = CAIRO_STATUS_SUCCESS;
+
+ polygon->num_edges = 0;
+
+ polygon->edges = polygon->edges_embedded;
+ polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
+ if (boxes->num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) {
+ polygon->edges_size = 2 * boxes->num_boxes;
+ polygon->edges = _cairo_malloc_ab (polygon->edges_size,
+ 2*sizeof(cairo_edge_t));
+ if (unlikely (polygon->edges == NULL))
+ return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+ polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+
+ polygon->limits = NULL;
+ polygon->num_limits = 0;
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ cairo_point_t p1, p2;
+
+ p1 = chunk->base[i].p1;
+ p2.x = p1.x;
+ p2.y = chunk->base[i].p2.y;
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+
+ p1 = chunk->base[i].p2;
+ p2.x = p1.x;
+ p2.y = chunk->base[i].p1.y;
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+ }
+ }
+
+ return polygon->status;
+}
+
+cairo_status_t
+_cairo_polygon_init_box_array (cairo_polygon_t *polygon,
+ cairo_box_t *boxes,
+ int num_boxes)
+{
+ int i;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
+
+ polygon->status = CAIRO_STATUS_SUCCESS;
+
+ polygon->num_edges = 0;
+
+ polygon->edges = polygon->edges_embedded;
+ polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
+ if (num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) {
+ polygon->edges_size = 2 * num_boxes;
+ polygon->edges = _cairo_malloc_ab (polygon->edges_size,
+ 2*sizeof(cairo_edge_t));
+ if (unlikely (polygon->edges == NULL))
+ return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+ polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+
+ polygon->limits = NULL;
+ polygon->num_limits = 0;
+
+ for (i = 0; i < num_boxes; i++) {
+ cairo_point_t p1, p2;
+
+ p1 = boxes[i].p1;
+ p2.x = p1.x;
+ p2.y = boxes[i].p2.y;
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+
+ p1 = boxes[i].p2;
+ p2.x = p1.x;
+ p2.y = boxes[i].p1.y;
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+ }
+
+ return polygon->status;
+}
+
+
+void
+_cairo_polygon_fini (cairo_polygon_t *polygon)
+{
+ if (polygon->edges != polygon->edges_embedded)
+ free (polygon->edges);
+
+ VG (VALGRIND_MAKE_MEM_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 bot_left, top_right;
+ cairo_fixed_t top_y, bot_y;
+ int n;
+
+ for (n = 0; n < polygon->num_limits; n++) {
+ const cairo_box_t *limits = &polygon->limits[n];
+ cairo_fixed_t pleft, pright;
+
+ if (top >= limits->p2.y)
+ continue;
+ if (bottom <= limits->p1.y)
+ continue;
+
+ bot_left.x = limits->p1.x;
+ bot_left.y = limits->p2.y;
+
+ top_right.x = limits->p2.x;
+ top_right.y = limits->p1.y;
+
+ /* The useful region */
+ top_y = MAX (top, limits->p1.y);
+ bot_y = MIN (bottom, limits->p2.y);
+
+ /* The projection of the edge on the horizontal axis */
+ pleft = MIN (p1->x, p2->x);
+ pright = MAX (p1->x, p2->x);
+
+ if (limits->p1.x <= pleft && pright <= limits->p2.x) {
+ /* Projection of the edge completely contained in the box:
+ * clip vertically by restricting top and bottom */
+
+ _add_edge (polygon, p1, p2, top_y, bot_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ } else if (pright <= limits->p1.x) {
+ /* Projection of the edge to the left of the box:
+ * replace with the left side of the box (clipped top/bottom) */
+
+ _add_edge (polygon, &limits->p1, &bot_left, top_y, bot_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ } else if (limits->p2.x <= pleft) {
+ /* Projection of the edge to the right of the box:
+ * replace with the right side of the box (clipped top/bottom) */
+
+ _add_edge (polygon, &top_right, &limits->p2, top_y, bot_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ } else {
+ /* The edge and the box intersect in a generic way */
+ cairo_fixed_t left_y, right_y;
+ cairo_bool_t top_left_to_bottom_right;
+
+ /*
+ * The edge intersects the lines corresponding to the left
+ * and right sides of the limit box at left_y and right_y,
+ * but we need to add edges for the range from top_y to
+ * bot_y.
+ *
+ * For both intersections, there are three cases:
+ *
+ * 1) It is outside the vertical range of the limit
+ * box. In this case we can simply further clip the
+ * edge we will be emitting (i.e. restrict its
+ * top/bottom limits to those of the limit box).
+ *
+ * 2) It is inside the vertical range of the limit
+ * box. In this case, we need to add the vertical edge
+ * connecting the correct vertex to the intersection,
+ * in order to preserve the winding count.
+ *
+ * 3) It is exactly on the box. In this case, do nothing.
+ *
+ * These operations restrict the active range (stored in
+ * top_y/bot_y) so that the p1-p2 edge is completely
+ * inside the box if it is clipped to this vertical range.
+ */
+
+ top_left_to_bottom_right = (p1->x <= p2->x) == (p1->y <= p2->y);
+ if (top_left_to_bottom_right) {
+ if (pleft >= limits->p1.x) {
+ left_y = top_y;
+ } else {
+ left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p1.x);
+ if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x)
+ left_y++;
+ }
+
+ left_y = MIN (left_y, bot_y);
+ if (top_y < left_y) {
+ _add_edge (polygon, &limits->p1, &bot_left,
+ top_y, left_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ top_y = left_y;
+ }
+
+ if (pright <= limits->p2.x) {
+ right_y = bot_y;
+ } else {
+ right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p2.x);
+ if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x)
+ right_y--;
+ }
+
+ right_y = MAX (right_y, top_y);
+ if (bot_y > right_y) {
+ _add_edge (polygon, &top_right, &limits->p2,
+ right_y, bot_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ bot_y = right_y;
+ }
+ } else {
+ if (pright <= limits->p2.x) {
+ right_y = top_y;
+ } else {
+ right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p2.x);
+ if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x)
+ right_y++;
+ }
+
+ right_y = MIN (right_y, bot_y);
+ if (top_y < right_y) {
+ _add_edge (polygon, &top_right, &limits->p2,
+ top_y, right_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ top_y = right_y;
+ }
+
+ if (pleft >= limits->p1.x) {
+ left_y = bot_y;
+ } else {
+ left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p1.x);
+ if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x)
+ left_y--;
+ }
+
+ left_y = MAX (left_y, top_y);
+ if (bot_y > left_y) {
+ _add_edge (polygon, &limits->p1, &bot_left,
+ left_y, bot_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ bot_y = left_y;
+ }
+ }
+
+ if (top_y != bot_y) {
+ _add_edge (polygon, p1, p2, top_y, bot_y, dir);
+ assert_last_edge_is_valid (polygon, limits);
+ }
+ }
+ }
+}
+
+static void
+_cairo_polygon_add_edge (cairo_polygon_t *polygon,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ int dir)
+{
+ /* drop horizontal edges */
+ if (p1->y == p2->y)
+ return;
+
+ if (p1->y > p2->y) {
+ const cairo_point_t *t;
+ t = p1, p1 = p2, p2 = t;
+ dir = -dir;
+ }
+
+ if (polygon->num_limits) {
+ if (p2->y <= polygon->limit.p1.y)
+ return;
+
+ if (p1->y >= polygon->limit.p2.y)
+ return;
+
+ _add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir);
+ } else
+ _add_edge (polygon, p1, p2, p1->y, p2->y, dir);
+}
+
+cairo_status_t
+_cairo_polygon_add_external_edge (void *polygon,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ _cairo_polygon_add_edge (polygon, p1, p2, 1);
+ return _cairo_polygon_status (polygon);
+}
+
+cairo_status_t
+_cairo_polygon_add_line (cairo_polygon_t *polygon,
+ const cairo_line_t *line,
+ int top, int bottom,
+ int dir)
+{
+ /* drop horizontal edges */
+ if (line->p1.y == line->p2.y)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (bottom <= top)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (polygon->num_limits) {
+ if (line->p2.y <= polygon->limit.p1.y)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (line->p1.y >= polygon->limit.p2.y)
+ return CAIRO_STATUS_SUCCESS;
+
+ _add_clipped_edge (polygon, &line->p1, &line->p2, top, bottom, dir);
+ } else
+ _add_edge (polygon, &line->p1, &line->p2, top, bottom, dir);
+
+ return polygon->status;
+}
+
+cairo_status_t
+_cairo_polygon_add_contour (cairo_polygon_t *polygon,
+ const cairo_contour_t *contour)
+{
+ const struct _cairo_contour_chain *chain;
+ const cairo_point_t *prev = NULL;
+ int i;
+
+ if (contour->chain.num_points <= 1)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ prev = &contour->chain.points[0];
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ _cairo_polygon_add_edge (polygon, prev, &chain->points[i],
+ contour->direction);
+ prev = &chain->points[i];
+ }
+ }
+
+ return polygon->status;
+}
+
+void
+_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy)
+{
+ int n;
+
+ dx = _cairo_fixed_from_int (dx);
+ dy = _cairo_fixed_from_int (dy);
+
+ polygon->extents.p1.x += dx;
+ polygon->extents.p2.x += dx;
+ polygon->extents.p1.y += dy;
+ polygon->extents.p2.y += dy;
+
+ for (n = 0; n < polygon->num_edges; n++) {
+ cairo_edge_t *e = &polygon->edges[n];
+
+ e->top += dy;
+ e->bottom += dy;
+
+ e->line.p1.x += dx;
+ e->line.p2.x += dx;
+ e->line.p1.y += dy;
+ e->line.p2.y += dy;
+ }
+}
diff --git a/src/cairo-private.h b/src/cairo-private.h
new file mode 100755
index 000000000..9f4f55b7c
--- /dev/null
+++ b/src/cairo-private.h
@@ -0,0 +1,64 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_PRIVATE_H
+#define CAIRO_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-reference-count-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo {
+ cairo_reference_count_t ref_count;
+ cairo_status_t status;
+ cairo_user_data_array_t user_data;
+
+ const cairo_backend_t *backend;
+};
+
+cairo_private cairo_t *
+_cairo_create_in_error (cairo_status_t status);
+
+cairo_private void
+_cairo_init (cairo_t *cr,
+ const cairo_backend_t *backend);
+
+cairo_private void
+_cairo_fini (cairo_t *cr);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_PRIVATE_H */
diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
new file mode 100755
index 000000000..1d5d27d49
--- /dev/null
+++ b/src/cairo-ps-surface-private.h
@@ -0,0 +1,104 @@
+/* -*- 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;
+
+ 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/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
new file mode 100755
index 000000000..01df6090b
--- /dev/null
+++ b/src/cairo-ps-surface.c
@@ -0,0 +1,4660 @@
+/* -*- 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-pdf-shading-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-list-inline.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-type3-glyph-surface-private.h"
+#include "cairo-image-info-private.h"
+
+#include <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.
+ *
+ * Since: 1.2
+ **/
+
+typedef enum {
+ CAIRO_PS_COMPRESS_NONE,
+ CAIRO_PS_COMPRESS_LZW,
+ CAIRO_PS_COMPRESS_DEFLATE
+ } cairo_ps_compress_t;
+
+static const cairo_surface_backend_t cairo_ps_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend;
+
+static cairo_bool_t
+_cairo_ps_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle);
+
+static const cairo_ps_level_t _cairo_ps_levels[] =
+{
+ CAIRO_PS_LEVEL_2,
+ CAIRO_PS_LEVEL_3
+};
+
+#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels)
+
+static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] =
+{
+ "PS Level 2",
+ "PS Level 3"
+};
+
+static const char *_cairo_ps_supported_mime_types[] =
+{
+ CAIRO_MIME_TYPE_JPEG,
+ NULL
+};
+
+typedef struct _cairo_page_standard_media {
+ const char *name;
+ int width;
+ int height;
+} cairo_page_standard_media_t;
+
+static const cairo_page_standard_media_t _cairo_page_standard_media[] =
+{
+ { "A0", 2384, 3371 },
+ { "A1", 1685, 2384 },
+ { "A2", 1190, 1684 },
+ { "A3", 842, 1190 },
+ { "A4", 595, 842 },
+ { "A5", 420, 595 },
+ { "B4", 729, 1032 },
+ { "B5", 516, 729 },
+ { "Letter", 612, 792 },
+ { "Tabloid", 792, 1224 },
+ { "Ledger", 1224, 792 },
+ { "Legal", 612, 1008 },
+ { "Statement", 396, 612 },
+ { "Executive", 540, 720 },
+ { "Folio", 612, 936 },
+ { "Quarto", 610, 780 },
+ { "10x14", 720, 1008 },
+};
+
+typedef struct _cairo_page_media {
+ char *name;
+ int width;
+ int height;
+ cairo_list_t link;
+} cairo_page_media_t;
+
+static void
+_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
+{
+ char ctime_buf[26];
+ time_t now;
+ char **comments;
+ int i, num_comments;
+ int level;
+ const char *eps_header = "";
+ cairo_bool_t has_bbox;
+
+ if (surface->has_creation_date)
+ now = surface->creation_date;
+ else
+ now = time (NULL);
+
+ if (surface->ps_level_used == CAIRO_PS_LEVEL_2)
+ level = 2;
+ else
+ level = 3;
+
+ if (surface->eps)
+ eps_header = " EPSF-3.0";
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%!PS-Adobe-3.0%s\n"
+ "%%%%Creator: cairo %s (http://cairographics.org)\n"
+ "%%%%CreationDate: %s"
+ "%%%%Pages: %d\n",
+ eps_header,
+ cairo_version_string (),
+ ctime_r (&now, ctime_buf),
+ surface->num_pages);
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%%%DocumentData: Clean7Bit\n"
+ "%%%%LanguageLevel: %d\n",
+ level);
+
+ if (!cairo_list_is_empty (&surface->document_media)) {
+ cairo_page_media_t *page;
+ cairo_bool_t first = TRUE;
+
+ cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) {
+ if (first) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%%%DocumentMedia: ");
+ first = FALSE;
+ } else {
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%%%+ ");
+ }
+ _cairo_output_stream_printf (surface->final_stream,
+ "%s %d %d 0 () ()\n",
+ page->name,
+ page->width,
+ page->height);
+ }
+ }
+
+ has_bbox = FALSE;
+ num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
+ comments = _cairo_array_index (&surface->dsc_header_comments, 0);
+ if (comments == NULL)
+ return;
+
+ for (i = 0; i < num_comments; i++) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "%s\n", comments[i]);
+ if (strncmp (comments[i], "%%BoundingBox:", 14) == 0)
+ has_bbox = TRUE;
+
+ free (comments[i]);
+ comments[i] = NULL;
+ }
+
+ if (!has_bbox) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%%%BoundingBox: %d %d %d %d\n",
+ surface->bbox_x1,
+ surface->bbox_y1,
+ surface->bbox_x2,
+ surface->bbox_y2);
+ }
+
+ _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,
+ "save\n"
+ "50 dict begin\n");
+ } else {
+ _cairo_output_stream_printf (surface->final_stream,
+ "/languagelevel where\n"
+ "{ pop languagelevel } { 1 } ifelse\n"
+ "%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n"
+ " (This print job requires a PostScript Language Level %d printer.) show\n"
+ " showpage quit } if\n",
+ level,
+ level);
+ }
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "/q { gsave } bind def\n"
+ "/Q { grestore } bind def\n"
+ "/cm { 6 array astore concat } bind def\n"
+ "/w { setlinewidth } bind def\n"
+ "/J { setlinecap } bind def\n"
+ "/j { setlinejoin } bind def\n"
+ "/M { setmiterlimit } bind def\n"
+ "/d { setdash } bind def\n"
+ "/m { moveto } bind def\n"
+ "/l { lineto } bind def\n"
+ "/c { curveto } bind def\n"
+ "/h { closepath } bind def\n"
+ "/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n"
+ " 0 exch rlineto 0 rlineto closepath } bind def\n"
+ "/S { stroke } bind def\n"
+ "/f { fill } bind def\n"
+ "/f* { eofill } bind def\n"
+ "/n { newpath } bind def\n"
+ "/W { clip } bind def\n"
+ "/W* { eoclip } bind def\n"
+ "/BT { } bind def\n"
+ "/ET { } bind def\n"
+ "/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");
+ }
+}
+
+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;
+}
+
+
+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_ps (&subset, font_subset);
+ if (unlikely (status))
+ return status;
+
+ /* FIXME: Figure out document structure convention for fonts */
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->final_stream,
+ "%% _cairo_ps_surface_emit_truetype_font_subset\n");
+#endif
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "11 dict begin\n"
+ "/FontType 42 def\n"
+ "/FontName /%s def\n"
+ "/PaintType 0 def\n"
+ "/FontMatrix [ 1 0 0 1 0 0 ] def\n"
+ "/FontBBox [ 0 0 0 0 ] def\n"
+ "/Encoding 256 array def\n"
+ "0 1 255 { Encoding exch /.notdef put } for\n",
+ subset.ps_name);
+
+ /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */
+
+ if (font_subset->is_latin) {
+ for (i = 1; i < 256; i++) {
+ if (font_subset->latin_to_subset_glyph_index[i] > 0) {
+ if (font_subset->glyph_names != NULL) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "Encoding %d /%s put\n",
+ i, font_subset->glyph_names[font_subset->latin_to_subset_glyph_index[i]]);
+ } else {
+ _cairo_output_stream_printf (surface->final_stream,
+ "Encoding %d /g%ld put\n", i, font_subset->latin_to_subset_glyph_index[i]);
+ }
+ }
+ }
+ } else {
+ for (i = 1; i < font_subset->num_glyphs; i++) {
+ if (font_subset->glyph_names != NULL) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "Encoding %d /%s put\n",
+ i, font_subset->glyph_names[i]);
+ } else {
+ _cairo_output_stream_printf (surface->final_stream,
+ "Encoding %d /g%d put\n", i, i);
+ }
+ }
+ }
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "/CharStrings %d dict dup begin\n"
+ "/.notdef 0 def\n",
+ font_subset->num_glyphs);
+
+ for (i = 1; i < font_subset->num_glyphs; i++) {
+ if (font_subset->glyph_names != NULL) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "/%s %d def\n",
+ font_subset->glyph_names[i], i);
+ } else {
+ _cairo_output_stream_printf (surface->final_stream,
+ "/g%d %d def\n", i, i);
+ }
+ }
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "end readonly def\n");
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "/sfnts [\n");
+ begin = 0;
+ end = 0;
+ for (i = 0; i < subset.num_string_offsets; i++) {
+ end = subset.string_offsets[i];
+ _cairo_output_stream_printf (surface->final_stream,"<");
+ _cairo_output_stream_write_hex_string (surface->final_stream,
+ subset.data + begin, end - begin);
+ _cairo_output_stream_printf (surface->final_stream,"00>\n");
+ begin = end;
+ }
+ if (subset.data_length > end) {
+ _cairo_output_stream_printf (surface->final_stream,"<");
+ _cairo_output_stream_write_hex_string (surface->final_stream,
+ subset.data + end, subset.data_length - end);
+ _cairo_output_stream_printf (surface->final_stream,"00>\n");
+ }
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "] def\n"
+ "/f-%d-%d currentdict end definefont pop\n",
+ font_subset->font_id,
+ font_subset->subset_id);
+
+ _cairo_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_int_status_t
+_cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_ps_surface_t *surface = closure;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ unsigned int i;
+ cairo_surface_t *type3_surface;
+
+ type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
+ NULL,
+ _cairo_ps_emit_imagemask,
+ surface->font_subsets);
+
+ 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)) {
+ cairo_surface_destroy (type3_surface);
+ 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_int_status_t
+_cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_ps_surface_t *surface = closure;
+ cairo_int_status_t status;
+
+ status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+ if (_cairo_int_status_is_error (status))
+ return status;
+
+ status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_ps_surface_t *surface = closure;
+ cairo_int_status_t status;
+
+ status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+ if (_cairo_int_status_is_error (status))
+ return status;
+
+ status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface)
+{
+ cairo_status_t status;
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->final_stream,
+ "%% _cairo_ps_surface_emit_font_subsets\n");
+#endif
+
+ status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
+ _cairo_ps_surface_analyze_user_font_subset,
+ surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets,
+ _cairo_ps_surface_emit_unscaled_font_subset,
+ surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets,
+ _cairo_ps_surface_emit_scaled_font_subset,
+ surface);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
+ _cairo_ps_surface_emit_scaled_font_subset,
+ surface);
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_body (cairo_ps_surface_t *surface)
+{
+ char buf[4096];
+ int n;
+
+ if (ferror (surface->tmpfile) != 0)
+ return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
+
+ rewind (surface->tmpfile);
+ while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0)
+ _cairo_output_stream_write (surface->final_stream, buf, n);
+
+ if (ferror (surface->tmpfile) != 0)
+ return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
+{
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%%%Trailer\n");
+
+ if (surface->eps) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "end 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;
+ }
+
+ _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
+ surface->has_creation_date = FALSE;
+ surface->eps = FALSE;
+ surface->ps_level = CAIRO_PS_LEVEL_3;
+ surface->ps_level_used = CAIRO_PS_LEVEL_2;
+ surface->width = width;
+ surface->height = height;
+ cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height);
+ 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;
+
+ if (surface->status)
+ return FALSE;
+ if (surface->finished) {
+ if (set_error_on_failure)
+ _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return FALSE;
+ }
+
+ if (! _cairo_surface_is_paginated (surface)) {
+ if (set_error_on_failure)
+ _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return FALSE;
+ }
+
+ target = _cairo_paginated_surface_get_target (surface);
+ if (target->status) {
+ if (set_error_on_failure)
+ _cairo_surface_set_error (surface, target->status);
+ return FALSE;
+ }
+ if (target->finished) {
+ if (set_error_on_failure)
+ _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return FALSE;
+ }
+
+ if (! _cairo_surface_is_ps (target)) {
+ if (set_error_on_failure)
+ _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return FALSE;
+ }
+
+ *ps_surface = (cairo_ps_surface_t *) target;
+ return TRUE;
+}
+
+/**
+ * cairo_ps_surface_restrict_to_level:
+ * @surface: a PostScript #cairo_surface_t
+ * @level: PostScript level
+ *
+ * Restricts the generated PostSript file to @level. See
+ * cairo_ps_get_levels() for a list of available level values that
+ * can be used here.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the given surface. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface.
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_surface_restrict_to_level (cairo_surface_t *surface,
+ cairo_ps_level_t level)
+{
+ cairo_ps_surface_t *ps_surface = NULL;
+
+ if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+ return;
+
+ if (level < CAIRO_PS_LEVEL_LAST)
+ ps_surface->ps_level = level;
+}
+
+/**
+ * cairo_ps_get_levels:
+ * @levels: supported level list
+ * @num_levels: list length
+ *
+ * Used to retrieve the list of supported levels. See
+ * cairo_ps_surface_restrict_to_level().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_get_levels (cairo_ps_level_t const **levels,
+ int *num_levels)
+{
+ if (levels != NULL)
+ *levels = _cairo_ps_levels;
+
+ if (num_levels != NULL)
+ *num_levels = CAIRO_PS_LEVEL_LAST;
+}
+
+/**
+ * cairo_ps_level_to_string:
+ * @level: a level id
+ *
+ * Get the string representation of the given @level id. This function
+ * will return %NULL if @level id isn't valid. See cairo_ps_get_levels()
+ * for a way to get the list of valid level ids.
+ *
+ * Return value: the string associated to given level.
+ *
+ * Since: 1.6
+ **/
+const char *
+cairo_ps_level_to_string (cairo_ps_level_t level)
+{
+ if (level >= CAIRO_PS_LEVEL_LAST)
+ return NULL;
+
+ return _cairo_ps_level_strings[level];
+}
+
+/**
+ * cairo_ps_surface_set_eps:
+ * @surface: a PostScript #cairo_surface_t
+ * @eps: %TRUE to output EPS format PostScript
+ *
+ * If @eps is %TRUE, the PostScript surface will output Encapsulated
+ * PostScript.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the current page. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface. An Encapsulated PostScript file should never contain more
+ * than one page.
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_surface_set_eps (cairo_surface_t *surface,
+ cairo_bool_t eps)
+{
+ cairo_ps_surface_t *ps_surface = NULL;
+
+ if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+ return;
+
+ ps_surface->eps = eps;
+}
+
+/**
+ * cairo_ps_surface_get_eps:
+ * @surface: a PostScript #cairo_surface_t
+ *
+ * Check whether the PostScript surface will output Encapsulated PostScript.
+ *
+ * Return value: %TRUE if the surface will output Encapsulated PostScript.
+ *
+ * Since: 1.6
+ **/
+cairo_public cairo_bool_t
+cairo_ps_surface_get_eps (cairo_surface_t *surface)
+{
+ cairo_ps_surface_t *ps_surface = NULL;
+
+ if (! _extract_ps_surface (surface, FALSE, &ps_surface))
+ return FALSE;
+
+ return ps_surface->eps;
+}
+
+/**
+ * cairo_ps_surface_set_size:
+ * @surface: a PostScript #cairo_surface_t
+ * @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
+ * @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
+ *
+ * Changes the size of a PostScript surface for the current (and
+ * subsequent) pages.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the current page. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface or immediately after completing a page with either
+ * cairo_show_page() or cairo_copy_page().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_ps_surface_set_size (cairo_surface_t *surface,
+ double width_in_points,
+ double height_in_points)
+{
+ cairo_ps_surface_t *ps_surface = NULL;
+ cairo_status_t status;
+
+ if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+ return;
+
+ ps_surface->width = width_in_points;
+ ps_surface->height = height_in_points;
+ cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points);
+ _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators,
+ &ps_surface->cairo_to_ps);
+ status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface,
+ width_in_points,
+ height_in_points);
+ if (status)
+ status = _cairo_surface_set_error (surface, status);
+}
+
+/**
+ * cairo_ps_surface_dsc_comment:
+ * @surface: a PostScript #cairo_surface_t
+ * @comment: a comment string to be emitted into the PostScript output
+ *
+ * Emit a comment into the PostScript output for the given surface.
+ *
+ * The comment is expected to conform to the PostScript Language
+ * Document Structuring Conventions (DSC). Please see that manual for
+ * details on the available comments and their meanings. In
+ * particular, the \%\%IncludeFeature comment allows a
+ * device-independent means of controlling printer device features. So
+ * the PostScript Printer Description Files Specification will also be
+ * a useful reference.
+ *
+ * The comment string must begin with a percent character (\%) and the
+ * total length of the string (including any initial percent
+ * characters) must not exceed 255 characters. Violating either of
+ * these conditions will place @surface into an error state. But
+ * beyond these two conditions, this function will not enforce
+ * conformance of the comment with any particular specification.
+ *
+ * The comment string should not have a trailing newline.
+ *
+ * The DSC specifies different sections in which particular comments
+ * can appear. This function provides for comments to be emitted
+ * within three sections: the header, the Setup section, and the
+ * PageSetup section. Comments appearing in the first two sections
+ * apply to the entire document while comments in the BeginPageSetup
+ * section apply only to a single page.
+ *
+ * For comments to appear in the header section, this function should
+ * be called after the surface is created, but before a call to
+ * cairo_ps_surface_dsc_begin_setup().
+ *
+ * For comments to appear in the Setup section, this function should
+ * be called after a call to cairo_ps_surface_dsc_begin_setup() but
+ * before a call to cairo_ps_surface_dsc_begin_page_setup().
+ *
+ * For comments to appear in the PageSetup section, this function
+ * should be called after a call to
+ * cairo_ps_surface_dsc_begin_page_setup().
+ *
+ * Note that it is only necessary to call
+ * cairo_ps_surface_dsc_begin_page_setup() for the first page of any
+ * surface. After a call to cairo_show_page() or cairo_copy_page()
+ * comments are unambiguously directed to the PageSetup section of the
+ * current page. But it doesn't hurt to call this function at the
+ * beginning of every page as that consistency may make the calling
+ * code simpler.
+ *
+ * As a final note, cairo automatically generates several comments on
+ * its own. As such, applications must not manually generate any of
+ * the following comments:
+ *
+ * Header section: \%!PS-Adobe-3.0, \%\%Creator, \%\%CreationDate, \%\%Pages,
+ * \%\%BoundingBox, \%\%DocumentData, \%\%LanguageLevel, \%\%EndComments.
+ *
+ * Setup section: \%\%BeginSetup, \%\%EndSetup
+ *
+ * PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup.
+ *
+ * Other sections: \%\%BeginProlog, \%\%EndProlog, \%\%Page, \%\%Trailer, \%\%EOF
+ *
+ * Here is an example sequence showing how this function might be used:
+ *
+ * <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);
+ if (comments == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ 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);
+ if (comments == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ 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);
+ if (comments == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ 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_show_page (void *abstract_surface)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ if (surface->clipper.clip != NULL)
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream,
+ "Q Q\n"
+ "showpage\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+color_is_gray (double red, double green, double blue)
+{
+ const double epsilon = 0.00001;
+
+ return (fabs (red - green) < epsilon &&
+ fabs (red - blue) < epsilon);
+}
+
+/**
+ * _cairo_ps_surface_acquire_source_surface_from_pattern:
+ * @surface: the ps surface
+ * @pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source
+ * @extents: extents of the operation that is using this source
+ * @width: returns width of surface
+ * @height: returns height of surface
+ * @x_offset: returns x offset of surface
+ * @y_offset: returns y offset of surface
+ * @surface: returns surface of type image surface or recording surface
+ * @image_extra: returns image extra for image type surface
+ *
+ * Acquire source surface or raster source pattern.
+ **/
+static cairo_status_t
+_cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *width,
+ int *height,
+ double *x_offset,
+ double *y_offset,
+ cairo_surface_t **source_surface,
+ void **image_extra)
+{
+ cairo_status_t status;
+ cairo_image_surface_t *image;
+
+ *x_offset = *y_offset = 0;
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_t *surf = ((cairo_surface_pattern_t *) pattern)->surface;
+
+ if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (surf->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surf;
+
+ *width = sub->extents.width;
+ *height = sub->extents.height;
+ } else {
+ cairo_surface_t *free_me = NULL;
+ cairo_recording_surface_t *recording_surface;
+ cairo_box_t bbox;
+ cairo_rectangle_int_t extents;
+
+ recording_surface = (cairo_recording_surface_t *) surf;
+ if (_cairo_surface_is_snapshot (&recording_surface->base)) {
+ free_me = _cairo_surface_snapshot_get_target (&recording_surface->base);
+ recording_surface = (cairo_recording_surface_t *) free_me;
+ }
+
+ status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
+ cairo_surface_destroy (free_me);
+ if (unlikely (status))
+ return status;
+
+ _cairo_box_round_to_rectangle (&bbox, &extents);
+ *width = extents.width;
+ *height = extents.height;
+ }
+ *source_surface = surf;
+
+ return CAIRO_STATUS_SUCCESS;
+ } else {
+ status = _cairo_surface_acquire_source_image (surf, &image, image_extra);
+ if (unlikely (status))
+ return status;
+ }
+ } break;
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
+ cairo_surface_t *surf;
+ cairo_box_t box;
+ cairo_rectangle_int_t rect;
+
+ /* get the operation extents in pattern space */
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
+ _cairo_box_round_to_rectangle (&box, &rect);
+ surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, &rect);
+ if (!surf)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ assert (_cairo_surface_is_image (surf));
+ image = (cairo_image_surface_t *) surf;
+ } break;
+
+ case CAIRO_PATTERN_TYPE_SOLID:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+
+ *width = image->width;
+ *height = image->height;
+ *source_surface = &image->base;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *source,
+ void *image_extra)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE: {
+ cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
+ if (surf_pat->surface->type != CAIRO_SURFACE_TYPE_RECORDING) {
+ cairo_image_surface_t *image = (cairo_image_surface_t *) source;
+ _cairo_surface_release_source_image (surf_pat->surface, image, image_extra);
+ }
+ } break;
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ _cairo_raster_source_pattern_release (pattern, source);
+ break;
+
+ case CAIRO_PATTERN_TYPE_SOLID:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ default:
+
+ ASSERT_NOT_REACHED;
+ break;
+ }
+}
+
+/**
+ * _cairo_ps_surface_create_padded_image_from_image:
+ * @surface: the ps surface
+ * @source: The source image
+ * @extents: extents of the operation that is using this source
+ * @width: returns width of padded image
+ * @height: returns height of padded image
+ * @x_offset: returns x offset of padded image
+ * @y_offset: returns y offset of padded image
+ * @image: returns the padded image or NULL if padding not required to fill @extents
+ *
+ * Creates a padded image if the source image does not fill the extents.
+ **/
+static cairo_status_t
+_cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t *surface,
+ cairo_image_surface_t *source,
+ const cairo_matrix_t *source_matrix,
+ const cairo_rectangle_int_t *extents,
+ int *width,
+ int *height,
+ double *x_offset,
+ double *y_offset,
+ cairo_image_surface_t **image)
+{
+ cairo_box_t box;
+ cairo_rectangle_int_t rect;
+ cairo_surface_t *pad_image;
+ cairo_surface_pattern_t pad_pattern;
+ int w, h;
+ cairo_int_status_t status;
+
+ /* get the operation extents in pattern space */
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_matrix_transform_bounding_box_fixed (source_matrix, &box, NULL);
+ _cairo_box_round_to_rectangle (&box, &rect);
+
+ /* Check if image needs padding to fill extents. */
+ w = source->width;
+ h = source->height;
+ if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
+ _cairo_fixed_integer_ceil(box.p1.y) < 0 ||
+ _cairo_fixed_integer_floor(box.p2.y) > w ||
+ _cairo_fixed_integer_floor(box.p2.y) > h)
+ {
+ pad_image =
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ source->pixman_format,
+ rect.width, rect.height,
+ 0);
+ if (pad_image->status) {
+ status = pad_image->status;
+ cairo_surface_destroy (pad_image);
+ status;
+ }
+
+ _cairo_pattern_init_for_surface (&pad_pattern, &source->base);
+ cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y);
+ pad_pattern.base.extend = CAIRO_EXTEND_PAD;
+ status = _cairo_surface_paint (pad_image,
+ CAIRO_OPERATOR_SOURCE,
+ &pad_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pad_pattern.base);
+ *image = (cairo_image_surface_t *) pad_image;
+ *width = rect.width;
+ *height = rect.height;
+ *x_offset = rect.x;
+ *y_offset = rect.y;
+ } else {
+ *image = NULL;
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ int width, height;
+ double x_offset, y_offset;
+ cairo_surface_t *source;
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_int_status_t status;
+ cairo_image_transparency_t transparency;
+
+ status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
+ pattern,
+ extents,
+ &width,
+ &height,
+ &x_offset,
+ &y_offset,
+ &source,
+ &image_extra);
+ if (unlikely (status))
+ return status;
+
+ image = (cairo_image_surface_t *) source;
+ 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_ps_surface_release_source_surface_from_pattern (surface, pattern, source, 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)
+{
+ double min_alpha, max_alpha;
+
+ if (surface->ps_level == CAIRO_PS_LEVEL_2)
+ return FALSE;
+
+ /* Alpha gradients are only supported (by flattening the alpha)
+ * if there is no variation in the alpha across the gradient. */
+ _cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha);
+ if (min_alpha != max_alpha)
+ return FALSE;
+
+ surface->ps_level_used = CAIRO_PS_LEVEL_3;
+
+ return TRUE;
+}
+
+static cairo_bool_t
+pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return TRUE;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _gradient_pattern_supported (surface, pattern);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return TRUE;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+mask_supported (cairo_ps_surface_t *surface,
+ const cairo_pattern_t *mask,
+ const cairo_rectangle_int_t *extents)
+{
+ if (surface->ps_level == CAIRO_PS_LEVEL_2)
+ return FALSE;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
+ if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ /* check if mask if opaque or bilevel alpha */
+ if (_cairo_ps_surface_analyze_surface_pattern_transparency (surface, mask, extents) == CAIRO_INT_STATUS_SUCCESS) {
+ surface->ps_level_used = CAIRO_PS_LEVEL_3;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_pattern_t *mask,
+ const cairo_rectangle_int_t *extents)
+{
+ double min_alpha;
+
+ if (surface->force_fallbacks &&
+ surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (! pattern_supported (surface, pattern))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Mask is only supported when the mask is an image with opaque or bilevel alpha. */
+ if (mask && !mask_supported (surface, mask, extents))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+ if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (pattern->extend == CAIRO_EXTEND_PAD) {
+ cairo_box_t box;
+ cairo_rectangle_int_t rect;
+ cairo_rectangle_int_t rec_extents;
+
+ /* get the operation extents in pattern space */
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
+ _cairo_box_round_to_rectangle (&box, &rect);
+
+ /* Check if surface needs padding to fill extents */
+ if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) {
+ if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x ||
+ _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y ||
+ _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width ||
+ _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+ return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+ }
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ if (mask)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ else
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
+ * the pattern contains transparency, we return
+ * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis
+ * surface. If the analysis surface determines that there is
+ * anything drawn under this operation, a fallback image will be
+ * used. Otherwise the operation will be replayed during the
+ * render stage and we blend the transparency into the white
+ * background to convert the pattern to opaque.
+ */
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE || pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, pattern, extents);
+
+ /* Patterns whose drawn part is opaque are directly supported;
+ those whose drawn part is partially transparent can be
+ supported by flattening the alpha. */
+ _cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
+ if (CAIRO_ALPHA_IS_OPAQUE (min_alpha))
+ return CAIRO_STATUS_SUCCESS;
+
+ return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+}
+
+static cairo_bool_t
+_cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_pattern_t *mask,
+ const cairo_rectangle_int_t *extents)
+{
+ return _cairo_ps_surface_analyze_operation (surface, op, pattern, mask, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/* The "standard" implementation limit for PostScript string sizes is
+ * 65535 characters (see PostScript Language Reference, Appendix
+ * B). 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)) {
+ status = opaque->status;
+ cairo_surface_destroy (opaque);
+ return status;
+ }
+
+ if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
+ status = _cairo_surface_paint (opaque,
+ CAIRO_OPERATOR_SOURCE,
+ &_cairo_pattern_white.base,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (opaque);
+ return status;
+ }
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, &image->base);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (opaque);
+ return status;
+ }
+
+ *opaque_image = (cairo_image_surface_t *) opaque;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface,
+ const unsigned char *data,
+ unsigned long length,
+ cairo_ps_compress_t compress,
+ cairo_bool_t use_strings)
+{
+ cairo_output_stream_t *base85_stream, *string_array_stream, *deflate_stream;
+ unsigned char *data_compressed;
+ unsigned long data_compressed_size;
+ cairo_status_t status, status2;
+
+ if (use_strings)
+ string_array_stream = _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);
+ }
+
+ switch (compress) {
+ case CAIRO_PS_COMPRESS_NONE:
+ _cairo_output_stream_write (base85_stream, data, length);
+ break;
+
+ case CAIRO_PS_COMPRESS_LZW:
+ /* XXX: Should fix cairo-lzw to provide a stream-based interface
+ * instead. */
+ data_compressed_size = length;
+ data_compressed = _cairo_lzw_compress ((unsigned char*)data, &data_compressed_size);
+ if (unlikely (data_compressed == NULL)) {
+ status = _cairo_output_stream_destroy (string_array_stream);
+ status = _cairo_output_stream_destroy (base85_stream);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ _cairo_output_stream_write (base85_stream, data_compressed, data_compressed_size);
+ free (data_compressed);
+ break;
+
+ case CAIRO_PS_COMPRESS_DEFLATE:
+ deflate_stream = _cairo_deflate_stream_create (base85_stream);
+ if (_cairo_output_stream_get_status (deflate_stream)) {
+ return _cairo_output_stream_destroy (deflate_stream);
+ }
+ _cairo_output_stream_write (deflate_stream, data, length);
+ status = _cairo_output_stream_destroy (deflate_stream);
+ if (unlikely (status)) {
+ status2 = _cairo_output_stream_destroy (string_array_stream);
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ return _cairo_output_stream_destroy (deflate_stream);
+ }
+ break;
+ }
+ status = _cairo_output_stream_destroy (base85_stream);
+
+ /* Mark end of base85 data */
+ _cairo_output_stream_printf (string_array_stream, "~>");
+ status2 = _cairo_output_stream_destroy (string_array_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
+ cairo_image_surface_t *image_surf,
+ cairo_operator_t op,
+ cairo_filter_t filter,
+ cairo_bool_t stencil_mask)
+{
+ cairo_status_t status;
+ unsigned char *data;
+ unsigned long data_size;
+ cairo_image_surface_t *ps_image;
+ int x, y, i, a;
+ cairo_image_transparency_t transparency;
+ cairo_bool_t use_mask;
+ uint32_t *pixel32;
+ uint8_t *pixel8;
+ int bit;
+ cairo_image_color_t color;
+ const char *interpolate;
+ cairo_ps_compress_t compress;
+ const char *compress_filter;
+ cairo_image_surface_t *image;
+
+ if (image_surf->base.status)
+ return image_surf->base.status;
+
+ image = image_surf;
+ if (image->format != CAIRO_FORMAT_RGB24 &&
+ image->format != CAIRO_FORMAT_ARGB32 &&
+ image->format != CAIRO_FORMAT_A8 &&
+ image->format != CAIRO_FORMAT_A1)
+ {
+ cairo_surface_t *surf;
+ cairo_surface_pattern_t pattern;
+
+ surf = _cairo_image_surface_create_with_content (image_surf->base.content,
+ image_surf->width,
+ image_surf->height);
+ image = (cairo_image_surface_t *) surf;
+ if (surf->status) {
+ status = surf->status;
+ goto bail0;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, &image_surf->base);
+ status = _cairo_surface_paint (surf,
+ CAIRO_OPERATOR_SOURCE, &pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status))
+ goto bail0;
+ }
+ ps_image = image;
+
+ 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;
+ }
+
+ if (stencil_mask) {
+ use_mask = FALSE;
+ color = CAIRO_IMAGE_IS_MONOCHROME;
+ transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+ } else {
+ transparency = _cairo_image_analyze_transparency (image);
+
+ /* PostScript can not represent the alpha channel, so we blend the
+ current image over a white (or black for CONTENT_COLOR
+ surfaces) RGB surface to eliminate it. */
+
+ if (op == CAIRO_OPERATOR_SOURCE ||
+ transparency == CAIRO_IMAGE_HAS_ALPHA ||
+ (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA &&
+ surface->ps_level == CAIRO_PS_LEVEL_2))
+ {
+ status = _cairo_ps_surface_flatten_image_transparency (surface,
+ image,
+ &ps_image);
+ if (unlikely (status))
+ return status;
+
+ use_mask = FALSE;
+ } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
+ use_mask = FALSE;
+ } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA */
+ use_mask = TRUE;
+ }
+
+ color = _cairo_image_analyze_color (ps_image);
+ }
+
+ /* Type 2 (mask and image interleaved) has the mask and image
+ * samples interleaved by row. The mask row is first, one bit per
+ * pixel with (bit 7 first). The row is padded to byte
+ * boundaries. The image data is 3 bytes per pixel RGB format. */
+ switch (color) {
+ default:
+ case CAIRO_IMAGE_UNKNOWN_COLOR:
+ ASSERT_NOT_REACHED;
+ case CAIRO_IMAGE_IS_COLOR:
+ data_size = ps_image->width * 3;
+ break;
+ case CAIRO_IMAGE_IS_GRAYSCALE:
+ data_size = ps_image->width;
+ break;
+ case CAIRO_IMAGE_IS_MONOCHROME:
+ data_size = (ps_image->width + 7)/8;
+ break;
+ }
+ if (use_mask)
+ data_size += (ps_image->width + 7)/8;
+ data_size *= ps_image->height;
+ data = malloc (data_size);
+ if (unlikely (data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail1;
+ }
+
+ i = 0;
+ for (y = 0; y < ps_image->height; y++) {
+ if (stencil_mask || use_mask) {
+ /* mask row */
+ if (ps_image->format == CAIRO_FORMAT_A1) {
+ pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
+
+ for (x = 0; x < (ps_image->width + 7) / 8; x++, pixel8++) {
+ a = *pixel8;
+ a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
+ data[i++] = a;
+ }
+ } else {
+ pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
+ pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
+ bit = 7;
+ for (x = 0; x < ps_image->width; x++) {
+ if (ps_image->format == CAIRO_FORMAT_ARGB32) {
+ a = (*pixel32 & 0xff000000) >> 24;
+ pixel32++;
+ } else {
+ a = *pixel8;
+ pixel8++;
+ }
+
+ if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
+ data[i++] = a;
+ } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
+ if (bit == 7)
+ data[i] = 0;
+ if (a != 0)
+ data[i] |= (1 << bit);
+ bit--;
+ if (bit < 0) {
+ bit = 7;
+ i++;
+ }
+ }
+ }
+ if (bit != 7)
+ i++;
+ }
+ }
+ if (stencil_mask)
+ continue;
+
+ /* image row*/
+ pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
+ bit = 7;
+ for (x = 0; x < ps_image->width; x++, pixel32++) {
+ int r, g, b;
+
+ if (ps_image->format == CAIRO_FORMAT_ARGB32) {
+ /* At this point ARGB32 images are either opaque or
+ * bilevel alpha so we don't need to unpremultiply. */
+ if (((*pixel32 & 0xff000000) >> 24) == 0) {
+ r = g = b = 0;
+ } else {
+ r = (*pixel32 & 0x00ff0000) >> 16;
+ g = (*pixel32 & 0x0000ff00) >> 8;
+ b = (*pixel32 & 0x000000ff) >> 0;
+ }
+ } else if (ps_image->format == CAIRO_FORMAT_RGB24) {
+ r = (*pixel32 & 0x00ff0000) >> 16;
+ g = (*pixel32 & 0x0000ff00) >> 8;
+ b = (*pixel32 & 0x000000ff) >> 0;
+ } else {
+ r = g = b = 0;
+ }
+
+ switch (color) {
+ case CAIRO_IMAGE_IS_COLOR:
+ case CAIRO_IMAGE_UNKNOWN_COLOR:
+ data[i++] = r;
+ data[i++] = g;
+ data[i++] = b;
+ break;
+
+ case CAIRO_IMAGE_IS_GRAYSCALE:
+ data[i++] = r;
+ break;
+
+ case CAIRO_IMAGE_IS_MONOCHROME:
+ if (bit == 7)
+ data[i] = 0;
+ if (r != 0)
+ data[i] |= (1 << bit);
+ bit--;
+ if (bit < 0) {
+ bit = 7;
+ i++;
+ }
+ break;
+ }
+ }
+ if (bit != 7)
+ i++;
+ }
+
+ if (surface->ps_level == CAIRO_PS_LEVEL_2) {
+ compress = CAIRO_PS_COMPRESS_LZW;
+ compress_filter = "LZWDecode";
+ } else {
+ compress = CAIRO_PS_COMPRESS_DEFLATE;
+ compress_filter = "FlateDecode";
+ surface->ps_level_used = CAIRO_PS_LEVEL_3;
+ }
+
+ if (surface->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,
+ data_size,
+ compress,
+ TRUE);
+ if (unlikely (status))
+ goto bail2;
+
+ _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,
+ "%s 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 %d def\n"
+ " /Decode [ %s ] def\n",
+ color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+ ps_image->width,
+ ps_image->height,
+ interpolate,
+ color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8,
+ color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1");
+
+ if (surface->use_string_datasource) {
+ _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 /%s filter def\n",
+ compress_filter);
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource currentfile /ASCII85Decode filter /%s filter def\n",
+ compress_filter);
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+ " end\n"
+ " /MaskDict 8 dict def\n"
+ " MaskDict begin\n"
+ " /ImageType 1 def\n"
+ " /Width %d def\n"
+ " /Height %d def\n"
+ " /Interpolate %s def\n"
+ " /BitsPerComponent 1 def\n"
+ " /Decode [ 1 0 ] def\n"
+ " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+ " end\n"
+ "end\n"
+ "image\n",
+ ps_image->height,
+ ps_image->width,
+ ps_image->height,
+ interpolate,
+ ps_image->height);
+ } else {
+ if (!stencil_mask) {
+ _cairo_output_stream_printf (surface->stream,
+ "%s setcolorspace\n",
+ color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray");
+ }
+ _cairo_output_stream_printf (surface->stream,
+ "8 dict dup begin\n"
+ " /ImageType 1 def\n"
+ " /Width %d def\n"
+ " /Height %d def\n"
+ " /Interpolate %s def\n"
+ " /BitsPerComponent %d def\n"
+ " /Decode [ %s ] def\n",
+ ps_image->width,
+ ps_image->height,
+ interpolate,
+ color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8,
+ stencil_mask ? "1 0" : color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1");
+ 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 /%s filter def\n",
+ compress_filter);
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource currentfile /ASCII85Decode filter /%s filter def\n",
+ compress_filter);
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " /Interpolate %s def\n"
+ " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+ "end\n"
+ "%s\n",
+ interpolate,
+ ps_image->height,
+ stencil_mask ? "imagemask" : "image");
+ }
+
+ 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,
+ data_size,
+ compress,
+ FALSE);
+ _cairo_output_stream_printf (surface->stream, "\n");
+ } else {
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+bail2:
+ free (data);
+
+bail1:
+ if (!use_mask && ps_image != image)
+ cairo_surface_destroy (&ps_image->base);
+
+bail0:
+ if (image != image_surf)
+ cairo_surface_destroy (&image->base);
+
+ 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;
+ const char *colorspace;
+ const char *decode;
+
+ 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;
+
+ switch (info.num_components) {
+ case 1:
+ colorspace = "/DeviceGray";
+ decode = "0 1";
+ break;
+ case 3:
+ colorspace = "/DeviceRGB";
+ decode = "0 1 0 1 0 1";
+ break;
+ case 4:
+ colorspace = "/DeviceCMYK";
+ decode = "0 1 0 1 0 1 0 1";
+ break;
+ default:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (surface->use_string_datasource) {
+ /* 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,
+ CAIRO_PS_COMPRESS_NONE,
+ 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 [ %s ] def\n",
+ colorspace,
+ info.width,
+ info.height,
+ info.bits_per_component,
+ decode);
+
+ 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,
+ CAIRO_PS_COMPRESS_NONE,
+ FALSE);
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
+ cairo_surface_t *recording_surface)
+{
+ 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_surface_t *free_me = NULL;
+ cairo_surface_clipper_t old_clipper;
+ cairo_box_t bbox;
+ cairo_int_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;
+ old_clipper = surface->clipper;
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_ps_surface_clipper_intersect_clip_path);
+
+ if (_cairo_surface_is_snapshot (recording_surface))
+ free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
+
+ status =
+ _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+ &bbox,
+ NULL);
+ if (unlikely (status))
+ goto err;
+
+#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))
+ goto err;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto err;
+
+ _cairo_output_stream_printf (surface->stream, " Q\n");
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+ surface->clipper = old_clipper;
+ surface->content = old_content;
+ surface->width = old_width;
+ surface->height = old_height;
+ surface->page_bbox = old_page_bbox;
+ surface->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);
+
+err:
+ cairo_surface_destroy (free_me);
+ return status;
+}
+
+static cairo_int_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_surface_clipper_t old_clipper;
+ cairo_surface_t *free_me = NULL;
+ cairo_int_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;
+ old_clipper = surface->clipper;
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_ps_surface_clipper_intersect_clip_path);
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n",
+ extents->x, extents->y,
+ extents->width, extents->height);
+#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 (_cairo_surface_is_snapshot (recording_surface))
+ free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
+
+ 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))
+ goto err;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto err;
+
+ _cairo_output_stream_printf (surface->stream, " Q\n");
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+ surface->clipper = old_clipper;
+ surface->content = old_content;
+ surface->width = old_width;
+ surface->height = old_height;
+ surface->page_bbox = old_page_bbox;
+ surface->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);
+
+err:
+ cairo_surface_destroy (free_me);
+ return status;
+}
+
+static void
+_cairo_ps_surface_flatten_transparency (cairo_ps_surface_t *surface,
+ const cairo_color_t *color,
+ double *red,
+ double *green,
+ double *blue)
+{
+ *red = color->red;
+ *green = color->green;
+ *blue = color->blue;
+
+ if (! CAIRO_COLOR_IS_OPAQUE (color)) {
+ *red *= color->alpha;
+ *green *= color->alpha;
+ *blue *= color->alpha;
+ if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
+ double one_minus_alpha = 1. - color->alpha;
+ *red += one_minus_alpha;
+ *green += one_minus_alpha;
+ *blue += one_minus_alpha;
+ }
+ }
+}
+
+static void
+_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface,
+ cairo_solid_pattern_t *pattern)
+{
+ double red, green, blue;
+
+ _cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue);
+
+ if (color_is_gray (red, green, blue))
+ _cairo_output_stream_printf (surface->stream,
+ "%f g\n",
+ red);
+ else
+ _cairo_output_stream_printf (surface->stream,
+ "%f %f %f rg\n",
+ red, green, blue);
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
+ cairo_pattern_t *source_pattern,
+ cairo_surface_t *source_surface,
+ cairo_operator_t op,
+ int width,
+ int height,
+ cairo_bool_t stencil_mask)
+{
+ cairo_int_status_t status;
+
+ if (source_surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (source_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source_surface;
+ status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents);
+ } else {
+ status = _cairo_ps_surface_emit_recording_surface (surface, source_surface);
+ }
+ } else {
+ cairo_image_surface_t *image = (cairo_image_surface_t *) source_surface;
+ if (source_pattern->extend != CAIRO_EXTEND_PAD) {
+ status = _cairo_ps_surface_emit_jpeg_image (surface, source_surface,
+ width, height);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = _cairo_ps_surface_emit_image (surface, image,
+ op, source_pattern->filter, stencil_mask);
+ }
+
+ return status;
+}
+
+
+static void
+_path_fixed_init_rectangle (cairo_path_fixed_t *path,
+ cairo_rectangle_int_t *rect)
+{
+ cairo_status_t status;
+
+ _cairo_path_fixed_init (path);
+
+ status = _cairo_path_fixed_move_to (path,
+ _cairo_fixed_from_int (rect->x),
+ _cairo_fixed_from_int (rect->y));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (path,
+ _cairo_fixed_from_int (rect->width),
+ _cairo_fixed_from_int (0));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (path,
+ _cairo_fixed_from_int (0),
+ _cairo_fixed_from_int (rect->height));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (path,
+ _cairo_fixed_from_int (-rect->width),
+ _cairo_fixed_from_int (0));
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ status = _cairo_path_fixed_close_path (path);
+ assert (status == CAIRO_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+_cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface,
+ cairo_pattern_t *pattern,
+ cairo_rectangle_int_t *extents,
+ cairo_operator_t op,
+ cairo_bool_t stencil_mask)
+{
+ cairo_status_t status;
+ int width, height;
+ cairo_matrix_t cairo_p2d, ps_p2d;
+ cairo_path_fixed_t path;
+ double x_offset, y_offset;
+ cairo_surface_t *source;
+ cairo_image_surface_t *image = NULL;
+ void *image_extra;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
+ pattern,
+ extents,
+ &width, &height,
+ &x_offset, &y_offset,
+ &source,
+ &image_extra);
+ if (unlikely (status))
+ return status;
+
+ if (pattern->extend == CAIRO_EXTEND_PAD &&
+ pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ ((cairo_surface_pattern_t *)pattern)->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_image_surface_t *img;
+
+ img = (cairo_image_surface_t *) source;
+ status = _cairo_ps_surface_create_padded_image_from_image (surface,
+ img,
+ &pattern->matrix,
+ extents,
+ &width, &height,
+ &x_offset, &y_offset,
+ &image);
+ if (unlikely (status))
+ goto release_source;
+ }
+
+ _path_fixed_init_rectangle (&path, extents);
+ status = _cairo_pdf_operators_clip (&surface->pdf_operators,
+ &path,
+ CAIRO_FILL_RULE_WINDING);
+ _cairo_path_fixed_fini (&path);
+ if (unlikely (status))
+ goto release_source;
+
+ cairo_p2d = pattern->matrix;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ double x_scale = cairo_p2d.xx;
+ double y_scale = cairo_p2d.yy;
+
+ _cairo_output_stream_printf (surface->stream,
+ "%% Fallback Image: x=%f y=%f w=%d h=%d ",
+ -cairo_p2d.x0/x_scale,
+ -cairo_p2d.y0/y_scale,
+ (int)(width/x_scale),
+ (int)(height/y_scale));
+ if (x_scale == y_scale) {
+ _cairo_output_stream_printf (surface->stream,
+ "res=%fppi ",
+ x_scale*72);
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ "res=%fx%fppi ",
+ x_scale*72,
+ y_scale*72);
+ }
+ _cairo_output_stream_printf (surface->stream,
+ "size=%ld\n",
+ (long)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, x_offset, y_offset);
+ 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,
+ image ? &image->base : source,
+ op,
+ width, height,
+ stencil_mask);
+
+ release_source:
+ if (image)
+ cairo_surface_destroy (&image->base);
+
+ _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface,
+ cairo_pattern_t *pattern,
+ cairo_rectangle_int_t *extents,
+ cairo_operator_t op)
+{
+ cairo_status_t status;
+ 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;
+ double x_offset, y_offset;
+ cairo_surface_t *source;
+ cairo_image_surface_t *image = NULL;
+ void *image_extra;
+
+ cairo_p2d = pattern->matrix;
+ status = cairo_matrix_invert (&cairo_p2d);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
+ pattern,
+ extents,
+ &pattern_width, &pattern_height,
+ &x_offset, &y_offset,
+ &source,
+ &image_extra);
+ if (unlikely (status))
+ return status;
+
+ if (pattern->extend == CAIRO_EXTEND_PAD) {
+ cairo_image_surface_t *img;
+
+ assert (source->type == CAIRO_SURFACE_TYPE_IMAGE);
+ img = (cairo_image_surface_t *) source;
+ status = _cairo_ps_surface_create_padded_image_from_image (surface,
+ img,
+ &pattern->matrix,
+ extents,
+ &pattern_width, &pattern_height,
+ &x_offset, &y_offset,
+ &image);
+ if (unlikely (status))
+ goto release_source;
+ }
+
+ switch (pattern->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->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,
+ image ? &image->base : source,
+ op,
+ pattern_width, pattern_height, FALSE);
+ if (unlikely (status))
+ goto release_source;
+
+ 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->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->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");
+
+ release_source:
+ if (image)
+ cairo_surface_destroy (&image->base);
+
+ _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra);
+
+ return status;
+}
+
+typedef struct _cairo_ps_color_stop {
+ double offset;
+ double color[4];
+} cairo_ps_color_stop_t;
+
+static void
+_cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t *surface,
+ cairo_ps_color_stop_t *stop1,
+ cairo_ps_color_stop_t *stop2)
+{
+ _cairo_output_stream_printf (surface->stream,
+ " << /FunctionType 2\n"
+ " /Domain [ 0 1 ]\n"
+ " /C0 [ %f %f %f ]\n"
+ " /C1 [ %f %f %f ]\n"
+ " /N 1\n"
+ " >>\n",
+ stop1->color[0],
+ stop1->color[1],
+ stop1->color[2],
+ stop2->color[0],
+ stop2->color[1],
+ stop2->color[2]);
+}
+
+static void
+_cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t *surface,
+ unsigned int n_stops,
+ cairo_ps_color_stop_t stops[])
+{
+ unsigned int i;
+
+ _cairo_output_stream_printf (surface->stream,
+ "<< /FunctionType 3\n"
+ " /Domain [ 0 1 ]\n"
+ " /Functions [\n");
+ for (i = 0; i < n_stops - 1; i++)
+ _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]);
+
+ _cairo_output_stream_printf (surface->stream, " ]\n");
+
+ _cairo_output_stream_printf (surface->stream, " /Bounds [ ");
+ for (i = 1; i < n_stops-1; i++)
+ _cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset);
+ _cairo_output_stream_printf (surface->stream, "]\n");
+
+ _cairo_output_stream_printf (surface->stream, " /Encode [ 1 1 %d { pop 0 1 } for ]\n",
+ n_stops - 1);
+
+ _cairo_output_stream_printf (surface->stream, ">>\n");
+}
+
+static void
+calc_gradient_color (cairo_ps_color_stop_t *new_stop,
+ cairo_ps_color_stop_t *stop1,
+ cairo_ps_color_stop_t *stop2)
+{
+ int i;
+ double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset);
+
+ for (i = 0; i < 4; i++)
+ new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]);
+}
+
+#define COLOR_STOP_EPSILON 1e-6
+
+static cairo_status_t
+_cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface,
+ cairo_gradient_pattern_t *pattern)
+{
+ cairo_ps_color_stop_t *allstops, *stops;
+ unsigned int i, n_stops;
+
+ allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t));
+ if (unlikely (allstops == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ stops = &allstops[1];
+ n_stops = pattern->n_stops;
+
+ for (i = 0; i < n_stops; i++) {
+ cairo_gradient_stop_t *stop = &pattern->stops[i];
+
+ stops[i].color[0] = stop->color.red;
+ stops[i].color[1] = stop->color.green;
+ stops[i].color[2] = stop->color.blue;
+ stops[i].color[3] = stop->color.alpha;
+ stops[i].offset = pattern->stops[i].offset;
+ }
+
+ if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.extend == CAIRO_EXTEND_REFLECT)
+ {
+ if (stops[0].offset > COLOR_STOP_EPSILON) {
+ if (pattern->base.extend == CAIRO_EXTEND_REFLECT)
+ memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t));
+ else
+ calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]);
+ stops = allstops;
+ n_stops++;
+ }
+ stops[0].offset = 0.0;
+
+ if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) {
+ if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+ memcpy (&stops[n_stops],
+ &stops[n_stops - 1],
+ sizeof (cairo_ps_color_stop_t));
+ } else {
+ calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]);
+ }
+ n_stops++;
+ }
+ stops[n_stops-1].offset = 1.0;
+ }
+
+ for (i = 0; i < n_stops; i++) {
+ double red, green, blue;
+ cairo_color_t color;
+
+ _cairo_color_init_rgba (&color,
+ stops[i].color[0],
+ stops[i].color[1],
+ stops[i].color[2],
+ stops[i].color[3]);
+ _cairo_ps_surface_flatten_transparency (surface, &color,
+ &red, &green, &blue);
+ stops[i].color[0] = red;
+ stops[i].color[1] = green;
+ stops[i].color[2] = blue;
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ "/CairoFunction\n");
+ if (stops[0].offset == stops[n_stops - 1].offset) {
+ /*
+ * The first and the last stops have the same offset, but we
+ * don't want a function with an empty domain, because that
+ * would provoke underdefined behaviour from rasterisers.
+ * This can only happen with EXTEND_PAD, because EXTEND_NONE
+ * is optimised into a clear pattern in cairo-gstate, and
+ * REFLECT/REPEAT are always transformed to have the first
+ * stop at t=0 and the last stop at t=1. Thus we want a step
+ * function going from the first color to the last one.
+ *
+ * This can be accomplished by stitching three functions:
+ * - a constant first color function,
+ * - a step from the first color to the last color (with empty domain)
+ * - a constant last color function
+ */
+ cairo_ps_color_stop_t pad_stops[4];
+
+ assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+
+ pad_stops[0] = pad_stops[1] = stops[0];
+ pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
+
+ pad_stops[0].offset = 0;
+ pad_stops[3].offset = 1;
+
+ _cairo_ps_surface_emit_stitched_colorgradient (surface, 4, pad_stops);
+ } else if (n_stops == 2) {
+ /* no need for stitched function */
+ _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]);
+ } else {
+ /* multiple stops: stitch. XXX possible optimization: regulary spaced
+ * stops do not require stitching. XXX */
+ _cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops, stops);
+ }
+ _cairo_output_stream_printf (surface->stream,
+ "def\n");
+
+ free (allstops);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t *surface,
+ cairo_gradient_pattern_t *pattern,
+ int begin,
+ int end)
+{
+ _cairo_output_stream_printf (surface->stream,
+ "/CairoFunction\n"
+ "<< /FunctionType 3\n"
+ " /Domain [ %d %d ]\n"
+ " /Functions [ %d {CairoFunction} repeat ]\n"
+ " /Bounds [ %d 1 %d {} for ]\n",
+ begin,
+ end,
+ end - begin,
+ begin + 1,
+ end - 1);
+
+ if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+ _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { 2 mod 0 eq {0 1} {1 0} ifelse } for ]\n",
+ begin,
+ end - 1);
+ } else {
+ _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { pop 0 1 } for ]\n",
+ begin,
+ end - 1);
+ }
+
+ _cairo_output_stream_printf (surface->stream, ">> def\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_gradient (cairo_ps_surface_t *surface,
+ cairo_gradient_pattern_t *pattern,
+ cairo_bool_t is_ps_pattern)
+{
+ cairo_matrix_t pat_to_ps;
+ cairo_circle_double_t start, end;
+ double domain[2];
+ cairo_status_t status;
+
+ assert (pattern->n_stops != 0);
+
+ status = _cairo_ps_surface_emit_pattern_stops (surface, pattern);
+ if (unlikely (status))
+ return status;
+
+ pat_to_ps = pattern->base.matrix;
+ status = cairo_matrix_invert (&pat_to_ps);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+ cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+
+ if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.extend == CAIRO_EXTEND_REFLECT)
+ {
+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+ double x_scale, y_scale, tolerance;
+
+ /* TODO: use tighter extents */
+ bounds_x1 = 0;
+ bounds_y1 = 0;
+ bounds_x2 = surface->width;
+ bounds_y2 = surface->height;
+ _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+ &bounds_x1, &bounds_y1,
+ &bounds_x2, &bounds_y2,
+ NULL);
+
+ x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution;
+ y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution;
+
+ tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix));
+ tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1);
+ tolerance *= MIN (x_scale, y_scale);
+
+ _cairo_gradient_pattern_box_to_parameter (pattern,
+ bounds_x1, bounds_y1,
+ bounds_x2, bounds_y2,
+ tolerance, domain);
+ } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
+ /*
+ * If the first and the last stop offset are the same, then
+ * the color function is a step function.
+ * _cairo_ps_surface_emit_pattern_stops emits it as a stitched
+ * function no matter how many stops the pattern has. The
+ * domain of the stitched function will be [0 1] in this case.
+ *
+ * This is done to avoid emitting degenerate gradients for
+ * EXTEND_PAD patterns having a step color function.
+ */
+ domain[0] = 0.0;
+ domain[1] = 1.0;
+
+ assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+ } else {
+ domain[0] = pattern->stops[0].offset;
+ domain[1] = pattern->stops[pattern->n_stops - 1].offset;
+ }
+
+ /* PS requires the first and last stop to be the same as the
+ * extreme coordinates. For repeating patterns this moves the
+ * extreme coordinates out to the begin/end of the repeating
+ * function. For non repeating patterns this may move the extreme
+ * coordinates in if there are not stops at offset 0 and 1. */
+ _cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
+ _cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
+
+ if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.extend == CAIRO_EXTEND_REFLECT)
+ {
+ int repeat_begin, repeat_end;
+
+ repeat_begin = floor (domain[0]);
+ repeat_end = ceil (domain[1]);
+
+ status = _cairo_ps_surface_emit_repeating_function (surface,
+ pattern,
+ repeat_begin,
+ repeat_end);
+ if (unlikely (status))
+ return status;
+ } else if (pattern->n_stops <= 2) {
+ /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+ * Type 2 function is used by itself without a stitching
+ * function. Type 2 functions always have the domain [0 1] */
+ domain[0] = 0.0;
+ domain[1] = 1.0;
+ }
+
+ if (is_ps_pattern) {
+ _cairo_output_stream_printf (surface->stream,
+ "<< /PatternType 2\n"
+ " /Shading\n");
+ }
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ _cairo_output_stream_printf (surface->stream,
+ " << /ShadingType 2\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /Coords [ %f %f %f %f ]\n",
+ start.center.x, start.center.y,
+ end.center.x, end.center.y);
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " << /ShadingType 3\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /Coords [ %f %f %f %f %f %f ]\n",
+ start.center.x, start.center.y,
+ MAX (start.radius, 0),
+ end.center.x, end.center.y,
+ MAX (end.radius, 0));
+ }
+
+ if (pattern->base.extend != CAIRO_EXTEND_NONE) {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ false false ]\n");
+ }
+
+ if (domain[0] == 0.0 && domain[1] == 1.0) {
+ _cairo_output_stream_printf (surface->stream,
+ " /Function CairoFunction\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /Function <<\n"
+ " /FunctionType 3\n"
+ " /Domain [ 0 1 ]\n"
+ " /Bounds [ ]\n"
+ " /Encode [ %f %f ]\n"
+ " /Functions [ CairoFunction ]\n"
+ " >>\n",
+ domain[0], domain[1]);
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " >>\n");
+
+ if (is_ps_pattern) {
+ _cairo_output_stream_printf (surface->stream,
+ ">>\n"
+ "[ %f %f %f %f %f %f ]\n"
+ "makepattern setpattern\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);
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ "shfill\n");
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_mesh_pattern (cairo_ps_surface_t *surface,
+ cairo_mesh_pattern_t *pattern,
+ cairo_bool_t is_ps_pattern)
+{
+ cairo_matrix_t pat_to_ps;
+ cairo_status_t status;
+ cairo_pdf_shading_t shading;
+ int i;
+
+ if (_cairo_array_num_elements (&pattern->patches) == 0)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ pat_to_ps = pattern->base.matrix;
+ status = cairo_matrix_invert (&pat_to_ps);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+
+ status = _cairo_pdf_shading_init_color (&shading, pattern);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream,
+ "currentfile\n"
+ "/ASCII85Decode filter /FlateDecode filter /ReusableStreamDecode filter\n");
+
+ status = _cairo_ps_surface_emit_base85_string (surface,
+ shading.data,
+ shading.data_length,
+ CAIRO_PS_COMPRESS_DEFLATE,
+ FALSE);
+ if (status)
+ return status;
+
+ _cairo_output_stream_printf (surface->stream,
+ "\n"
+ "/CairoData exch def\n");
+
+ if (is_ps_pattern) {
+ _cairo_output_stream_printf (surface->stream,
+ "<< /PatternType 2\n"
+ " /Shading\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " << /ShadingType %d\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /DataSource CairoData\n"
+ " /BitsPerCoordinate %d\n"
+ " /BitsPerComponent %d\n"
+ " /BitsPerFlag %d\n"
+ " /Decode [",
+ shading.shading_type,
+ shading.bits_per_coordinate,
+ shading.bits_per_component,
+ shading.bits_per_flag);
+
+ for (i = 0; i < shading.decode_array_length; i++)
+ _cairo_output_stream_printf (surface->stream, "%f ", shading.decode_array[i]);
+
+ _cairo_output_stream_printf (surface->stream,
+ "]\n"
+ " >>\n");
+
+ if (is_ps_pattern) {
+ _cairo_output_stream_printf (surface->stream,
+ ">>\n"
+ "[ %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\n"
+ "setpattern\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream, "shfill\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ "currentdict /CairoData undef\n");
+
+ _cairo_pdf_shading_fini (&shading);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ cairo_rectangle_int_t *extents,
+ cairo_operator_t op)
+{
+ cairo_status_t status;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+
+ if (surface->current_pattern_is_solid_color == FALSE ||
+ ! _cairo_color_equal (&surface->current_color, &solid->color))
+ {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
+
+ surface->current_pattern_is_solid_color = TRUE;
+ surface->current_color = solid->color;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ surface->current_pattern_is_solid_color = FALSE;
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+
+ _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
+ break;
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ status = _cairo_ps_surface_emit_surface_pattern (surface,
+ (cairo_pattern_t *)pattern,
+ extents,
+ op);
+ if (unlikely (status))
+ return status;
+ break;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _cairo_ps_surface_emit_gradient (surface,
+ (cairo_gradient_pattern_t *) pattern,
+ TRUE);
+ if (unlikely (status))
+ return status;
+ break;
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ status = _cairo_ps_surface_emit_mesh_pattern (surface,
+ (cairo_mesh_pattern_t *) pattern,
+ TRUE);
+ if (unlikely (status))
+ return status;
+ break;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_paint_gradient (cairo_ps_surface_t *surface,
+ const cairo_pattern_t *source,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_matrix_t pat_to_ps;
+ cairo_status_t status;
+
+ pat_to_ps = source->matrix;
+ status = cairo_matrix_invert (&pat_to_ps);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+ cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+
+ if (! _cairo_matrix_is_identity (&pat_to_ps)) {
+ _cairo_output_stream_printf (surface->stream,
+ "[%f %f %f %f %f %f] concat\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);
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_MESH) {
+ status = _cairo_ps_surface_emit_mesh_pattern (surface,
+ (cairo_mesh_pattern_t *)source,
+ FALSE);
+ if (unlikely (status))
+ return status;
+ } else {
+ status = _cairo_ps_surface_emit_gradient (surface,
+ (cairo_gradient_pattern_t *)source,
+ FALSE);
+ if (unlikely (status))
+ return status;
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface,
+ const cairo_pattern_t *source,
+ cairo_rectangle_int_t *extents,
+ cairo_operator_t op,
+ cairo_bool_t stencil_mask)
+{
+ switch (source->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _cairo_ps_surface_paint_surface (surface,
+ (cairo_pattern_t *)source,
+ extents,
+ op,
+ stencil_mask);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _cairo_ps_surface_paint_gradient (surface,
+ source,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_SOLID:
+ default:
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+ }
+}
+
+static cairo_bool_t
+_can_paint_pattern (const cairo_pattern_t *pattern)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return FALSE;
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return (pattern->extend == CAIRO_EXTEND_NONE ||
+ pattern->extend == CAIRO_EXTEND_PAD);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ return TRUE;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+_cairo_ps_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+
+ /* 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);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_set_clip (cairo_ps_surface_t *surface,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_clip_t *clip = composite->clip;
+
+ if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
+ clip = NULL;
+
+ if (clip == NULL) {
+ if (_cairo_composite_rectangles_can_reduce_clip (composite,
+ surface->clipper.clip))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *stream = surface->stream;
+ cairo_composite_rectangles_t extents;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ &surface->base,
+ op, source, clip);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+ goto cleanup_composite;
+ }
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (stream,
+ "%% _cairo_ps_surface_paint\n");
+#endif
+
+ status = _cairo_ps_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ if (_can_paint_pattern (source)) {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ _cairo_output_stream_printf (stream, "q\n");
+ status = _cairo_ps_surface_paint_pattern (surface,
+ source,
+ &extents.bounded, op, FALSE);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ _cairo_output_stream_printf (stream, "Q\n");
+ } else {
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ _cairo_output_stream_printf (stream, "0 0 %f %f rectfill\n",
+ surface->width, surface->height);
+ }
+
+cleanup_composite:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *stream = surface->stream;
+ cairo_composite_rectangles_t extents;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ &surface->base,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_ps_surface_analyze_operation (surface, op, source, mask, &extents.bounded);
+ goto cleanup_composite;
+ }
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, mask, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (stream,
+ "%% _cairo_ps_surface_mask\n");
+#endif
+
+ status = _cairo_ps_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ _cairo_output_stream_printf (stream, "q\n");
+ status = _cairo_ps_surface_paint_pattern (surface,
+ mask,
+ &extents.bounded, op, TRUE);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ _cairo_output_stream_printf (stream, "Q\n");
+
+cleanup_composite:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &surface->base,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ /* use the more accurate extents */
+ {
+ cairo_rectangle_int_t r;
+ cairo_box_t b;
+
+ status = _cairo_path_fixed_stroke_extents (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &r);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ _cairo_box_from_rectangle (&b, &r);
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b);
+ if (unlikely (status))
+ goto cleanup_composite;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+ goto cleanup_composite;
+ }
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_stroke\n");
+#endif
+
+ status = _cairo_ps_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
+ path,
+ style,
+ ctm,
+ ctm_inverse);
+
+cleanup_composite:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t*path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &surface->base,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ /* use the more accurate extents */
+ {
+ cairo_rectangle_int_t r;
+ cairo_box_t b;
+
+ _cairo_path_fixed_fill_extents (path,
+ fill_rule,
+ tolerance,
+ &r);
+
+ _cairo_box_from_rectangle (&b, &r);
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b);
+ if (unlikely (status))
+ goto cleanup_composite;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+ goto cleanup_composite;
+ }
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_fill\n");
+#endif
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_ps_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ if (_can_paint_pattern (source)) {
+ _cairo_output_stream_printf (surface->stream, "q\n");
+
+ status = _cairo_pdf_operators_clip (&surface->pdf_operators,
+ path,
+ fill_rule);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_ps_surface_paint_pattern (surface,
+ source,
+ &extents.bounded, op, FALSE);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ _cairo_output_stream_printf (surface->stream, "Q\n");
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+ } else {
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+ path,
+ fill_rule);
+ }
+
+cleanup_composite:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static cairo_bool_t
+_cairo_ps_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_bool_t overlap;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ &surface->base,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+ goto cleanup_composite;
+ }
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_show_glyphs\n");
+#endif
+
+ status = _cairo_ps_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ goto cleanup_composite;
+
+ status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font);
+
+cleanup_composite:
+ _cairo_composite_rectangles_fini (&extents);
+ return status;
+}
+
+static const char **
+_cairo_ps_surface_get_supported_mime_types (void *abstract_surface)
+{
+ return _cairo_ps_supported_mime_types;
+}
+
+static 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 != 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, has_page_bbox;
+ const char *page_media;
+
+ 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));
+
+ 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;
+ has_page_bbox = FALSE;
+ num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
+ comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
+ if (comments == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+
+ for (i = 0; i < num_comments; i++) {
+ _cairo_output_stream_printf (surface->stream,
+ "%s\n", comments[i]);
+ if (strncmp (comments[i], "%%PageMedia:", 11) == 0)
+ has_page_media = TRUE;
+
+ if (strncmp (comments[i], "%%PageBoundingBox:", 18) == 0)
+ has_page_bbox = TRUE;
+
+ free (comments[i]);
+ comments[i] = NULL;
+ }
+ _cairo_array_truncate (&surface->dsc_page_setup_comments, 0);
+
+ if (!has_page_media && !surface->eps) {
+ page_media = _cairo_ps_surface_get_page_media (surface);
+ if (unlikely (page_media == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%PageMedia: %s\n",
+ page_media);
+ }
+
+ if (!has_page_bbox) {
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%PageBoundingBox: %d %d %d %d\n",
+ 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,
+ _cairo_ps_surface_finish,
+
+ _cairo_default_context_create,
+
+ NULL, /* create similar: handled by wrapper */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* cairo_ps_surface_copy_page */
+ _cairo_ps_surface_show_page,
+
+ _cairo_ps_surface_get_extents,
+ _cairo_ps_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ /* Here are the drawing functions */
+
+ _cairo_ps_surface_paint, /* paint */
+ _cairo_ps_surface_mask,
+ _cairo_ps_surface_stroke,
+ _cairo_ps_surface_fill,
+ NULL, /* fill-stroke */
+ NULL, /* show_glyphs */
+ _cairo_ps_surface_has_show_text_glyphs,
+ _cairo_ps_surface_show_text_glyphs,
+ _cairo_ps_surface_get_supported_mime_types,
+};
+
+static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = {
+ _cairo_ps_surface_start_page,
+ _cairo_ps_surface_set_paginated_mode,
+ _cairo_ps_surface_set_bounding_box,
+ NULL, /* _cairo_ps_surface_has_fallback_images, */
+ _cairo_ps_surface_supports_fine_grained_fallbacks,
+};
diff --git a/src/cairo-ps.h b/src/cairo-ps.h
new file mode 100755
index 000000000..33d0e0d94
--- /dev/null
+++ b/src/cairo-ps.h
@@ -0,0 +1,116 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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. (Since 1.6)
+ * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification. (Since 1.6)
+ *
+ * #cairo_ps_level_t is used to describe the language level of the
+ * PostScript Language Reference that a generated PostScript file will
+ * conform to.
+ *
+ * Since: 1.6
+ **/
+typedef enum _cairo_ps_level {
+ CAIRO_PS_LEVEL_2,
+ CAIRO_PS_LEVEL_3
+} cairo_ps_level_t;
+
+cairo_public cairo_surface_t *
+cairo_ps_surface_create (const char *filename,
+ double width_in_points,
+ double height_in_points);
+
+cairo_public cairo_surface_t *
+cairo_ps_surface_create_for_stream (cairo_write_func_t write_func,
+ void *closure,
+ double width_in_points,
+ double height_in_points);
+
+cairo_public void
+cairo_ps_surface_restrict_to_level (cairo_surface_t *surface,
+ cairo_ps_level_t level);
+
+cairo_public void
+cairo_ps_get_levels (cairo_ps_level_t const **levels,
+ int *num_levels);
+
+cairo_public const char *
+cairo_ps_level_to_string (cairo_ps_level_t level);
+
+cairo_public void
+cairo_ps_surface_set_eps (cairo_surface_t *surface,
+ cairo_bool_t eps);
+
+cairo_public cairo_bool_t
+cairo_ps_surface_get_eps (cairo_surface_t *surface);
+
+cairo_public void
+cairo_ps_surface_set_size (cairo_surface_t *surface,
+ double width_in_points,
+ double height_in_points);
+
+cairo_public void
+cairo_ps_surface_dsc_comment (cairo_surface_t *surface,
+ const char *comment);
+
+cairo_public void
+cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface);
+
+cairo_public void
+cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_PS_SURFACE */
+# error Cairo was not compiled with support for the ps backend
+#endif /* CAIRO_HAS_PS_SURFACE */
+
+#endif /* CAIRO_PS_H */
diff --git a/src/cairo-qt-surface.cpp b/src/cairo-qt-surface.cpp
new file mode 100755
index 000000000..ce05dba7e
--- /dev/null
+++ b/src/cairo-qt-surface.cpp
@@ -0,0 +1,1713 @@
+/* -*- 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>
+ */
+
+/* Get INT16_MIN etc. as per C99 */
+#define __STDC_LIMIT_MACROS
+
+#include "cairoint.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-types-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-fallback-private.h"
+
+#include "cairo-ft.h"
+#include "cairo-qt.h"
+
+#include <memory>
+
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEngine>
+#include <QtGui/QPaintDevice>
+#include <QtGui/QImage>
+#include <QtGui/QPixmap>
+#include <QtGui/QBrush>
+#include <QtGui/QPen>
+#include <QWidget>
+#include <QtCore/QVarLengthArray>
+
+#if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0
+extern void qt_draw_glyphs(QPainter *, const quint32 *glyphs, const QPointF *positions, int count);
+#endif
+
+#include <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;
+ }
+}
+
+static bool
+_op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op)
+{
+ if (qs->p == NULL)
+ return false;
+
+ if (qs->supports_porter_duff) {
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+
+ case CAIRO_OPERATOR_XOR:
+ return TRUE;
+
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return FALSE;
+
+ }
+ } else {
+ return op == CAIRO_OPERATOR_OVER;
+ }
+}
+
+static cairo_format_t
+_cairo_format_from_qimage_format (QImage::Format fmt)
+{
+ switch (fmt) {
+ case QImage::Format_ARGB32_Premultiplied:
+ return CAIRO_FORMAT_ARGB32;
+ case QImage::Format_RGB32:
+ return CAIRO_FORMAT_RGB24;
+ case QImage::Format_Indexed8: // XXX not quite
+ return CAIRO_FORMAT_A8;
+#ifdef WORDS_BIGENDIAN
+ case QImage::Format_Mono:
+#else
+ case QImage::Format_MonoLSB:
+#endif
+ return CAIRO_FORMAT_A1;
+
+ case QImage::Format_Invalid:
+#ifdef WORDS_BIGENDIAN
+ case QImage::Format_MonoLSB:
+#else
+ case QImage::Format_Mono:
+#endif
+ case QImage::Format_ARGB32:
+ case QImage::Format_RGB16:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_RGB555:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB444:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::NImageFormats:
+ default:
+ ASSERT_NOT_REACHED;
+ return (cairo_format_t) -1;
+ }
+}
+
+static QImage::Format
+_qimage_format_from_cairo_format (cairo_format_t fmt)
+{
+ switch (fmt) {
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_ARGB32:
+ return QImage::Format_ARGB32_Premultiplied;
+ case CAIRO_FORMAT_RGB24:
+ return QImage::Format_RGB32;
+ case CAIRO_FORMAT_RGB16_565:
+ return QImage::Format_RGB16;
+ case CAIRO_FORMAT_A8:
+ return QImage::Format_Indexed8; // XXX not quite
+ case CAIRO_FORMAT_A1:
+#ifdef WORDS_BIGENDIAN
+ return QImage::Format_Mono; // XXX think we need to choose between this and LSB
+#else
+ return QImage::Format_MonoLSB;
+#endif
+ }
+
+ 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 (const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm_inverse = NULL)
+{
+ qpainter_path_data data;
+ cairo_status_t status;
+
+ if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse))
+ ctm_inverse = NULL;
+ data.ctm_inverse = ctm_inverse;
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_path_to_qpainterpath_move_to,
+ _cairo_path_to_qpainterpath_line_to,
+ _cairo_path_to_qpainterpath_curve_to,
+ _cairo_path_to_qpainterpath_close_path,
+ &data);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ return data.path;
+}
+
+static inline QPainterPath
+path_to_qt (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_matrix_t *ctm_inverse = NULL)
+{
+ QPainterPath qpath = path_to_qt (path, ctm_inverse);
+
+ qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ?
+ Qt::WindingFill :
+ Qt::OddEvenFill);
+
+ return qpath;
+}
+
+/*
+ * Surface backend methods
+ */
+static cairo_surface_t *
+_cairo_qt_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ bool use_pixmap;
+
+ D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content));
+
+ use_pixmap = qs->image == NULL;
+ if (use_pixmap) {
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ use_pixmap = FALSE;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ break;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ use_pixmap = ! _qpixmaps_have_no_alpha;
+ break;
+ }
+ }
+
+ if (use_pixmap) {
+ cairo_surface_t *result =
+ cairo_qt_surface_create_with_qpixmap (content, width, height);
+
+ /* XXX result->content is always content. ??? */
+ if (result->content == content) {
+ D(fprintf(stderr, "qpixmap content: %d\n", content));
+ return result;
+ }
+
+ _qpixmaps_have_no_alpha = TRUE;
+ cairo_surface_destroy (result);
+ }
+
+ D(fprintf (stderr, "qimage\n"));
+ return cairo_qt_surface_create_with_qimage
+ (_cairo_format_from_content (content), width, height);
+}
+
+static cairo_status_t
+_cairo_qt_surface_finish (void *abstract_surface)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] finish\n", abstract_surface));
+
+ /* Only delete p if we created it */
+ if (qs->image || qs->pixmap)
+ delete qs->p;
+ else
+ qs->p->restore ();
+
+ if (qs->image_equiv)
+ cairo_surface_destroy (qs->image_equiv);
+
+ _cairo_surface_clipper_reset (&qs->clipper);
+
+ if (qs->image)
+ delete qs->image;
+
+ if (qs->pixmap)
+ delete qs->pixmap;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_qimg_destroy (void *closure)
+{
+ QImage *qimg = (QImage *) closure;
+ delete qimg;
+}
+
+static cairo_status_t
+_cairo_qt_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface));
+
+ *image_extra = NULL;
+
+ if (qs->image_equiv) {
+ *image_out = (cairo_image_surface_t*)
+ cairo_surface_reference (qs->image_equiv);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (qs->pixmap) {
+ QImage *qimg = new QImage(qs->pixmap->toImage());
+ cairo_surface_t *image;
+ cairo_status_t status;
+
+ image = cairo_image_surface_create_for_data (qimg->bits(),
+ _cairo_format_from_qimage_format (qimg->format()),
+ qimg->width(), qimg->height(),
+ qimg->bytesPerLine());
+
+ status = _cairo_user_data_array_set_data (&image->user_data,
+ (const cairo_user_data_key_t *)&_qimg_destroy,
+ qimg,
+ _qimg_destroy);
+ if (status) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ *image_out = (cairo_image_surface_t *) image;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+static void
+_cairo_qt_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface));
+
+ cairo_surface_destroy (&image->base);
+}
+
+struct _qimage_surface {
+ cairo_image_surface_t image;
+ QImage *qimg;
+};
+
+static cairo_surface_t *
+map_qimage_to_image (QImage *qimg, const cairo_rectangle_int_t *extents)
+{
+ struct _qimage_surface *surface;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+ uint8_t *data;
+
+ if (qimg == NULL)
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ switch (qimg->format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ case QImage::Format_RGB32:
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case QImage::Format_Indexed8: // XXX not quite
+ pixman_format = PIXMAN_a8;
+ break;
+#ifdef WORDS_BIGENDIAN
+ case QImage::Format_Mono:
+#else
+ case QImage::Format_MonoLSB:
+#endif
+ pixman_format = PIXMAN_a1;
+ break;
+
+ case QImage::Format_Invalid:
+#ifdef WORDS_BIGENDIAN
+ case QImage::Format_MonoLSB:
+#else
+ case QImage::Format_Mono:
+#endif
+ case QImage::Format_ARGB32:
+ case QImage::Format_RGB16:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_RGB555:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB444:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::NImageFormats:
+ default:
+ delete qimg;
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT);
+ }
+
+ data = qimg->bits();
+ data += extents->y * qimg->bytesPerLine();
+ data += extents->x * PIXMAN_FORMAT_BPP (pixman_format) / 8;
+
+ pixman_image = pixman_image_create_bits (pixman_format,
+ extents->width,
+ extents->height,
+ (uint32_t *)data,
+ qimg->bytesPerLine());
+ if (pixman_image == NULL) {
+ delete qimg;
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ surface = (struct _qimage_surface *) malloc (sizeof(*surface));
+ if (unlikely (surface == NULL)) {
+ pixman_image_unref (pixman_image);
+ delete qimg;
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_image_surface_init (&surface->image, pixman_image, pixman_format);
+ surface->qimg = qimg;
+
+ cairo_surface_set_device_offset (&surface->image.base,
+ -extents->x, -extents->y);
+
+ return &surface->image.base;
+}
+
+static cairo_image_surface_t *
+_cairo_qt_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ QImage *qimg = NULL;
+
+ D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface));
+
+ if (qs->image_equiv)
+ return _cairo_image_surface_map_to_image (qs->image_equiv,
+ extents);
+
+ QPoint offset;
+
+ if (qs->pixmap) {
+ qimg = new QImage(qs->pixmap->toImage());
+ } else {
+ // Try to figure out what kind of QPaintDevice we have, and
+ // how we can grab an image from it
+ QPaintDevice *pd = qs->p->device();
+ if (!pd)
+ return (cairo_image_surface_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ QPaintDevice *rpd = QPainter::redirected(pd, &offset);
+ if (rpd)
+ pd = rpd;
+
+ if (pd->devType() == QInternal::Image) {
+ qimg = new QImage(((QImage*) pd)->copy());
+ } else if (pd->devType() == QInternal::Pixmap) {
+ qimg = new QImage(((QPixmap*) pd)->toImage());
+ } else if (pd->devType() == QInternal::Widget) {
+ qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage());
+ }
+ }
+
+ return (cairo_image_surface_t *) map_qimage_to_image (qimg, extents);
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface));
+
+ if (!qs->image_equiv) {
+ struct _qimage_surface *qimage = (struct _qimage_surface *)image;
+
+ // XXX should I be using setBackgroundMode here instead of setCompositionMode?
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_Source);
+
+ qs->p->drawImage ((int)qimage->image.base.device_transform.x0,
+ (int)qimage->image.base.device_transform.y0,
+ *qimage->qimg,
+ (int)qimage->image.base.device_transform.x0,
+ (int)qimage->image.base.device_transform.y0,
+ (int)qimage->image.width,
+ (int)qimage->image.height);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ delete qimage->qimg;
+ }
+
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_qt_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ extents->x = qs->window.x();
+ extents->y = qs->window.y();
+ extents->width = qs->window.width();
+ extents->height = qs->window.height();
+
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_qt_surface_t *qs = cairo_container_of (clipper,
+ cairo_qt_surface_t,
+ clipper);
+
+ if (path == NULL) {
+ if (qs->pixmap || qs->image) {
+ // we own p
+ qs->p->setClipping (false);
+ } else {
+ qs->p->restore ();
+ qs->p->save ();
+ }
+ } else {
+ // XXX Antialiasing is ignored
+ qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs,
+ const cairo_region_t *clip_region)
+{
+ _cairo_surface_clipper_reset (&qs->clipper);
+
+ if (clip_region == NULL) {
+ // How the clip path is reset depends on whether we own p or not
+ if (qs->pixmap || qs->image) {
+ // we own p
+ qs->p->setClipping (false);
+ } else {
+ qs->p->restore ();
+ qs->p->save ();
+ }
+ } else {
+ QRegion qr;
+ int num_rects = cairo_region_num_rectangles (clip_region);
+ for (int i = 0; i < num_rects; ++i) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, i, &rect);
+
+ QRect r(rect.x, rect.y, rect.width, rect.height);
+ qr = qr.unite(r);
+ }
+
+ qs->p->setClipRegion (qr, Qt::IntersectClip);
+ }
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_set_clip (cairo_qt_surface_t *qs,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)"));
+
+ if (clip == NULL) {
+ _cairo_surface_clipper_reset (&qs->clipper);
+ // How the clip path is reset depends on whether we own p or not
+ if (qs->pixmap || qs->image) {
+ // we own p
+ qs->p->setClipping (false);
+ } else {
+ qs->p->restore ();
+ qs->p->save ();
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+#if ENABLE_FAST_CLIP
+ // Qt will implicitly enable clipping, and will use ReplaceClip
+ // instead of IntersectClip if clipping was disabled before
+
+ // Note: Qt is really bad at dealing with clip paths. It doesn't
+ // seem to usefully recognize rectangular paths, instead going down
+ // extremely slow paths whenever a clip path is set. So,
+ // we do a bunch of work here to try to get rectangles or regions
+ // down to Qt for clipping.
+
+ cairo_region_t *clip_region = NULL;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ // We weren't able to extract a region from the traps.
+ // Just hand the path down to QPainter.
+ status = (cairo_int_status_t)
+ _cairo_surface_clipper_set_clip (&qs->clipper, clip);
+ } else if (status == CAIRO_INT_STATUS_SUCCESS) {
+ _cairo_qt_surface_set_clip_region (qs, clip_region);
+ status = CAIRO_INT_STATUS_SUCCESS;
+ }
+#else
+ status = (cairo_int_status_t)
+ _cairo_surface_clipper_set_clip (&qs->clipper, clip);
+#endif
+
+ return status;
+}
+
+/*
+ * Brush conversion
+ */
+
+struct PatternToBrushConverter {
+ PatternToBrushConverter (const cairo_pattern_t *pattern) :
+ mAcquiredImageParent(0),
+ mAcquiredImage(0),
+ mAcquiredImageExtra(0)
+ {
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+ QColor color;
+ color.setRgbF(solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+ mBrush = QBrush(color);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
+ cairo_surface_t *surface = spattern->surface;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_QT) {
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+ if (qs->image) {
+ mBrush = QBrush(*qs->image);
+ } else if (qs->pixmap) {
+ mBrush = QBrush(*qs->pixmap);
+ } else {
+ // do something smart
+ mBrush = QBrush(0xff0000ff);
+ }
+ } else {
+ cairo_image_surface_t *isurf = NULL;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ isurf = (cairo_image_surface_t*) surface;
+ } else {
+ void *image_extra;
+
+ if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) {
+ mAcquiredImageParent = surface;
+ mAcquiredImage = isurf;
+ mAcquiredImageExtra = image_extra;
+ } else {
+ isurf = NULL;
+ }
+ }
+
+ if (isurf) {
+ mBrush = QBrush (QImage ((const uchar *) isurf->data,
+ isurf->width,
+ isurf->height,
+ isurf->stride,
+ _qimage_format_from_cairo_format (isurf->format)));
+ } else {
+ mBrush = QBrush(0x0000ffff);
+ }
+ }
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ QGradient *grad;
+ cairo_bool_t reverse_stops = FALSE;
+ cairo_bool_t emulate_reflect = FALSE;
+ double offset = 0.0;
+
+ cairo_extend_t extend = pattern->extend;
+
+ cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern;
+ grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y,
+ lpat->pd2.x, lpat->pd2.y);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern;
+
+ /* Based on the SVG surface code */
+
+ cairo_circle_double_t *c0, *c1;
+ double x0, y0, r0, x1, y1, r1;
+
+ if (rpat->cd1.radius < rpat->cd2.radius) {
+ c0 = &rpat->cd1;
+ c1 = &rpat->cd2;
+ reverse_stops = FALSE;
+ } else {
+ c0 = &rpat->cd2;
+ c1 = &rpat->cd1;
+ reverse_stops = TRUE;
+ }
+
+ x0 = c0->center.x;
+ y0 = c0->center.y;
+ r0 = c0->radius;
+ x1 = c1->center.x;
+ y1 = c1->center.y;
+ r1 = c1->radius;
+
+ if (r0 == r1) {
+ grad = new QRadialGradient (x1, y1, r1, x1, y1);
+ } else {
+ double fx = (r1 * x0 - r0 * x1) / (r1 - r0);
+ double fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+
+ /* QPainter doesn't support the inner circle and use instead a gradient focal.
+ * That means we need to emulate the cairo behaviour by processing the
+ * cairo gradient stops.
+ * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
+ * it's just a matter of stop position translation and calculation of
+ * the corresponding SVG radial gradient focal.
+ * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
+ * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
+ * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
+ * list that maps to the original cairo stop list.
+ */
+ if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) {
+ double r_org = r1;
+ double r, x, y;
+
+ if (extend == CAIRO_EXTEND_REFLECT) {
+ r1 = 2 * r1 - r0;
+ emulate_reflect = TRUE;
+ }
+
+ offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
+ r = r1 - r0;
+
+ /* New position of outer circle. */
+ x = r * (x1 - fx) / r_org + fx;
+ y = r * (y1 - fy) / r_org + fy;
+
+ x1 = x;
+ y1 = y;
+ r1 = r;
+ r0 = 0.0;
+ } else {
+ offset = r0 / r1;
+ }
+
+ grad = new QRadialGradient (x1, y1, r1, fx, fy);
+
+ if (extend == CAIRO_EXTEND_NONE && r0 != 0.0)
+ grad->setColorAt (r0 / r1, Qt::transparent);
+ }
+ }
+
+ switch (extend) {
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_PAD:
+ grad->setSpread(QGradient::PadSpread);
+
+ grad->setColorAt (0.0, Qt::transparent);
+ grad->setColorAt (1.0, Qt::transparent);
+ break;
+
+ case CAIRO_EXTEND_REFLECT:
+ grad->setSpread(QGradient::ReflectSpread);
+ break;
+
+ case CAIRO_EXTEND_REPEAT:
+ grad->setSpread(QGradient::RepeatSpread);
+ break;
+ }
+
+ for (unsigned int i = 0; i < gpat->n_stops; i++) {
+ int index = i;
+ if (reverse_stops)
+ index = gpat->n_stops - i - 1;
+
+ double offset = gpat->stops[i].offset;
+ QColor color;
+ color.setRgbF (gpat->stops[i].color.red,
+ gpat->stops[i].color.green,
+ gpat->stops[i].color.blue,
+ gpat->stops[i].color.alpha);
+
+ if (emulate_reflect) {
+ offset = offset / 2.0;
+ grad->setColorAt (1.0 - offset, color);
+ }
+
+ grad->setColorAt (offset, color);
+ }
+
+ mBrush = QBrush(*grad);
+
+ delete grad;
+ }
+
+ if (mBrush.style() != Qt::NoBrush &&
+ pattern->type != CAIRO_PATTERN_TYPE_SOLID &&
+ ! _cairo_matrix_is_identity (&pattern->matrix))
+ {
+ cairo_matrix_t pm = pattern->matrix;
+ cairo_status_t status = cairo_matrix_invert (&pm);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm));
+ }
+ }
+
+ ~PatternToBrushConverter () {
+ 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,
+ const cairo_path_fixed_t *path = NULL,
+ cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING,
+ double tolerance = 0.0,
+ cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE)
+{
+#if ENABLE_FAST_FILL
+ QImage *qsSrc_image = NULL;
+ QPixmap *qsSrc_pixmap = NULL;
+ std::auto_ptr<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,
+ const cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ cairo_int_status_t status;
+
+ D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op)));
+
+ if (! _op_is_supported (qs, op))
+ return _cairo_surface_fallback_paint (abstract_surface, op, source, clip);
+
+ status = _cairo_qt_surface_set_clip (qs, clip);
+ if (unlikely (status))
+ return status;
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ if (! _cairo_qt_fast_fill (qs, source)) {
+ PatternToBrushConverter brush (source);
+ qs->p->fillRect (qs->window, brush);
+ }
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op)));
+
+ if (! _op_is_supported (qs, op))
+ return _cairo_surface_fallback_fill (abstract_surface, op,
+ source, path, fill_rule,
+ tolerance, antialias, clip);
+
+ cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip);
+ if (unlikely (status))
+ return status;
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
+ // enabled
+ //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
+ qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
+
+ if (! _cairo_qt_fast_fill (qs, source,
+ path, fill_rule, tolerance, antialias))
+ {
+ PatternToBrushConverter brush(source);
+ qs->p->fillPath (path_to_qt (path, fill_rule), brush);
+ }
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op)));
+
+ if (! _op_is_supported (qs, op))
+ return _cairo_surface_fallback_stroke (abstract_surface, op,
+ source, path, style, ctm,
+ ctm_inverse, tolerance,
+ antialias, clip);
+
+ cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip);
+ if (unlikely (int_status))
+ return int_status;
+
+ QMatrix savedMatrix = qs->p->worldMatrix();
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true);
+ // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
+ // enabled
+ //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
+ qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
+
+ PatternToPenConverter pen(source, style);
+
+ qs->p->setPen(pen);
+ qs->p->drawPath(path_to_qt (path, ctm_inverse));
+ qs->p->setPen(Qt::black);
+
+ qs->p->setWorldMatrix (savedMatrix, false);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+#if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ // pick out the colour to use from the cairo source
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) source;
+ cairo_scaled_glyph_t* glyph;
+ // documentation says you have to freeze the cache, but I don't believe it
+ _cairo_scaled_font_freeze_cache(scaled_font);
+
+ QColor tempColour(solid->color.red * 255, solid->color.green * 255, solid->color.blue * 255);
+ QVarLengthArray<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;
+ }
+ qt_draw_glyphs(qs->p, glyphss.data(), positions.data(), num_glyphs);
+ _cairo_scaled_font_thaw_cache(scaled_font);
+ return CAIRO_INT_STATUS_SUCCESS;
+#else
+ return _cairo_surface_fallback_glyphs (abstract_surface, op,
+ source, glyphs, num_glyphs,
+ scaled_font, clip);
+#endif
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op)));
+
+ if (qs->p && mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+ cairo_int_status_t result;
+
+ qs->p->setOpacity (solid_mask->color.alpha);
+
+ result = _cairo_qt_surface_paint (abstract_surface, op, source, clip);
+
+ qs->p->setOpacity (1.0);
+
+ return result;
+ }
+
+ // otherwise skip for now
+ return _cairo_surface_fallback_mask (abstract_surface, op, source, mask, clip);
+}
+
+static cairo_status_t
+_cairo_qt_surface_mark_dirty (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ if (qs->p && !(qs->image || qs->pixmap))
+ qs->p->save ();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Backend struct
+ */
+
+static const cairo_surface_backend_t cairo_qt_surface_backend = {
+ CAIRO_SURFACE_TYPE_QT,
+ _cairo_qt_surface_finish,
+
+ _cairo_default_context_create, /* XXX */
+
+ _cairo_qt_surface_create_similar,
+ NULL, /* similar image */
+ _cairo_qt_surface_map_to_image,
+ _cairo_qt_surface_unmap_image,
+
+ _cairo_surface_default_source,
+ _cairo_qt_surface_acquire_source_image,
+ _cairo_qt_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_qt_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ _cairo_qt_surface_mark_dirty,
+
+ _cairo_qt_surface_paint,
+ _cairo_qt_surface_mask,
+ _cairo_qt_surface_stroke,
+ _cairo_qt_surface_fill,
+ NULL, /* fill_stroke */
+ _cairo_qt_surface_show_glyphs
+};
+
+cairo_surface_t *
+cairo_qt_surface_create (QPainter *painter)
+{
+ cairo_qt_surface_t *qs;
+
+ qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+ if (qs == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+ _cairo_surface_init (&qs->base,
+ &cairo_qt_surface_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ _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, /* device */
+ _cairo_content_from_format (format));
+
+ _cairo_surface_clipper_init (&qs->clipper,
+ _cairo_qt_surface_clipper_intersect_clip_path);
+
+
+ QImage *image = new QImage (width, height,
+ _qimage_format_from_cairo_format (format));
+
+ qs->image = image;
+
+ if (!image->isNull()) {
+ qs->p = new QPainter(image);
+ qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+ }
+
+ qs->image_equiv = cairo_image_surface_create_for_data (image->bits(),
+ format,
+ width, height,
+ image->bytesPerLine());
+
+ qs->window = QRect(0, 0, width, height);
+
+ D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n",
+ qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
+ qs->supports_porter_duff));
+
+ return &qs->base;
+}
+
+cairo_surface_t *
+cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_qt_surface_t *qs;
+
+ if ((content & CAIRO_CONTENT_COLOR) == 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+ qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+ if (qs == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+ QPixmap *pixmap = new QPixmap (width, height);
+ if (pixmap == NULL) {
+ free (qs);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ // By default, a QPixmap is opaque; however, if it's filled
+ // with a color with a transparency component, it is converted
+ // to a format that preserves transparency.
+ if (content == CAIRO_CONTENT_COLOR_ALPHA)
+ pixmap->fill(Qt::transparent);
+
+ _cairo_surface_init (&qs->base,
+ &cairo_qt_surface_backend,
+ NULL, /* device */
+ content);
+
+ _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 EXTEND_REPEAT
+ * - implement EXTEND_NONE (?? probably need to clip to the extents of the source)
+ * - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that)
+ *
+ * - stroke-image failure
+ *
+ * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN)
+ *
+ * - Implement gradient sources
+ *
+ * - Make create_similar smarter -- create QPixmaps in more circumstances
+ * (e.g. if the pixmap can have alpha)
+ *
+ * - Implement show_glyphs() in terms of Qt
+ *
+ */
diff --git a/src/cairo-qt.h b/src/cairo-qt.h
new file mode 100755
index 000000000..c20bbb18d
--- /dev/null
+++ b/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/src/cairo-quartz-filters.c b/src/cairo-quartz-filters.c
new file mode 100755
index 000000000..804cddfde
--- /dev/null
+++ b/src/cairo-quartz-filters.c
@@ -0,0 +1,450 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright � 2013 Samsung Research America - Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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):
+ * Henry Song <henry.song@samsung.com>
+ */
+
+#define _GNU_SOURCE /* required for RTLD_DEFAULT */
+#include "cairoint.h"
+#include "cairo-pattern-private.h"
+#include "cairo-quartz-private.h"
+#include "cairo-quartz.h"
+#include <Accelerate/Accelerate.h>
+
+#define CAIRO_QUARTZ_MAX_SCALE 4
+
+static int16_t *
+_cairo_quartz_pattern_create_gaussian_matrix (const cairo_pattern_t *pattern,
+ int *row, int *col,
+ int *sum,
+ int *shrink_x, int *shrink_y)
+{
+ double x_sigma, y_sigma;
+ double x_sigma_sq, y_sigma_sq;
+ int n;
+ double *buffer;
+ int16_t *i_buffer;
+ int i, x, y;
+ double u, v;
+ double u1, v1;
+ int x_radius, y_radius;
+ int i_row, i_col;
+ int x_factor, y_factor;
+ cairo_rectangle_int_t extents;
+ int width, height;
+ int max_factor;
+ double max_sigma;
+
+ max_factor = CAIRO_QUARTZ_MAX_SCALE;;
+ max_sigma = CAIRO_MAX_SIGMA;
+
+ width = CAIRO_MIN_SHRINK_SIZE;
+ height = CAIRO_MIN_SHRINK_SIZE;
+
+ if (_cairo_surface_get_extents (((cairo_surface_pattern_t *)pattern)->surface, &extents)) {
+ width = extents.width;
+ height = extents.height;
+ }
+
+ x_factor = y_factor = 1;
+ x_sigma = pattern->x_sigma;
+ y_sigma = pattern->y_sigma;
+
+ /* no blur */
+ if (x_sigma == 0.0 && y_sigma == 0.0) {
+ return NULL;
+ }
+
+ if (x_sigma == 0.0)
+ x_radius = 0;
+ else {
+ while (x_sigma >= max_sigma) {
+ if (width <= CAIRO_MIN_SHRINK_SIZE || x_factor >= max_factor)
+ break;
+
+ x_sigma *= 0.5;
+ x_factor *= 2;
+ width *= 0.5;
+ }
+ }
+
+ if (y_sigma == 0.0)
+ y_radius = 0;
+ else {
+ while (y_sigma >= max_sigma) {
+ if (height <= CAIRO_MIN_SHRINK_SIZE || y_factor >= max_factor)
+ break;
+
+ y_sigma *= 0.5;
+ y_factor *= 2;
+ height *= 0.5;
+ }
+ }
+
+ /* 2D gaussian
+ * f(x, y) = exp (-((x-x0)^2/(2*x_sigma^2)+(y-y0)^2/(2*y_sigma*2)))
+ */
+ x_radius = x_sigma * 2;
+ y_radius = y_sigma * 2;
+
+ i_row = y_radius;
+ i_col = x_radius;
+ n = (2 * i_row + 1) * (2 * i_col + 1);
+
+ x_sigma_sq = 2 * x_sigma * x_sigma;
+ y_sigma_sq = 2 * y_sigma * y_sigma;
+
+ buffer = _cairo_malloc_ab (n, sizeof (double));
+ if (! buffer)
+ return NULL;
+ i_buffer = _cairo_malloc_ab (n, sizeof (i_buffer));
+ if (! i_buffer) {
+ free (buffer);
+ return NULL;
+ }
+ i = 0;
+ *sum = 0;
+
+ for (y = -i_row; y <= i_row; y++) {
+ for (x = -i_col; x <= i_col; x++) {
+ u = x * x;
+ v = y * y;
+ if (u == 0.0)
+ u1 = 0.0;
+ else
+ u1 = u / x_sigma_sq;
+
+ if (v == 0.0)
+ v1 = 0.0;
+ else
+ v1 = v / y_sigma_sq;
+ buffer[i] = exp (-(u1 + v1));
+ i_buffer[i] = ceil (buffer[i] - 0.5);
+ *sum += i_buffer[i];
+ i++;
+ }
+ }
+
+ free (buffer);
+
+ *row = i_row * 2 + 1;
+ *col = i_col * 2 + 1;
+ *shrink_x = x_factor;
+ *shrink_y = y_factor;
+
+ return i_buffer;
+}
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+static CGContextRef
+_cairo_quartz_get_image_context (CGImageRef image)
+{
+ int width, height;
+ int bytes_per_row;
+ CGContextRef context;
+ CGColorSpaceRef color_space;
+ CGBitmapInfo bitmap_info;
+ CGRect size;
+
+ void *buffer;
+
+ if (image == NULL)
+ return NULL;
+
+ width = CGImageGetWidth (image);
+ height = CGImageGetHeight (image);
+ bytes_per_row = CGImageGetBytesPerRow (image);
+
+ color_space = CGImageGetColorSpace (image);
+ buffer = malloc (sizeof (char) * bytes_per_row * height);
+ if (! buffer)
+ return NULL;
+
+ bitmap_info = CGImageGetBitmapInfo (image);
+
+ /* create output image bitmap context */
+ context = CGBitmapContextCreate (buffer, width, height,
+ CGImageGetBitsPerComponent (image),
+ bytes_per_row,
+ color_space,
+ bitmap_info);
+
+ if (! context) {
+ free (buffer);
+ return NULL;
+ }
+
+ size = CGRectMake (0, 0, width, height);
+
+ CGContextDrawImage (context, size, image);
+
+ return context;
+}
+#endif
+
+static cairo_int_status_t
+_cairo_quartz_resize_image (CGImageRef src, double x_resize_factor,
+ double y_resize_factor, CGImageRef *out)
+{
+ int width, height;
+ int bytes_per_row;
+ int bytes_per_pixel;
+ CGContextRef out_bitmap_context;
+ CGColorSpaceRef color_space;
+ CGBitmapInfo bitmap_info;
+ CGRect size;
+
+ void *buffer;
+
+ if (src == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (x_resize_factor <= 0.0 ||
+ y_resize_factor <= 0.0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ width = CGImageGetWidth (src) * x_resize_factor;
+ height = CGImageGetHeight (src) * y_resize_factor;
+ bytes_per_pixel = CGImageGetBytesPerRow (src) / CGImageGetWidth (src);
+
+ color_space = CGImageGetColorSpace (src);
+ bytes_per_row = bytes_per_pixel * width;
+ buffer = malloc (sizeof (char) * bytes_per_row * height);
+ if (! buffer)
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ bitmap_info = CGImageGetBitmapInfo (src);
+
+ /* create output image bitmap context */
+ out_bitmap_context = CGBitmapContextCreate (buffer, width, height,
+ CGImageGetBitsPerComponent (src),
+ bytes_per_row,
+ color_space,
+ bitmap_info);
+
+ size = CGRectMake (0, 0, width, height);
+
+ CGContextDrawImage (out_bitmap_context, size, src);
+
+ *out = CGBitmapContextCreateImage (out_bitmap_context);
+
+ /* clean up */
+ CGContextRelease (out_bitmap_context);
+ free (buffer);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_convolve_pass (vImage_Buffer *src,
+ const int16_t *kernel,
+ int kernel_width, int kernel_height,
+ const int32_t divisor,
+ unsigned char *edge_fill,
+ vImage_Buffer *dst)
+{
+ vImage_Error error;
+
+ dst->data = malloc (src->rowBytes * src->height);
+ if (! dst->data)
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->rowBytes = src->rowBytes;
+
+ /* we always use background color beyond edge */
+ error = vImageConvolve_ARGB8888 (src, dst, NULL, /* no temp buffer */
+ 0, 0,
+ kernel, kernel_width, kernel_height,
+ divisor,
+ edge_fill,
+ kvImageNoFlags);
+
+ if (error != kvImageNoError)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_quartz_gaussian_filter (const cairo_pattern_t *src,
+ const CGImageRef image,
+ CGImageRef *out_image)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ vImage_Buffer src_buffer, dst_buffer;
+ int16_t *kernel = NULL;
+ int32_t divisor;
+ int shrink_factor_x;
+ int shrink_factor_y;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+ CGContextRef image_ctx;
+#else
+ CGDataProviderRef image_provider;
+ CFDataRef image_data_ref;
+#endif
+ CGImageRef resized_image;
+ CGImageRef resized_out_image;
+
+ CGContextRef ctx;
+ CGColorSpaceRef color_space;
+ CGBitmapInfo bitmap_info;
+
+ int row, col;
+ unsigned char edge_color[4] = {0, 0, 0, 0};
+
+ if (src->type != CAIRO_PATTERN_TYPE_SURFACE ||
+ ! src->convolution_matrix) {
+ *out_image = CGImageRetain (image);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ /* re-compute scaling */
+ kernel = _cairo_quartz_pattern_create_gaussian_matrix ((cairo_pattern_t *)src,
+ &row, &col,
+ &divisor,
+ &shrink_factor_x,
+ &shrink_factor_y);
+ if (! kernel) {
+ *out_image = NULL;
+ return CAIRO_INT_STATUS_NO_MEMORY;
+ }
+
+ if (shrink_factor_x == 1 &&
+ shrink_factor_y == 1)
+ resized_image = CGImageRetain (image);
+ else {
+ status = _cairo_quartz_resize_image (image,
+ 1.0 / src->shrink_factor_x,
+ 1.0 / src->shrink_factor_y,
+ &resized_image);
+ if (unlikely (status)) {
+ free (kernel);
+ *out_image = NULL;
+ return status;
+ }
+ }
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+ image_ctx = _cairo_quartz_get_image_context (resized_image);
+ if (! image_ctx) {
+ free (kernel);
+ *out_image = NULL;
+ return CAIRO_INT_STATUS_NO_MEMORY;
+ }
+#else
+ image_provider = CGImageGetDataProvider (resized_image);
+ image_data_ref = CGDataProviderCopyData (image_provider);
+#endif
+
+ src_buffer.width = CGImageGetWidth (resized_image);
+ src_buffer.height = CGImageGetHeight (resized_image);
+ src_buffer.rowBytes = CGImageGetBytesPerRow (resized_image);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+ src_buffer.data = CGBitmapContextGetData (image_ctx);
+ if (! src_buffer.data) {
+ free (kernel);
+ CGContextRelease (image_ctx);
+ *out_image = NULL;
+ return CAIRO_INT_STATUS_NO_MEMORY;
+ }
+#else
+ src_buffer.data = (void *) CFDataGetBytePtr (image_data_ref);
+#endif
+
+ dst_buffer.data = NULL;
+
+ status = _cairo_quartz_convolve_pass (&src_buffer,
+ kernel,
+ col, row,
+ divisor,
+ edge_color,
+ &dst_buffer);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+ CGContextRelease (image_ctx);
+ free (src_buffer.data);
+#else
+ CFRelease (image_data_ref);
+#endif
+
+ free (kernel);
+ CGImageRelease (resized_image);
+
+ if (unlikely (status)) {
+ if (dst_buffer.data)
+ free (dst_buffer.data);
+ *out_image = NULL;
+ return status;
+ }
+
+ /* create resized_out_image from blur */
+ color_space = CGImageGetColorSpace (resized_image);
+ bitmap_info = CGImageGetBitmapInfo (resized_image);
+
+ ctx = CGBitmapContextCreate (dst_buffer.data,
+ dst_buffer.width,
+ dst_buffer.height,
+ CGImageGetBitsPerComponent (resized_image),
+ dst_buffer.rowBytes,
+ color_space,
+ bitmap_info);
+
+ resized_out_image = CGBitmapContextCreateImage (ctx);
+
+ CGContextRelease (ctx);
+ free (dst_buffer.data);
+
+ /* scale back from resized_out_image to out_image */
+ if (shrink_factor_x == 1 &&
+ shrink_factor_y == 1) {
+ *out_image = resized_out_image;
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ status = _cairo_quartz_resize_image (resized_out_image,
+ src->shrink_factor_x,
+ src->shrink_factor_y,
+ out_image);
+ if (unlikely (status)) {
+ CGImageRelease (resized_out_image);
+ *out_image = NULL;
+ return status;
+ }
+ CGImageRelease (resized_out_image);
+ return status;
+}
diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c
new file mode 100755
index 000000000..a9bbbdc7a
--- /dev/null
+++ b/src/cairo-quartz-font.c
@@ -0,0 +1,860 @@
+/* -*- 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-image-surface-private.h"
+#include "cairo-quartz.h"
+#include "cairo-quartz-private.h"
+
+#include "cairo-error-private.h"
+
+/**
+ * SECTION:cairo-quartz-fonts
+ * @Title: Quartz (CGFont) Fonts
+ * @Short_Description: Font support via CGFont on OS X
+ * @See_Also: #cairo_font_face_t
+ *
+ * The Quartz font backend is primarily used to render text on Apple
+ * MacOS X systems. The CGFont API is used for the internal
+ * implementation of the font backend methods.
+ **/
+
+/**
+ * CAIRO_HAS_QUARTZ_FONT:
+ *
+ * Defined if the Quartz font backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.6
+ **/
+
+static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL;
+
+/* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */
+static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL;
+static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL;
+
+/* These aren't public before 10.5, and some have different names in 10.4 */
+static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL;
+static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL;
+static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL;
+static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL;
+
+/* Not public, but present */
+static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL;
+static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+
+/* Not public in the least bit */
+static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL;
+
+/* 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;
+
+/* Not public anymore in 64-bits nor in 10.7 */
+static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
+
+static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
+static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
+
+static void
+quartz_font_ensure_symbols(void)
+{
+ if (_cairo_quartz_font_symbol_lookup_done)
+ return;
+
+ CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag");
+
+ /* Look for the 10.5 versions first */
+ CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes");
+ if (!CGFontGetGlyphBBoxesPtr)
+ CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes");
+
+ CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars");
+ if (!CGFontGetGlyphsForUnicharsPtr)
+ CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes");
+
+ CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox");
+
+ /* We just need one of these two */
+ CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName");
+ CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName");
+
+ /* These have the same name in 10.4 and 10.5 */
+ CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm");
+ CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances");
+ CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath");
+
+ CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics");
+ CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent");
+ CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent");
+ CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading");
+
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
+ FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
+
+ if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+ CGFontGetGlyphBBoxesPtr &&
+ CGFontGetGlyphsForUnicharsPtr &&
+ CGFontGetUnitsPerEmPtr &&
+ CGFontGetGlyphAdvancesPtr &&
+ CGFontGetGlyphPathPtr &&
+ (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
+ _cairo_quartz_font_symbols_present = TRUE;
+
+ _cairo_quartz_font_symbol_lookup_done = TRUE;
+}
+
+typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t;
+typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t;
+
+struct _cairo_quartz_scaled_font {
+ cairo_scaled_font_t base;
+};
+
+struct _cairo_quartz_font_face {
+ cairo_font_face_t base;
+
+ CGFontRef cgFont;
+};
+
+/*
+ * 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;
+
+ 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);
+
+ _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 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;
+ }
+
+ 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;
+ 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);
+
+ glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph);
+ if (!glyphPath)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ path = _cairo_path_fixed_create ();
+ if (!path) {
+ CGPathRelease (glyphPath);
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ }
+
+ CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func);
+
+ CGPathRelease (glyphPath);
+
+ _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+ cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
+
+ cairo_image_surface_t *surface = NULL;
+
+ CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
+
+ int advance;
+ CGRect bbox;
+ double width, height;
+ double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+
+ CGContextRef cgContext = NULL;
+ CGAffineTransform textMatrix;
+ CGRect glyphRect, glyphRectInt;
+ CGPoint glyphOrigin;
+
+ //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);
+
+ /* Create blank 2x2 image if we don't have this character.
+ * Maybe we should draw a better missing-glyph slug or something,
+ * but this is ok for now.
+ */
+ if (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;
+ }
+
+ /* scale(1,-1) * font->base.scale * scale(1,-1) */
+ textMatrix = CGAffineTransformMake (font->base.scale.xx,
+ -font->base.scale.yx,
+ -font->base.scale.xy,
+ font->base.scale.yy,
+ 0, -0);
+ glyphRect = CGRectMake (bbox.origin.x / emscale,
+ bbox.origin.y / emscale,
+ bbox.size.width / emscale,
+ bbox.size.height / emscale);
+
+ glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);
+
+ /* Round the rectangle outwards, so that we don't have to deal
+ * with non-integer-pixel origins or dimensions.
+ */
+ glyphRectInt = CGRectIntegral (glyphRect);
+
+#if 0
+ fprintf (stderr, "glyphRect[o]: %f %f %f %f\n",
+ glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
+ fprintf (stderr, "glyphRectInt: %f %f %f %f\n",
+ glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height);
+#endif
+
+ glyphOrigin = glyphRectInt.origin;
+
+ //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm));
+
+ width = glyphRectInt.size.width;
+ height = glyphRectInt.size.height;
+
+ //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
+
+ surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+ if (surface->base.status)
+ return surface->base.status;
+
+ if (surface->width != 0 && surface->height != 0) {
+ cgContext = CGBitmapContextCreate (surface->data,
+ surface->width,
+ surface->height,
+ 8,
+ surface->stride,
+ NULL,
+ kCGImageAlphaOnly);
+
+ if (cgContext == NULL) {
+ cairo_surface_destroy (&surface->base);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ CGContextSetFont (cgContext, font_face->cgFont);
+ CGContextSetFontSize (cgContext, 1.0);
+ CGContextSetTextMatrix (cgContext, textMatrix);
+
+ switch (font->base.options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ case CAIRO_ANTIALIAS_BEST:
+ CGContextSetShouldAntialias (cgContext, TRUE);
+ CGContextSetShouldSmoothFonts (cgContext, TRUE);
+ if (CGContextSetAllowsFontSmoothingPtr &&
+ !CGContextGetAllowsFontSmoothingPtr (cgContext))
+ CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE);
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+ CGContextSetShouldAntialias (cgContext, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_GOOD:
+ case CAIRO_ANTIALIAS_FAST:
+ CGContextSetShouldAntialias (cgContext, TRUE);
+ CGContextSetShouldSmoothFonts (cgContext, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_DEFAULT:
+ default:
+ /* Don't do anything */
+ break;
+ }
+
+ CGContextSetAlpha (cgContext, 1.0);
+ CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);
+
+ CGContextRelease (cgContext);
+ }
+
+ cairo_surface_set_device_offset (&surface->base,
+ - glyphOrigin.x,
+ height + glyphOrigin.y);
+
+ _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_scaled_glyph_init (void *abstract_font,
+ cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_glyph_info_t info)
+{
+ cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font;
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS))
+ status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph);
+
+ if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH))
+ status = _cairo_quartz_init_glyph_path (font, scaled_glyph);
+
+ if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE))
+ status = _cairo_quartz_init_glyph_surface (font, scaled_glyph);
+
+ return status;
+}
+
+static unsigned long
+_cairo_quartz_ucs4_to_index (void *abstract_font,
+ uint32_t ucs4)
+{
+ cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
+ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font);
+ UniChar u = (UniChar) ucs4;
+ CGGlyph glyph;
+
+ CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1);
+
+ return glyph;
+}
+
+static cairo_int_status_t
+_cairo_quartz_load_truetype_table (void *abstract_font,
+ unsigned long tag,
+ long offset,
+ unsigned char *buffer,
+ unsigned long *length)
+{
+ cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font);
+ CFDataRef data = NULL;
+
+ if (likely (CGFontCopyTableForTagPtr))
+ data = CGFontCopyTableForTagPtr (font_face->cgFont, tag);
+
+ if (!data)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (buffer == NULL) {
+ *length = CFDataGetLength (data);
+ CFRelease (data);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (CFDataGetLength (data) < offset + (long) *length) {
+ CFRelease (data);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ CFDataGetBytes (data, CFRangeMake (offset, *length), buffer);
+ CFRelease (data);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = {
+ CAIRO_FONT_TYPE_QUARTZ,
+ _cairo_quartz_scaled_font_fini,
+ _cairo_quartz_scaled_glyph_init,
+ NULL, /* text_to_glyphs */
+ _cairo_quartz_ucs4_to_index,
+ _cairo_quartz_load_truetype_table,
+ NULL, /* map_glyphs_to_unicode */
+};
+
+/*
+ * private methods that the quartz surface uses
+ */
+
+CGFontRef
+_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
+{
+ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
+
+ return ffont->cgFont;
+}
+
+/*
+ * 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)
+{
+ quartz_font_ensure_symbols();
+
+ if (FMGetATSFontRefFromFontPtr != NULL) {
+ ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
+ CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
+ cairo_font_face_t *ff;
+
+ ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
+
+ CGFontRelease (cgFont);
+
+ return ff;
+ } else {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+ }
+}
+
+/* This is the old name for the above function, exported for compat purposes */
+cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+
+cairo_font_face_t *
+cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+{
+ return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
+}
diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c
new file mode 100755
index 000000000..2715abd06
--- /dev/null
+++ b/src/cairo-quartz-image-surface.c
@@ -0,0 +1,385 @@
+/* -*- 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-image-surface-inline.h"
+#include "cairo-quartz-image.h"
+#include "cairo-quartz-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-default-context-private.h"
+
+#define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)))
+#define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH)))
+#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)))
+#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT)))
+
+static void
+DataProviderReleaseCallback (void *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 *isurf =
+ _cairo_image_surface_create_with_content (content, width, height);
+ cairo_surface_t *result = cairo_quartz_image_surface_create (isurf);
+ cairo_surface_destroy (isurf);
+
+ return result;
+}
+
+static cairo_surface_t *
+_cairo_quartz_image_surface_create_similar_image (void *asurface,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_surface_t *isurf = cairo_image_surface_create (format, width, height);
+ cairo_surface_t *result = cairo_quartz_image_surface_create (isurf);
+ cairo_surface_destroy (isurf);
+
+ return result;
+}
+
+static cairo_status_t
+_cairo_quartz_image_surface_finish (void *asurface)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+ /* 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_image_surface_t *
+_cairo_quartz_image_surface_map_to_image (void *asurface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+ return _cairo_surface_map_to_image (&surface->imageSurface->base, extents);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_unmap_image (void *asurface,
+ cairo_image_surface_t *image)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+ return _cairo_surface_unmap_image (&surface->imageSurface->base, image);
+}
+
+static cairo_bool_t
+_cairo_quartz_image_surface_get_extents (void *asurface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+ extents->x = 0;
+ extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ return TRUE;
+}
+
+/* we assume some drawing happened to the image buffer; make sure it's
+ * represented in the CGImage on flush()
+ */
+
+static cairo_status_t
+_cairo_quartz_image_surface_flush (void *asurface,
+ unsigned flags)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+ CGImageRef oldImage = surface->image;
+ CGImageRef newImage = NULL;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* XXX only flush if the image has been modified. */
+
+ /* To be released by the ReleaseCallback */
+ cairo_surface_reference ((cairo_surface_t*) surface->imageSurface);
+
+ newImage = CairoQuartzCreateCGImage (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);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_quartz_image_surface_t *surface = abstract_surface;
+ return _cairo_surface_paint (&surface->imageSurface->base,
+ op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_quartz_image_surface_t *surface = abstract_surface;
+ return _cairo_surface_mask (&surface->imageSurface->base,
+ op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_quartz_image_surface_t *surface = abstract_surface;
+ return _cairo_surface_stroke (&surface->imageSurface->base,
+ op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_quartz_image_surface_t *surface = abstract_surface;
+ return _cairo_surface_fill (&surface->imageSurface->base,
+ op, source, path,
+ fill_rule, tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_quartz_image_surface_t *surface = abstract_surface;
+ return _cairo_surface_show_text_glyphs (&surface->imageSurface->base,
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font, clip);
+}
+
+
+static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
+ CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
+ _cairo_quartz_image_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_quartz_image_surface_create_similar,
+ _cairo_quartz_image_surface_create_similar_image,
+ _cairo_quartz_image_surface_map_to_image,
+ _cairo_quartz_image_surface_unmap_image,
+
+ _cairo_surface_default_source,
+ _cairo_quartz_image_surface_acquire_source_image,
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_quartz_image_surface_get_extents,
+ NULL, /* get_font_options */
+
+ _cairo_quartz_image_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_quartz_image_surface_paint,
+ _cairo_quartz_image_surface_mask,
+ _cairo_quartz_image_surface_stroke,
+ _cairo_quartz_image_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_quartz_image_surface_glyphs,
+};
+
+/**
+ * cairo_quartz_image_surface_create:
+ * @image_surface: a cairo image surface to wrap with a quartz image surface
+ *
+ * Creates a Quartz surface backed by a CGImageRef that references the
+ * given image surface. The resulting surface can be rendered quickly
+ * when used as a source when rendering to a #cairo_quartz_surface. If
+ * the data in the image surface is ever updated, cairo_surface_flush()
+ * must be called on the #cairo_quartz_image_surface to ensure that the
+ * CGImageRef refers to the updated data.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.6
+ **/
+cairo_surface_t *
+cairo_quartz_image_surface_create (cairo_surface_t *surface)
+{
+ cairo_quartz_image_surface_t *qisurf;
+
+ CGImageRef image;
+
+ cairo_image_surface_t *image_surface;
+ int width, height, stride;
+ cairo_format_t format;
+ unsigned char *data;
+
+ if (surface->status)
+ return surface;
+
+ if (! _cairo_surface_is_image (surface))
+ return SURFACE_ERROR_TYPE_MISMATCH;
+
+ image_surface = (cairo_image_surface_t*) surface;
+ width = image_surface->width;
+ height = image_surface->height;
+ stride = image_surface->stride;
+ format = image_surface->format;
+ 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 = CairoQuartzCreateCGImage (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->width = width;
+ qisurf->height = height;
+
+ qisurf->image = image;
+ qisurf->imageSurface = image_surface;
+
+ return &qisurf->base;
+}
+
+
+cairo_surface_t *
+cairo_quartz_image_surface_get_image (cairo_surface_t *asurface)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface;
+
+ if (asurface->type != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE)
+ return NULL;
+
+ return (cairo_surface_t*) surface->imageSurface;
+}
diff --git a/src/cairo-quartz-image.h b/src/cairo-quartz-image.h
new file mode 100755
index 000000000..dae234dac
--- /dev/null
+++ b/src/cairo-quartz-image.h
@@ -0,0 +1,59 @@
+/* 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 <Carbon/Carbon.h>
+
+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/src/cairo-quartz-private.h b/src/cairo-quartz-private.h
new file mode 100755
index 000000000..58b97ac99
--- /dev/null
+++ b/src/cairo-quartz-private.h
@@ -0,0 +1,111 @@
+/* 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
+
+typedef enum {
+ DO_DIRECT,
+ DO_SHADING,
+ DO_IMAGE,
+ DO_TILED_IMAGE
+} cairo_quartz_action_t;
+
+typedef struct cairo_quartz_surface {
+ cairo_surface_t base;
+
+ CGContextRef cgContext;
+ CGAffineTransform cgContextBaseCTM;
+
+ void *imageData;
+ cairo_surface_t *imageSurfaceEquiv;
+
+ cairo_surface_clipper_t clipper;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_int_t virtual_extents;
+} cairo_quartz_surface_t;
+
+typedef struct cairo_quartz_image_surface {
+ cairo_surface_t base;
+
+ int width, height;
+
+ CGImageRef image;
+ cairo_image_surface_t *imageSurface;
+} cairo_quartz_image_surface_t;
+
+cairo_private cairo_bool_t
+_cairo_quartz_verify_surface_size(int width, int height);
+
+cairo_private CGImageRef
+CairoQuartzCreateCGImage (cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride,
+ void *data,
+ cairo_bool_t interpolate,
+ CGColorSpaceRef colorSpaceOverride,
+ CGDataProviderReleaseDataCallback releaseCallback,
+ void *releaseInfo);
+
+cairo_private CGFontRef
+_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+
+cairo_private cairo_status_t
+_cairo_quartz_gaussian_filter (const cairo_pattern_t *src,
+ const CGImageRef image,
+ CGImageRef *out_image);
+
+#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/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
new file mode 100755
index 000000000..6d0a0c079
--- /dev/null
+++ b/src/cairo-quartz-surface.c
@@ -0,0 +1,2895 @@
+/* -*- 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>
+ */
+
+#define _GNU_SOURCE /* required for RTLD_DEFAULT */
+#include "cairoint.h"
+
+#include "cairo-quartz-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-shadow-private.h"
+#include "cairo-list-inline.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.
+ *
+ * Since: 1.6
+ **/
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+/* This method is private, but it exists. Its params are are exposed
+ * as args to the NS* method, but not as CG.
+ */
+enum PrivateCGCompositeMode {
+ kPrivateCGCompositeClear = 0,
+ kPrivateCGCompositeCopy = 1,
+ kPrivateCGCompositeSourceOver = 2,
+ kPrivateCGCompositeSourceIn = 3,
+ kPrivateCGCompositeSourceOut = 4,
+ kPrivateCGCompositeSourceAtop = 5,
+ kPrivateCGCompositeDestinationOver = 6,
+ kPrivateCGCompositeDestinationIn = 7,
+ kPrivateCGCompositeDestinationOut = 8,
+ kPrivateCGCompositeDestinationAtop = 9,
+ kPrivateCGCompositeXOR = 10,
+ kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
+ kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
+};
+typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
+CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
+#endif
+
+/* Some of these are present in earlier versions of the OS than where
+ * they are public; other are not public at all
+ */
+/* public since 10.5 */
+static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+
+/* public since 10.6 */
+static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
+static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+
+/* not yet public */
+static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+
+static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
+
+/* Shadow cache functions */
+static cairo_list_t shadow_caches;
+static unsigned long shadow_caches_size = 0;
+static cairo_recursive_mutex_t shadow_caches_mutex;
+static unsigned shadow_caches_mutex_depth = 0;
+static cairo_atomic_int_t shadow_caches_ref_count = 0;
+
+static void
+_cairo_quartz_surface_shadow_caches_init (void)
+{
+ _cairo_atomic_int_inc (&shadow_caches_ref_count);
+
+ if (shadow_caches_ref_count == 1)
+ cairo_list_init (&shadow_caches);
+
+ CAIRO_RECURSIVE_MUTEX_INIT (shadow_caches_mutex);
+}
+
+static void
+_cairo_quartz_surface_shadow_caches_destroy (void)
+{
+ assert (shadow_caches_ref_count != 0);
+
+ if (! _cairo_atomic_int_dec_and_test (&shadow_caches_ref_count))
+ return;
+
+ if (shadow_caches_mutex_depth == 0) {
+ CAIRO_MUTEX_FINI (shadow_caches_mutex);
+
+ while (! cairo_list_is_empty (&shadow_caches)) {
+ cairo_shadow_cache_t *shadow;
+
+ shadow = cairo_list_first_entry (&shadow_caches,
+ cairo_shadow_cache_t,
+ link);
+ cairo_list_del (&shadow->link);
+ cairo_surface_destroy (shadow->surface);
+ free (shadow);
+ }
+ shadow_caches_size = 0;
+ }
+}
+
+static cairo_status_t
+_cairo_quartz_surface_shadow_cache_acquire (void *abstract_surface)
+{
+ cairo_quartz_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_QUARTZ)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ if (unlikely (surface->base.status))
+ return surface->base.status;
+
+ CAIRO_MUTEX_LOCK (shadow_caches_mutex);
+ shadow_caches_mutex_depth++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_quartz_surface_shadow_cache_release (void *abstract_surface)
+{
+ cairo_quartz_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_QUARTZ)
+ return;
+
+ if (unlikely (surface->base.status))
+ return;
+
+ assert (shadow_caches_mutex_depth > 0);
+ shadow_caches_mutex_depth--;
+
+ CAIRO_MUTEX_UNLOCK (shadow_caches_mutex);
+}
+
+static cairo_list_t *
+_cairo_quartz_surface_get_shadow_cache (void *abstract_surface)
+{
+ cairo_quartz_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_QUARTZ)
+ return NULL;
+
+ if (unlikely (surface->base.status))
+ return NULL;
+
+ return &shadow_caches;
+}
+
+static unsigned long *
+_cairo_quartz_surface_get_shadow_cache_size (void *abstract_surface)
+{
+ cairo_quartz_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_QUARTZ)
+ return NULL;
+
+ if (unlikely (surface->base.status))
+ return NULL;
+
+ return &shadow_caches_size;
+}
+
+static cairo_bool_t
+_cairo_quartz_surface_has_shadow_cache (void *abstract_surface)
+{
+ cairo_quartz_surface_t *surface = abstract_surface;
+
+ if (! surface || surface->base.type != CAIRO_SURFACE_TYPE_QUARTZ)
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * 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 (likely (_cairo_quartz_symbol_lookup_done))
+ return;
+
+ CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
+ CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
+ CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
+ _cairo_quartz_symbol_lookup_done = TRUE;
+}
+
+CGImageRef
+CairoQuartzCreateCGImage (cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride,
+ void *data,
+ cairo_bool_t interpolate,
+ CGColorSpaceRef colorSpaceOverride,
+ CGDataProviderReleaseDataCallback releaseCallback,
+ void *releaseInfo)
+{
+ CGImageRef image = NULL;
+ CGDataProviderRef dataProvider = NULL;
+ CGColorSpaceRef colorSpace = colorSpaceOverride;
+ CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
+ int bitsPerComponent, bitsPerPixel;
+
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ if (colorSpace == NULL)
+ colorSpace = CGColorSpaceCreateDeviceRGB ();
+ bitinfo |= kCGImageAlphaPremultipliedFirst;
+ bitsPerComponent = 8;
+ bitsPerPixel = 32;
+ break;
+
+ case CAIRO_FORMAT_RGB24:
+ if (colorSpace == NULL)
+ colorSpace = CGColorSpaceCreateDeviceRGB ();
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
+ bitsPerComponent = 8;
+ bitsPerPixel = 32;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ bitsPerComponent = 8;
+ bitsPerPixel = 8;
+ break;
+
+ case CAIRO_FORMAT_A1:
+#ifdef WORDS_BIGENDIAN
+ bitsPerComponent = 1;
+ bitsPerPixel = 1;
+ break;
+#endif
+
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ return NULL;
+ }
+
+ dataProvider = CGDataProviderCreateWithData (releaseInfo,
+ data,
+ height * stride,
+ releaseCallback);
+
+ if (unlikely (!dataProvider)) {
+ // manually release
+ if (releaseCallback)
+ releaseCallback (releaseInfo, data, height * stride);
+ goto FINISH;
+ }
+
+ if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
+ cairo_quartz_float_t decode[] = {1.0, 0.0};
+ image = CGImageMaskCreate (width, height,
+ bitsPerComponent,
+ bitsPerPixel,
+ stride,
+ dataProvider,
+ decode,
+ interpolate);
+ } else
+ image = CGImageCreate (width, height,
+ bitsPerComponent,
+ bitsPerPixel,
+ stride,
+ colorSpace,
+ bitinfo,
+ dataProvider,
+ NULL,
+ interpolate,
+ kCGRenderingIntentDefault);
+
+FINISH:
+
+ CGDataProviderRelease (dataProvider);
+
+ if (colorSpace != colorSpaceOverride)
+ CGColorSpaceRelease (colorSpace);
+
+ return image;
+}
+
+static inline cairo_bool_t
+_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
+{
+ if (unlikely (cgc == NULL))
+ return FALSE;
+
+ if (likely (CGContextGetTypePtr)) {
+ /* 4 is the type value of a bitmap context */
+ return CGContextGetTypePtr (cgc) == 4;
+ }
+
+ /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
+ return CGBitmapContextGetBitsPerPixel (cgc) != 0;
+}
+
+/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
+
+#define CG_MAX_HEIGHT SHRT_MAX
+#define CG_MAX_WIDTH USHRT_MAX
+
+/* is the desired size of the surface within bounds? */
+cairo_bool_t
+_cairo_quartz_verify_surface_size (int width, int height)
+{
+ /* hmmm, allow width, height == 0 ? */
+ if (width < 0 || height < 0)
+ return FALSE;
+
+ if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Cairo path -> Quartz path conversion helpers
+ */
+
+/* cairo path -> execute in context */
+static cairo_status_t
+_cairo_path_to_quartz_context_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ CGContextMoveToPoint (closure, x, y);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_quartz_context_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ //ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ CGContextAddLineToPoint (closure, x, y);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_quartz_context_curve_to (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
+ // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
+ // _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
+ // _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
+ double x0 = _cairo_fixed_to_double (p0->x);
+ double y0 = _cairo_fixed_to_double (p0->y);
+ double x1 = _cairo_fixed_to_double (p1->x);
+ double y1 = _cairo_fixed_to_double (p1->y);
+ double x2 = _cairo_fixed_to_double (p2->x);
+ double y2 = _cairo_fixed_to_double (p2->y);
+
+ CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_quartz_context_close_path (void *closure)
+{
+ //ND ((stderr, "closepath\n"));
+ CGContextClosePath (closure);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
+ CGContextRef closure)
+{
+ cairo_status_t status;
+
+ CGContextBeginPath (closure);
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_path_to_quartz_context_move_to,
+ _cairo_path_to_quartz_context_line_to,
+ _cairo_path_to_quartz_context_curve_to,
+ _cairo_path_to_quartz_context_close_path,
+ closure);
+
+ assert (status == CAIRO_STATUS_SUCCESS);
+}
+
+/*
+ * Misc helpers/callbacks
+ */
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+static PrivateCGCompositeMode
+_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return kPrivateCGCompositeClear;
+ case CAIRO_OPERATOR_SOURCE:
+ return kPrivateCGCompositeCopy;
+ case CAIRO_OPERATOR_OVER:
+ return kPrivateCGCompositeSourceOver;
+ case CAIRO_OPERATOR_IN:
+ return kPrivateCGCompositeSourceIn;
+ case CAIRO_OPERATOR_OUT:
+ return kPrivateCGCompositeSourceOut;
+ case CAIRO_OPERATOR_ATOP:
+ return kPrivateCGCompositeSourceAtop;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return kPrivateCGCompositeDestinationOver;
+ case CAIRO_OPERATOR_DEST_IN:
+ return kPrivateCGCompositeDestinationIn;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return kPrivateCGCompositeDestinationOut;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return kPrivateCGCompositeDestinationAtop;
+ case CAIRO_OPERATOR_XOR:
+ return kPrivateCGCompositeXOR;
+ case CAIRO_OPERATOR_ADD:
+ return kPrivateCGCompositePlusLighter;
+
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_SATURATE:
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ default:
+ ASSERT_NOT_REACHED;
+ }
+}
+#endif
+
+static CGBlendMode
+_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_MULTIPLY:
+ return kCGBlendModeMultiply;
+ case CAIRO_OPERATOR_SCREEN:
+ return kCGBlendModeScreen;
+ case CAIRO_OPERATOR_OVERLAY:
+ return kCGBlendModeOverlay;
+ case CAIRO_OPERATOR_DARKEN:
+ return kCGBlendModeDarken;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return kCGBlendModeLighten;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return kCGBlendModeColorDodge;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return kCGBlendModeColorBurn;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return kCGBlendModeHardLight;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return kCGBlendModeSoftLight;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return kCGBlendModeDifference;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return kCGBlendModeExclusion;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return kCGBlendModeHue;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return kCGBlendModeSaturation;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return kCGBlendModeColor;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return kCGBlendModeLuminosity;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ case CAIRO_OPERATOR_CLEAR:
+ return kCGBlendModeClear;
+ case CAIRO_OPERATOR_SOURCE:
+ return kCGBlendModeCopy;
+ case CAIRO_OPERATOR_OVER:
+ return kCGBlendModeNormal;
+ case CAIRO_OPERATOR_IN:
+ return kCGBlendModeSourceIn;
+ case CAIRO_OPERATOR_OUT:
+ return kCGBlendModeSourceOut;
+ case CAIRO_OPERATOR_ATOP:
+ return kCGBlendModeSourceAtop;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return kCGBlendModeDestinationOver;
+ case CAIRO_OPERATOR_DEST_IN:
+ return kCGBlendModeDestinationIn;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return kCGBlendModeDestinationOut;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return kCGBlendModeDestinationAtop;
+ case CAIRO_OPERATOR_XOR:
+ return kCGBlendModeXOR;
+ case CAIRO_OPERATOR_ADD:
+ return kCGBlendModePlusLighter;
+#else
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+#endif
+
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_SATURATE:
+ default:
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static cairo_int_status_t
+_cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
+{
+ CGBlendMode blendmode;
+
+ assert (op != CAIRO_OPERATOR_DEST);
+
+ /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
+ * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
+ * uses the definition from the Adobe Supplement.
+ */
+ if (op == CAIRO_OPERATOR_SATURATE ||
+ op == CAIRO_OPERATOR_COLOR_DODGE ||
+ op == CAIRO_OPERATOR_COLOR_BURN)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+ if (op <= CAIRO_OPERATOR_ADD) {
+ PrivateCGCompositeMode compmode;
+
+ compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
+ CGContextSetCompositeOperation (context, compmode);
+ return CAIRO_STATUS_SUCCESS;
+ }
+#endif
+
+ blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
+ CGContextSetBlendMode (context, blendmode);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
+{
+ ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
+
+ /* When the destination has no color components, we can avoid some
+ * fallbacks, but we have to workaround operators which behave
+ * differently in Quartz. */
+ if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+ assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
+
+ if (op == CAIRO_OPERATOR_SOURCE ||
+ op == CAIRO_OPERATOR_IN ||
+ op == CAIRO_OPERATOR_OUT ||
+ op == CAIRO_OPERATOR_DEST_IN ||
+ op == CAIRO_OPERATOR_DEST_ATOP ||
+ op == CAIRO_OPERATOR_XOR)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (op == CAIRO_OPERATOR_DEST_OVER)
+ op = CAIRO_OPERATOR_OVER;
+ else if (op == CAIRO_OPERATOR_SATURATE)
+ op = CAIRO_OPERATOR_ADD;
+ else if (op == CAIRO_OPERATOR_COLOR_DODGE)
+ op = CAIRO_OPERATOR_OVER;
+ else if (op == CAIRO_OPERATOR_COLOR_BURN)
+ op = CAIRO_OPERATOR_OVER;
+ }
+
+ return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
+}
+
+static inline CGLineCap
+_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
+{
+ switch (ccap) {
+ default:
+ ASSERT_NOT_REACHED;
+
+ case CAIRO_LINE_CAP_BUTT:
+ return kCGLineCapButt;
+
+ case CAIRO_LINE_CAP_ROUND:
+ return kCGLineCapRound;
+
+ case CAIRO_LINE_CAP_SQUARE:
+ return kCGLineCapSquare;
+ }
+}
+
+static inline CGLineJoin
+_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
+{
+ switch (cjoin) {
+ default:
+ ASSERT_NOT_REACHED;
+
+ case CAIRO_LINE_JOIN_MITER:
+ return kCGLineJoinMiter;
+
+ case CAIRO_LINE_JOIN_ROUND:
+ return kCGLineJoinRound;
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ return kCGLineJoinBevel;
+ }
+}
+
+static inline CGInterpolationQuality
+_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
+{
+ switch (filter) {
+ case CAIRO_FILTER_NEAREST:
+ case CAIRO_FILTER_FAST:
+ return kCGInterpolationNone;
+
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return kCGInterpolationDefault;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return kCGInterpolationDefault;
+ }
+}
+
+static inline void
+_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
+ CGAffineTransform *dst)
+{
+ dst->a = src->xx;
+ dst->b = src->yx;
+ dst->c = src->xy;
+ dst->d = src->yy;
+ dst->tx = src->x0;
+ dst->ty = src->y0;
+}
+
+
+/*
+ * Source -> Quartz setup and finish functions
+ */
+
+static void
+ComputeGradientValue (void *info,
+ const cairo_quartz_float_t *in,
+ cairo_quartz_float_t *out)
+{
+ double fdist = *in;
+ const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
+ unsigned int i;
+
+ /* Put fdist back in the 0.0..1.0 range if we're doing
+ * REPEAT/REFLECT
+ */
+ if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
+ fdist = fdist - floor (fdist);
+ } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
+ fdist = fmod (fabs (fdist), 2.0);
+ if (fdist > 1.0)
+ fdist = 2.0 - fdist;
+ }
+
+ for (i = 0; i < grad->n_stops; i++)
+ if (grad->stops[i].offset > fdist)
+ break;
+
+ if (i == 0 || i == grad->n_stops) {
+ if (i == grad->n_stops)
+ --i;
+ out[0] = grad->stops[i].color.red;
+ out[1] = grad->stops[i].color.green;
+ out[2] = grad->stops[i].color.blue;
+ out[3] = grad->stops[i].color.alpha;
+ } else {
+ cairo_quartz_float_t ax = grad->stops[i-1].offset;
+ cairo_quartz_float_t bx = grad->stops[i].offset - ax;
+ cairo_quartz_float_t bp = (fdist - ax)/bx;
+ cairo_quartz_float_t ap = 1.0 - bp;
+
+ out[0] =
+ grad->stops[i-1].color.red * ap +
+ grad->stops[i].color.red * bp;
+ out[1] =
+ grad->stops[i-1].color.green * ap +
+ grad->stops[i].color.green * bp;
+ out[2] =
+ grad->stops[i-1].color.blue * ap +
+ grad->stops[i].color.blue * bp;
+ out[3] =
+ grad->stops[i-1].color.alpha * ap +
+ grad->stops[i].color.alpha * bp;
+ }
+}
+
+static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
+ 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
+};
+static const CGFunctionCallbacks gradient_callbacks = {
+ 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+};
+
+/* Quartz computes a small number of samples of the gradient color
+ * function. On MacOS X 10.5 it apparently computes only 1024
+ * samples. */
+#define MAX_GRADIENT_RANGE 1024
+
+static CGFunctionRef
+CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents,
+ cairo_circle_double_t *start,
+ cairo_circle_double_t *end)
+{
+ cairo_pattern_t *pat;
+ cairo_quartz_float_t input_value_range[2];
+
+ if (gradient->base.extend != CAIRO_EXTEND_NONE) {
+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+ double t[2], tolerance;
+
+ tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
+ tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
+
+ bounds_x1 = extents->x;
+ bounds_y1 = extents->y;
+ bounds_x2 = extents->x + extents->width;
+ bounds_y2 = extents->y + extents->height;
+ _cairo_matrix_transform_bounding_box (&gradient->base.matrix,
+ &bounds_x1, &bounds_y1,
+ &bounds_x2, &bounds_y2,
+ NULL);
+
+ _cairo_gradient_pattern_box_to_parameter (gradient,
+ bounds_x1, bounds_y1,
+ bounds_x2, bounds_y2,
+ tolerance,
+ t);
+
+ if (gradient->base.extend == CAIRO_EXTEND_PAD) {
+ t[0] = MAX (t[0], -0.5);
+ t[1] = MIN (t[1], 1.5);
+ } else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
+ return NULL;
+
+ /* set the input range for the function -- the function knows how
+ to map values outside of 0.0 .. 1.0 to the correct color */
+ input_value_range[0] = t[0];
+ input_value_range[1] = t[1];
+ } else {
+ input_value_range[0] = 0;
+ input_value_range[1] = 1;
+ }
+
+ _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
+ _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
+
+ if (_cairo_pattern_create_copy (&pat, &gradient->base))
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+ gradient_output_value_ranges,
+ &gradient_callbacks);
+}
+
+/* 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);
+ free (source_img);
+}
+
+static cairo_status_t
+_cairo_surface_to_cgimage (const cairo_pattern_t *pattern,
+ cairo_surface_t *source,
+ cairo_rectangle_int_t *extents,
+ cairo_format_t format,
+ cairo_matrix_t *matrix,
+ const cairo_clip_t *clip,
+ CGImageRef *image_out)
+{
+ cairo_status_t status;
+ quartz_source_image_t *source_img;
+ cairo_image_surface_t *image_surface;
+ CGImageRef image;
+
+ if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
+
+ status = _cairo_quartz_gaussian_filter (pattern, surface->image,
+ image_out);
+ if (unlikely (status)) {
+ *image_out = NULL;
+ }
+ return status;
+ }
+
+ if (_cairo_surface_is_quartz (source)) {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
+ if (IS_EMPTY (surface)) {
+ *image_out = NULL;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+ image = CGBitmapContextCreateImage (surface->cgContext);
+ if (image) {
+ status = _cairo_quartz_gaussian_filter (pattern, image,
+ image_out);
+ CGImageRelease (image);
+ if (unlikely (status)) {
+ *image_out = NULL;
+ }
+ }
+ else {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ *image_out = NULL;
+ }
+ return status;
+ }
+ }
+
+ source_img = malloc (sizeof (quartz_source_image_t));
+ if (unlikely (source_img == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ source_img->surface = source;
+
+ if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ image_surface = (cairo_image_surface_t *)
+ cairo_image_surface_create (format, extents->width, extents->height);
+ if (unlikely (image_surface->base.status)) {
+ status = image_surface->base.status;
+ cairo_surface_destroy (&image_surface->base);
+ free (source_img);
+ return status;
+ }
+
+ status = _cairo_recording_surface_replay_with_clip (source,
+ matrix,
+ &image_surface->base,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image_surface->base);
+ free (source_img);
+ return status;
+ }
+
+ source_img->image_out = image_surface;
+ source_img->image_extra = NULL;
+
+ cairo_matrix_init_identity (matrix);
+ }
+ else {
+ status = _cairo_surface_acquire_source_image (source_img->surface,
+ &source_img->image_out,
+ &source_img->image_extra);
+ if (unlikely (status)) {
+ 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 = CairoQuartzCreateCGImage (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 (unlikely (image == NULL)) {
+ *image_out = NULL;
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = _cairo_quartz_gaussian_filter (pattern, image,
+ image_out);
+ CGImageRelease (image);
+
+ if (unlikely (status)) {
+ *image_out = NULL;
+ }
+ }
+
+ return status;
+}
+
+/* Generic #cairo_pattern_t -> CGPattern function */
+
+typedef struct {
+ CGImageRef image;
+ CGRect imageBounds;
+ cairo_bool_t do_reflect;
+} SurfacePatternDrawInfo;
+
+static void
+SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
+{
+ SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
+
+ CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
+ CGContextScaleCTM (context, 1, -1);
+
+ CGContextDrawImage (context, info->imageBounds, info->image);
+ if (info->do_reflect) {
+ /* draw 3 more copies of the image, flipped.
+ * DrawImage draws the image according to the current Y-direction into the rectangle given
+ * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
+ * of the base image position, and the Y axis is extending upwards.
+ */
+
+ /* Make the y axis extend downwards, and draw a flipped image below */
+ CGContextScaleCTM (context, 1, -1);
+ CGContextDrawImage (context, info->imageBounds, info->image);
+
+ /* Shift over to the right, and flip vertically (translation is 2x,
+ * since we'll be flipping and thus rendering the rectangle "backwards"
+ */
+ CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
+ CGContextScaleCTM (context, -1, 1);
+ CGContextDrawImage (context, info->imageBounds, info->image);
+
+ /* Then unflip the Y-axis again, and draw the image above the point. */
+ CGContextScaleCTM (context, 1, -1);
+ CGContextDrawImage (context, info->imageBounds, info->image);
+ }
+}
+
+static void
+SurfacePatternReleaseInfoFunc (void *ainfo)
+{
+ SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
+
+ CGImageRelease (info->image);
+ free (info);
+}
+
+static cairo_int_status_t
+_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
+ const cairo_pattern_t *apattern,
+ const cairo_clip_t *clip,
+ CGPatternRef *cgpat)
+{
+ cairo_surface_pattern_t *spattern;
+ cairo_surface_t *pat_surf;
+ cairo_rectangle_int_t extents;
+ cairo_format_t format = _cairo_format_from_content (dest->base.content);
+
+ CGImageRef image;
+ CGRect pbounds;
+ CGAffineTransform ptransform, stransform;
+ CGPatternCallbacks cb = { 0,
+ SurfacePatternDrawFunc,
+ SurfacePatternReleaseInfoFunc };
+ SurfacePatternDrawInfo *info;
+ cairo_quartz_float_t rw, rh;
+ cairo_status_t status;
+ cairo_bool_t is_bounded;
+
+ cairo_matrix_t m;
+
+ /* SURFACE is the only type we'll handle here */
+ assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
+
+ spattern = (cairo_surface_pattern_t *) apattern;
+ pat_surf = spattern->surface;
+
+ if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+ }
+ else
+ _cairo_surface_get_extents (&dest->base, &extents);
+
+ m = spattern->base.matrix;
+ status = _cairo_surface_to_cgimage (apattern, pat_surf, &extents,
+ format,
+ &m, clip, &image);
+ if (unlikely (status))
+ return status;
+
+ info = malloc (sizeof (SurfacePatternDrawInfo));
+ if (unlikely (!info))
+ return CAIRO_STATUS_NO_MEMORY;
+
+ /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
+ * that the data will stick around for this image when the printer gets to it.
+ * Otherwise, the underlying data store may disappear from under us!
+ *
+ * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
+ * since the Quartz surfaces have a higher chance of sticking around. If the
+ * source is a quartz image surface, then it's set up to retain a ref to the
+ * image surface that it's backed by.
+ */
+ info->image = image;
+ info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
+ info->do_reflect = FALSE;
+
+ pbounds.origin.x = 0;
+ pbounds.origin.y = 0;
+
+ if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
+ pbounds.size.width = 2.0 * extents.width;
+ pbounds.size.height = 2.0 * extents.height;
+ info->do_reflect = TRUE;
+ } else {
+ pbounds.size.width = extents.width;
+ pbounds.size.height = extents.height;
+ }
+ rw = pbounds.size.width;
+ rh = pbounds.size.height;
+
+ cairo_matrix_invert (&m);
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
+
+ /* The pattern matrix is relative to the bottom left, again; the
+ * incoming cairo pattern matrix is relative to the upper left.
+ * So we take the pattern matrix and the original context matrix,
+ * which gives us the correct base translation/y flip.
+ */
+ ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
+
+#ifdef QUARTZ_DEBUG
+ ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
+ ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
+ CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
+ ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
+#endif
+
+ *cgpat = CGPatternCreate (info,
+ pbounds,
+ ptransform,
+ rw, rh,
+ kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
+ TRUE,
+ &cb);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* State used during a drawing operation. */
+typedef struct {
+ /* The destination of the mask */
+ CGContextRef cgMaskContext;
+
+ /* The destination of the drawing of the source */
+ CGContextRef cgDrawContext;
+
+ /* The filter to be used when drawing the source */
+ CGInterpolationQuality filter;
+
+ /* Action type */
+ cairo_quartz_action_t action;
+
+ /* Destination rect */
+ CGRect rect;
+
+ /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
+ CGAffineTransform transform;
+
+ /* Used with DO_IMAGE and DO_TILED_IMAGE */
+ CGImageRef image;
+
+ /* Used with DO_SHADING */
+ CGShadingRef shading;
+
+ /* Temporary destination for unbounded operations */
+ CGLayerRef layer;
+ CGRect clipRect;
+} cairo_quartz_drawing_state_t;
+
+/*
+Quartz does not support repeating radients. We handle repeating gradients
+by manually extending the gradient and repeating color stops. We need to
+minimize the number of repetitions since Quartz seems to sample our color
+function across the entire range, even if part of that range is not needed
+for the visible area of the gradient, and it samples with some fixed resolution,
+so if the gradient range is too large it samples with very low resolution and
+the gradient is very coarse. _cairo_quartz_create_gradient_function computes
+the number of repetitions needed based on the extents.
+*/
+static cairo_int_status_t
+_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
+ const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_matrix_t mat;
+ cairo_circle_double_t start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
+
+ assert (gradient->n_stops > 0);
+
+ mat = gradient->base.matrix;
+ cairo_matrix_invert (&mat);
+ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
+ &start, &end);
+
+ if (unlikely (gradFunc == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ rgb = CGColorSpaceCreateDeviceRGB ();
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ state->shading = CGShadingCreateAxial (rgb,
+ CGPointMake (start.center.x,
+ start.center.y),
+ CGPointMake (end.center.x,
+ end.center.y),
+ gradFunc,
+ extend, extend);
+ } else {
+ state->shading = CGShadingCreateRadial (rgb,
+ CGPointMake (start.center.x,
+ start.center.y),
+ MAX (start.radius, 0),
+ CGPointMake (end.center.x,
+ end.center.y),
+ MAX (end.radius, 0),
+ gradFunc,
+ extend, extend);
+ }
+
+ CGColorSpaceRelease (rgb);
+ CGFunctionRelease (gradFunc);
+
+ state->action = DO_SHADING;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) composite->surface;
+ cairo_operator_t op = composite->op;
+ const cairo_pattern_t *source = &composite->source_pattern.base;
+ const cairo_clip_t *clip = composite->clip;
+ cairo_bool_t needs_temp;
+ cairo_status_t status;
+ cairo_format_t format = _cairo_format_from_content (composite->surface->content);
+
+ state->layer = NULL;
+ state->image = NULL;
+ state->shading = NULL;
+ state->cgDrawContext = NULL;
+ state->cgMaskContext = NULL;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ if (unlikely (status))
+ return status;
+
+ /* Save before we change the pattern, colorspace, etc. so that
+ * we can restore and make sure that quartz releases our
+ * pattern (which may be stack allocated)
+ */
+
+ CGContextSaveGState (surface->cgContext);
+ state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
+ state->clipRect = CGRectIntegral (state->clipRect);
+ state->rect = state->clipRect;
+
+ state->cgMaskContext = surface->cgContext;
+ state->cgDrawContext = state->cgMaskContext;
+
+ state->filter = _cairo_quartz_filter_to_quartz (source->filter);
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+ state->action = DO_DIRECT;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /*
+ * To implement mask unbounded operations Quartz needs a temporary
+ * surface which will be composited entirely (ignoring the mask).
+ * To implement source unbounded operations Quartz needs a
+ * temporary surface which allows extending the source to a size
+ * covering the whole mask, but there are some optimization
+ * opportunities:
+ *
+ * - CLEAR completely ignores the source, thus we can just use a
+ * solid color fill.
+ *
+ * - SOURCE can be implemented by drawing the source and clearing
+ * outside of the source as long as the two regions have no
+ * intersection. This happens when the source is a pixel-aligned
+ * rectangle. If the source is at least as big as the
+ * intersection between the clip rectangle and the mask
+ * rectangle, no clear operation is needed.
+ */
+ needs_temp = ! _cairo_operator_bounded_by_mask (op);
+
+ if (needs_temp) {
+ state->layer = CGLayerCreateWithContext (surface->cgContext,
+ state->clipRect.size,
+ NULL);
+ state->cgDrawContext = CGLayerGetContext (state->layer);
+ state->cgMaskContext = state->cgDrawContext;
+ CGContextTranslateCTM (state->cgDrawContext,
+ -state->clipRect.origin.x,
+ -state->clipRect.origin.y);
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+ CGContextSetRGBStrokeColor (state->cgDrawContext,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+ CGContextSetRGBFillColor (state->cgDrawContext,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+ state->action = DO_DIRECT;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ source->type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
+ cairo_rectangle_int_t extents;
+
+ extents = surface->virtual_extents;
+ extents.x -= surface->base.device_transform.x0;
+ extents.y -= surface->base.device_transform.y0;
+ _cairo_rectangle_union (&extents, &surface->extents);
+
+ return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
+ {
+ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ cairo_surface_t *pat_surf = spat->surface;
+ CGImageRef img;
+ cairo_matrix_t m = spat->base.matrix;
+ cairo_rectangle_int_t extents;
+ CGAffineTransform xform;
+ CGRect srcRect;
+ cairo_fixed_t fw, fh;
+ cairo_bool_t is_bounded;
+
+ _cairo_surface_get_extents (composite->surface, &extents);
+ status = _cairo_surface_to_cgimage (source, pat_surf, &extents,
+ format,
+ &m, clip, &img);
+ if (unlikely (status))
+ return status;
+
+ state->image = img;
+
+ if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
+ m.x0 = -ceil (m.x0 - 0.5);
+ m.y0 = -ceil (m.y0 - 0.5);
+ } else {
+ cairo_matrix_invert (&m);
+ }
+
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+
+ if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+ }
+
+ srcRect = CGRectMake (0, 0, extents.width, extents.height);
+
+ if (source->extend == CAIRO_EXTEND_NONE) {
+ int x, y;
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ (pat_surf->content == CAIRO_CONTENT_ALPHA ||
+ ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
+ {
+ state->layer = CGLayerCreateWithContext (surface->cgContext,
+ state->clipRect.size,
+ NULL);
+ state->cgDrawContext = CGLayerGetContext (state->layer);
+ CGContextTranslateCTM (state->cgDrawContext,
+ -state->clipRect.origin.x,
+ -state->clipRect.origin.y);
+ }
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+ state->rect = srcRect;
+ state->action = DO_IMAGE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+ /* Quartz seems to tile images at pixel-aligned regions only -- this
+ * leads to seams if the image doesn't end up scaling to fill the
+ * space exactly. The CGPattern tiling approach doesn't have this
+ * problem. Check if we're going to fill up the space (within some
+ * epsilon), and if not, fall back to the CGPattern type.
+ */
+
+ xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
+ state->transform);
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+ fw = _cairo_fixed_from_double (srcRect.size.width);
+ fh = _cairo_fixed_from_double (srcRect.size.height);
+
+ if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
+ (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
+ {
+ /* We're good to use DrawTiledImage, but ensure that
+ * the math works out */
+
+ srcRect.size.width = round (srcRect.size.width);
+ srcRect.size.height = round (srcRect.size.height);
+
+ xform = CGAffineTransformInvert (xform);
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+ state->rect = srcRect;
+ state->action = DO_TILED_IMAGE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* Fall through to generic SURFACE case */
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_quartz_float_t patternAlpha = 1.0f;
+ CGColorSpaceRef patternSpace;
+ CGPatternRef pattern = NULL;
+ cairo_int_status_t status;
+
+ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern);
+ if (unlikely (status))
+ return status;
+
+ patternSpace = CGColorSpaceCreatePattern (NULL);
+ CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
+ CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
+ CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
+ CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
+ CGColorSpaceRelease (patternSpace);
+
+ /* Quartz likes to munge the pattern phase (as yet unexplained
+ * why); force it to 0,0 as we've already baked in the correct
+ * pattern translation into the pattern matrix
+ */
+ CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
+
+ CGPatternRelease (pattern);
+
+ state->action = DO_DIRECT;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
+
+ if (state->layer) {
+ CGContextDrawLayerInRect (surface->cgContext,
+ state->clipRect,
+ state->layer);
+ CGContextRelease (state->cgDrawContext);
+ CGLayerRelease (state->layer);
+ }
+
+ if (state->cgMaskContext)
+ CGContextRestoreGState (surface->cgContext);
+
+ if (state->image)
+ CGImageRelease (state->image);
+
+ if (state->shading)
+ CGShadingRelease (state->shading);
+}
+
+static void
+_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
+ cairo_operator_t op)
+{
+ CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
+ CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
+
+ if (state->action == DO_DIRECT) {
+ CGContextFillRect (state->cgDrawContext, state->rect);
+ return;
+ }
+
+ CGContextConcatCTM (state->cgDrawContext, state->transform);
+
+ if (state->action == DO_SHADING) {
+ CGContextDrawShading (state->cgDrawContext, state->shading);
+ return;
+ }
+
+ CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
+ CGContextScaleCTM (state->cgDrawContext, 1, -1);
+
+ if (state->action == DO_IMAGE) {
+ CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ state->cgDrawContext == state->cgMaskContext)
+ {
+ CGContextBeginPath (state->cgDrawContext);
+ CGContextAddRect (state->cgDrawContext, state->rect);
+
+ CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
+ CGContextScaleCTM (state->cgDrawContext, 1, -1);
+ CGContextConcatCTM (state->cgDrawContext,
+ CGAffineTransformInvert (state->transform));
+
+ CGContextAddRect (state->cgDrawContext, state->clipRect);
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
+ CGContextEOFillPath (state->cgDrawContext);
+ }
+ } else {
+ CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
+ }
+}
+
+static cairo_image_surface_t *
+_cairo_quartz_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ unsigned int stride, bitinfo, bpp, color_comps;
+ CGColorSpaceRef colorspace;
+ void *imageData;
+ cairo_format_t format;
+
+ if (surface->imageSurfaceEquiv)
+ return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents);
+
+ if (IS_EMPTY (surface))
+ return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+
+ if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
+ return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
+ bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
+
+ // let's hope they don't add YUV under us
+ colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
+ color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
+
+ /* XXX TODO: We can handle many more data formats by
+ * converting to pixman_format_t */
+
+ if (bpp == 32 && color_comps == 3 &&
+ (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
+ (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
+ {
+ format = CAIRO_FORMAT_ARGB32;
+ }
+ else if (bpp == 32 && color_comps == 3 &&
+ (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
+ (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
+ {
+ format = CAIRO_FORMAT_RGB24;
+ }
+ else if (bpp == 8 && color_comps == 1)
+ {
+ format = CAIRO_FORMAT_A1;
+ }
+ else
+ {
+ return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ imageData = CGBitmapContextGetData (surface->cgContext);
+ stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
+
+ return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData,
+ format,
+ extents->width,
+ extents->height,
+ stride);
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+ if (surface->imageSurfaceEquiv)
+ return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image);
+
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * Cairo surface backend implementations
+ */
+
+static cairo_status_t
+_cairo_quartz_surface_finish (void *abstract_surface)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+ ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
+
+ if (IS_EMPTY (surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Restore our saved gstate that we use to reset clipping */
+ CGContextRestoreGState (surface->cgContext);
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ CGContextRelease (surface->cgContext);
+
+ surface->cgContext = NULL;
+
+ if (surface->imageSurfaceEquiv) {
+ cairo_surface_destroy (surface->imageSurfaceEquiv);
+ surface->imageSurfaceEquiv = NULL;
+ }
+
+ free (surface->imageData);
+ surface->imageData = NULL;
+
+ _cairo_quartz_surface_shadow_caches_destroy ();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+ //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
+
+ *image_extra = NULL;
+
+ *image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents);
+ if (unlikely (cairo_surface_status(&(*image_out)->base))) {
+ cairo_surface_destroy (&(*image_out)->base);
+ *image_out = NULL;
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_quartz_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ _cairo_quartz_surface_unmap_image (abstract_surface, image);
+}
+
+static cairo_surface_t *
+_cairo_quartz_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_quartz_surface_t *surface, *similar_quartz;
+ cairo_surface_t *similar;
+ cairo_format_t format;
+
+ if (content == CAIRO_CONTENT_COLOR_ALPHA)
+ format = CAIRO_FORMAT_ARGB32;
+ else if (content == CAIRO_CONTENT_COLOR)
+ format = CAIRO_FORMAT_RGB24;
+ else if (content == CAIRO_CONTENT_ALPHA)
+ format = CAIRO_FORMAT_A8;
+ else
+ return NULL;
+
+ // verify width and height of surface
+ if (!_cairo_quartz_verify_surface_size (width, height)) {
+ return _cairo_surface_create_in_error (_cairo_error
+ (CAIRO_STATUS_INVALID_SIZE));
+ }
+
+ similar = cairo_quartz_surface_create (format, width, height);
+ if (unlikely (similar->status))
+ return similar;
+
+ surface = (cairo_quartz_surface_t *) abstract_surface;
+ similar_quartz = (cairo_quartz_surface_t *) similar;
+ similar_quartz->virtual_extents = surface->virtual_extents;
+
+ return similar;
+}
+
+static cairo_bool_t
+_cairo_quartz_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+ *extents = surface->extents;
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_quartz_drawing_state_t state;
+ cairo_int_status_t rv;
+
+ ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
+ extents->surface, extents->op, extents->source_pattern.base.type));
+
+ rv = _cairo_quartz_setup_state (&state, extents);
+ if (unlikely (rv))
+ goto BAIL;
+
+ _cairo_quartz_draw_source (&state, extents->op);
+
+BAIL:
+ _cairo_quartz_teardown_state (&state, extents);
+
+ ND ((stderr, "-- paint\n"));
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
+ cairo_surface_t *mask_surf,
+ const cairo_matrix_t *mask_mat,
+ CGInterpolationQuality filter)
+{
+ CGRect rect;
+ CGImageRef img;
+ cairo_status_t status;
+ CGAffineTransform mask_matrix;
+ cairo_quartz_drawing_state_t state;
+ cairo_format_t format = _cairo_format_from_content (extents->surface->content);
+ cairo_rectangle_int_t dest_extents;
+ cairo_matrix_t m = *mask_mat;
+
+ _cairo_surface_get_extents (extents->surface, &dest_extents);
+ status = _cairo_surface_to_cgimage (&extents->mask_pattern.base,
+ mask_surf, &dest_extents, format,
+ &m, extents->clip, &img);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_quartz_setup_state (&state, extents);
+ if (unlikely (status))
+ goto BAIL;
+
+ rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix);
+
+ /* ClipToMask is essentially drawing an image, so we need to flip the CTM
+ * to get the image to appear oriented the right way */
+ CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
+ CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
+ CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
+
+ state.filter = filter;
+
+ CGContextSetInterpolationQuality (state.cgMaskContext, filter);
+ CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
+
+ CGContextClipToMask (state.cgMaskContext, rect, img);
+
+ CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
+ CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
+ CGContextConcatCTM (state.cgMaskContext, mask_matrix);
+
+ _cairo_quartz_draw_source (&state, extents->op);
+
+BAIL:
+ _cairo_quartz_teardown_state (&state, extents);
+
+ CGImageRelease (img);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_quartz_drawing_state_t state;
+ double alpha = extents->mask_pattern.solid.color.alpha;
+ cairo_status_t status;
+
+ status = _cairo_quartz_setup_state (&state, extents);
+ if (unlikely (status))
+ return status;
+
+ CGContextSetAlpha (surface->cgContext, alpha);
+ _cairo_quartz_draw_source (&state, extents->op);
+
+ _cairo_quartz_teardown_state (&state, extents);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
+ const cairo_pattern_t *source = &extents->source_pattern.base;
+ const cairo_pattern_t *mask = &extents->mask_pattern.base;
+ cairo_surface_t *mask_surf;
+ cairo_matrix_t matrix;
+ cairo_status_t status;
+ cairo_bool_t need_temp;
+ CGInterpolationQuality filter;
+
+ ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
+ extents->surface, extents->op, extents->source_pattern.base.type,
+ extents->mask_pattern.base.type));
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
+ return _cairo_quartz_cg_mask_with_solid (surface, extents);
+
+ need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE ||
+ mask->extend != CAIRO_EXTEND_NONE);
+
+ filter = _cairo_quartz_filter_to_quartz (source->filter);
+
+ if (! need_temp) {
+ mask_surf = extents->mask_pattern.surface.surface;
+
+ /* When an opaque surface used as a mask in Quartz, its
+ * luminosity is used as the alpha value, so we con only use
+ * surfaces with alpha without creating a temporary mask. */
+ need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
+ }
+
+ if (! need_temp) {
+ CGInterpolationQuality mask_filter;
+ cairo_bool_t simple_transform;
+
+ matrix = mask->matrix;
+
+ mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
+ if (mask_filter == kCGInterpolationNone) {
+ simple_transform = _cairo_matrix_is_translation (&matrix);
+ if (simple_transform) {
+ matrix.x0 = ceil (matrix.x0 - 0.5);
+ matrix.y0 = ceil (matrix.y0 - 0.5);
+ }
+ } else {
+ simple_transform = _cairo_matrix_is_integer_translation (&matrix,
+ NULL,
+ NULL);
+ }
+
+ /* Quartz only allows one interpolation to be set for mask and
+ * source, so we can skip the temp surface only if the source
+ * filtering makes the mask look correct. */
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
+ need_temp = ! (simple_transform || filter == mask_filter);
+ else
+ filter = mask_filter;
+ }
+
+ if (need_temp) {
+ /* Render the mask to a surface */
+ mask_surf = _cairo_quartz_surface_create_similar (surface,
+ CAIRO_CONTENT_ALPHA,
+ surface->extents.width,
+ surface->extents.height);
+ status = mask_surf->status;
+ if (unlikely (status))
+ goto BAIL;
+
+ /* mask_surf is clear, so use OVER instead of SOURCE to avoid a
+ * temporary layer or fallback to cairo-image. */
+ status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
+ if (unlikely (status))
+ goto BAIL;
+
+ cairo_matrix_init_identity (&matrix);
+ }
+
+ status = _cairo_quartz_cg_mask_with_surface (extents,
+ mask_surf, &matrix, filter);
+
+BAIL:
+
+ if (need_temp)
+ cairo_surface_destroy (mask_surf);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_quartz_drawing_state_t state;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+
+ ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
+ extents->surface, extents->op, extents->source_pattern.base.type));
+
+ rv = _cairo_quartz_setup_state (&state, extents);
+ if (unlikely (rv))
+ goto BAIL;
+
+ CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
+
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
+
+ if (state.action == DO_DIRECT) {
+ assert (state.cgDrawContext == state.cgMaskContext);
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextFillPath (state.cgMaskContext);
+ else
+ CGContextEOFillPath (state.cgMaskContext);
+ } else {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextClip (state.cgMaskContext);
+ else
+ CGContextEOClip (state.cgMaskContext);
+
+ _cairo_quartz_draw_source (&state, extents->op);
+ }
+
+BAIL:
+ _cairo_quartz_teardown_state (&state, extents);
+
+ ND ((stderr, "-- fill\n"));
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_quartz_drawing_state_t state;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+ CGAffineTransform strokeTransform, invStrokeTransform;
+
+ ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
+ extents->surface, extents->op, extents->source_pattern.base.type));
+
+ rv = _cairo_quartz_setup_state (&state, extents);
+ if (unlikely (rv))
+ goto BAIL;
+
+ // Turning antialiasing off used to cause misrendering with
+ // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
+ // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
+ CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
+ CGContextSetLineWidth (state.cgMaskContext, style->line_width);
+ CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+ CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+ CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
+
+ if (style->dash && style->num_dashes) {
+ cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
+ cairo_quartz_float_t *fdash = sdash;
+ unsigned int max_dashes = style->num_dashes;
+ unsigned int k;
+
+ if (style->num_dashes%2)
+ max_dashes *= 2;
+ if (max_dashes > ARRAY_LENGTH (sdash))
+ fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
+ if (unlikely (fdash == NULL)) {
+ rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ for (k = 0; k < max_dashes; k++)
+ fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
+
+ CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
+ if (fdash != sdash)
+ free (fdash);
+ } else
+ CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
+
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
+
+ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+ CGContextConcatCTM (state.cgMaskContext, strokeTransform);
+
+ if (state.action == DO_DIRECT) {
+ assert (state.cgDrawContext == state.cgMaskContext);
+ CGContextStrokePath (state.cgMaskContext);
+ } else {
+ CGContextReplacePathWithStrokedPath (state.cgMaskContext);
+ CGContextClip (state.cgMaskContext);
+
+ _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
+ CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
+
+ _cairo_quartz_draw_source (&state, extents->op);
+ }
+
+BAIL:
+ _cairo_quartz_teardown_state (&state, extents);
+
+ ND ((stderr, "-- stroke\n"));
+ return rv;
+}
+
+#if CAIRO_HAS_QUARTZ_FONT
+static cairo_int_status_t
+_cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ CGAffineTransform textTransform, invTextTransform;
+ CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
+ CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
+ CGGlyph *cg_glyphs = &glyphs_static[0];
+ CGSize *cg_advances = &cg_advances_static[0];
+ COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
+
+ cairo_quartz_drawing_state_t state;
+ cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ cairo_quartz_float_t xprev, yprev;
+ int i;
+ CGFontRef cgfref = NULL;
+
+ cairo_bool_t didForceFontSmoothing = FALSE;
+
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ rv = _cairo_quartz_setup_state (&state, extents);
+ if (unlikely (rv))
+ goto BAIL;
+
+ if (state.action == DO_DIRECT) {
+ assert (state.cgDrawContext == state.cgMaskContext);
+ CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
+ } else {
+ CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
+ }
+
+ /* this doesn't addref */
+ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+ CGContextSetFont (state.cgMaskContext, cgfref);
+ CGContextSetFontSize (state.cgMaskContext, 1.0);
+
+ switch (scaled_font->options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ case CAIRO_ANTIALIAS_BEST:
+ CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
+ CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
+ if (CGContextSetAllowsFontSmoothingPtr &&
+ !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
+ {
+ didForceFontSmoothing = TRUE;
+ CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
+ }
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+ CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_GOOD:
+ case CAIRO_ANTIALIAS_FAST:
+ CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
+ CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_DEFAULT:
+ /* Don't do anything */
+ break;
+ }
+
+ if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
+ cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
+ if (unlikely (cg_glyphs == NULL)) {
+ rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
+ }
+
+ /* scale(1,-1) * scaled_font->scale */
+ textTransform = CGAffineTransformMake (scaled_font->scale.xx,
+ scaled_font->scale.yx,
+ -scaled_font->scale.xy,
+ -scaled_font->scale.yy,
+ 0.0, 0.0);
+
+ /* scaled_font->scale_inverse * scale(1,-1) */
+ invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
+ -scaled_font->scale_inverse.yx,
+ scaled_font->scale_inverse.xy,
+ -scaled_font->scale_inverse.yy,
+ 0.0, 0.0);
+
+ CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
+ CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
+
+ /* Convert our glyph positions to glyph advances. We need n-1 advances,
+ * since the advance at index 0 is applied after glyph 0. */
+ xprev = glyphs[0].x;
+ yprev = glyphs[0].y;
+
+ cg_glyphs[0] = glyphs[0].index;
+
+ for (i = 1; i < num_glyphs; i++) {
+ cairo_quartz_float_t xf = glyphs[i].x;
+ cairo_quartz_float_t yf = glyphs[i].y;
+ cg_glyphs[i] = glyphs[i].index;
+ cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+ xprev = xf;
+ yprev = yf;
+ }
+
+ /* Translate to the first glyph's position before drawing */
+ CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
+ CGContextConcatCTM (state.cgMaskContext, textTransform);
+
+ CGContextShowGlyphsWithAdvances (state.cgMaskContext,
+ cg_glyphs,
+ cg_advances,
+ num_glyphs);
+
+ CGContextConcatCTM (state.cgMaskContext, invTextTransform);
+ CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
+
+ if (state.action != DO_DIRECT)
+ _cairo_quartz_draw_source (&state, extents->op);
+
+BAIL:
+ if (didForceFontSmoothing)
+ CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
+
+ _cairo_quartz_teardown_state (&state, extents);
+
+ if (cg_glyphs != glyphs_static)
+ free (cg_glyphs);
+
+ return rv;
+}
+#endif /* CAIRO_HAS_QUARTZ_FONT */
+
+static const cairo_compositor_t _cairo_quartz_cg_compositor = {
+ &_cairo_fallback_compositor,
+
+ _cairo_quartz_cg_paint,
+ _cairo_quartz_cg_mask,
+ _cairo_quartz_cg_stroke,
+ _cairo_quartz_cg_fill,
+#if CAIRO_HAS_QUARTZ_FONT
+ _cairo_quartz_cg_glyphs,
+#else
+ NULL,
+#endif
+};
+
+static cairo_int_status_t
+_cairo_quartz_surface_paint (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_quartz_surface_t *quartz_surface = surface;
+
+ status = cairo_device_acquire (quartz_surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_paint (surface, op, source, clip,
+ &source->shadow);
+
+ if (source->shadow.draw_shadow_only ||
+ unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ status = _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
+ surface, op, source, clip);
+
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_mask (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_quartz_surface_t *quartz_surface = surface;
+
+ status = cairo_device_acquire (quartz_surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_mask (surface, op, source, mask, clip,
+ &source->shadow);
+
+ if (source->shadow.draw_shadow_only ||
+ unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ status = _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
+ surface, op, source, mask,
+ clip);
+
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_fill (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_quartz_surface_t *quartz_surface = surface;
+
+ status = cairo_device_acquire (quartz_surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (surface, op, source, path,
+ fill_rule, tolerance, antialias,
+ clip, &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only) {
+ if (! source->shadow.path_is_fill_with_spread ||
+ source->shadow.type != CAIRO_SHADOW_INSET)
+ status = _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
+ surface, op, source, path,
+ fill_rule, tolerance,
+ antialias, clip);
+ else
+ status = _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
+ surface, op, source, clip);
+ }
+
+ if (unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (surface, op, source, path,
+ fill_rule, tolerance, antialias,
+ clip, &source->shadow);
+
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_stroke (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_quartz_surface_t *quartz_surface = surface;
+
+ status = cairo_device_acquire (quartz_surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (surface, op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip,
+ &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (source->shadow.draw_shadow_only &&
+ shadow_type == CAIRO_SHADOW_DROP) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
+ surface, op, source, path,
+ style, ctm,ctm_inverse,
+ tolerance, antialias, clip);
+
+ if (unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (surface, op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip,
+ &source->shadow);
+
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_quartz_surface_t *quartz_surface = surface;
+
+ status = cairo_device_acquire (quartz_surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (surface, op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip, &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (source->shadow.draw_shadow_only &&
+ shadow_type == CAIRO_SHADOW_INSET) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
+ surface, op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+
+ if (unlikely (status)) {
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (surface, op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip, &source->shadow);
+ cairo_device_release (quartz_surface->base.device);
+ return status;
+}
+
+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;
+}
+
+// XXXtodo implement show_page; need to figure out how to handle begin/end
+
+static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
+ CAIRO_SURFACE_TYPE_QUARTZ,
+ _cairo_quartz_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_quartz_surface_create_similar,
+ NULL, /* similar image */
+ _cairo_quartz_surface_map_to_image,
+ _cairo_quartz_surface_unmap_image,
+
+ _cairo_surface_default_source,
+ _cairo_quartz_surface_acquire_source_image,
+ _cairo_quartz_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_quartz_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_quartz_surface_paint,
+ _cairo_quartz_surface_mask,
+ _cairo_quartz_surface_stroke,
+ _cairo_quartz_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_quartz_surface_glyphs,
+ NULL, /* has_text_glyphs */
+ NULL, /* show_text_glyphs */
+ NULL, /* get_supported_mime_types */
+ NULL, /* get_shadow_surface */
+ NULL, /* get_glyph_shadow_surface */
+ NULL, /* get_shadow_mask_surface */
+ NULL, /* get_glyph_shadow_mask_surface */
+ _cairo_quartz_surface_shadow_cache_acquire,
+ _cairo_quartz_surface_shadow_cache_release,
+ _cairo_quartz_surface_get_shadow_cache,
+ _cairo_quartz_surface_get_shadow_cache_size,
+ _cairo_quartz_surface_has_shadow_cache,
+};
+
+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 (unlikely (surface == NULL))
+ return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (surface, 0, sizeof (cairo_quartz_surface_t));
+
+ _cairo_surface_init (&surface->base,
+ &cairo_quartz_surface_backend,
+ NULL, /* device */
+ content);
+
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_quartz_surface_clipper_intersect_clip_path);
+
+ /* Save our extents */
+ surface->extents.x = surface->extents.y = 0;
+ surface->extents.width = width;
+ surface->extents.height = height;
+ surface->virtual_extents = surface->extents;
+
+ if (IS_EMPTY (surface)) {
+ surface->cgContext = NULL;
+ surface->cgContextBaseCTM = CGAffineTransformIdentity;
+ surface->imageData = NULL;
+ surface->base.is_clear = TRUE;
+ return surface;
+ }
+
+ /* Save so we can always get back to a known-good CGContext -- this is
+ * required for proper behaviour of intersect_clip_path(NULL)
+ */
+ CGContextSaveGState (cgContext);
+
+ surface->cgContext = cgContext;
+ surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
+
+ surface->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
+
+ _cairo_quartz_surface_shadow_caches_init ();
+
+ 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.6
+ **/
+
+cairo_surface_t *
+cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_quartz_surface_t *surf;
+
+ surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
+ if (likely (!surf->base.status))
+ CGContextRetain (cgContext);
+
+ return &surf->base;
+}
+
+/**
+ * cairo_quartz_surface_create:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a Quartz surface backed by a CGBitmap. The surface is
+ * created using the Device RGB (or Device Gray, for A8) color space.
+ * All Cairo operations, including those that require software
+ * rendering, will succeed on this surface.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.6
+ **/
+cairo_surface_t *
+cairo_quartz_surface_create (cairo_format_t format,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_quartz_surface_t *surf;
+ CGContextRef cgc;
+ CGColorSpaceRef cgColorspace;
+ CGBitmapInfo bitinfo;
+ void *imageData;
+ int stride;
+ int bitsPerComponent;
+
+ if (!_cairo_quartz_verify_surface_size (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ if (width == 0 || height == 0) {
+ return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
+ width, height)->base;
+ }
+
+ if (format == CAIRO_FORMAT_ARGB32 ||
+ format == CAIRO_FORMAT_RGB24)
+ {
+ cgColorspace = CGColorSpaceCreateDeviceRGB ();
+ bitinfo = kCGBitmapByteOrder32Host;
+ if (format == CAIRO_FORMAT_ARGB32)
+ bitinfo |= kCGImageAlphaPremultipliedFirst;
+ else
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
+ bitsPerComponent = 8;
+ stride = width * 4;
+ } else if (format == CAIRO_FORMAT_A8) {
+ cgColorspace = NULL;
+ stride = width;
+ bitinfo = kCGImageAlphaOnly;
+ bitsPerComponent = 8;
+ } else if (format == CAIRO_FORMAT_A1) {
+ /* I don't think we can usefully support this, as defined by
+ * cairo_format_t -- these are 1-bit pixels stored in 32-bit
+ * quantities.
+ */
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ } else {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ /* The Apple docs say that for best performance, the stride and the data
+ * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
+ * so we don't have to anything special on allocation.
+ */
+ stride = (stride + 15) & ~15;
+
+ imageData = _cairo_malloc_ab (height, stride);
+ if (unlikely (!imageData)) {
+ CGColorSpaceRelease (cgColorspace);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ /* zero the memory to match the image surface behaviour */
+ memset (imageData, 0, height * stride);
+
+ cgc = CGBitmapContextCreate (imageData,
+ width,
+ height,
+ bitsPerComponent,
+ stride,
+ cgColorspace,
+ bitinfo);
+ CGColorSpaceRelease (cgColorspace);
+
+ if (!cgc) {
+ free (imageData);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ /* flip the Y axis */
+ CGContextTranslateCTM (cgc, 0.0, height);
+ CGContextScaleCTM (cgc, 1.0, -1.0);
+
+ surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
+ width, height);
+ if (surf->base.status) {
+ CGContextRelease (cgc);
+ free (imageData);
+ // create_internal will have set an error
+ return &surf->base;
+ }
+
+ surf->base.is_clear = TRUE;
+
+ surf->imageData = imageData;
+ surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
+
+ return &surf->base;
+}
+
+/**
+ * cairo_quartz_surface_get_cg_context:
+ * @surface: the Cairo Quartz surface
+ *
+ * Returns the CGContextRef that the given Quartz surface is backed
+ * by.
+ *
+ * A call to cairo_surface_flush() is required before using the
+ * CGContextRef to ensure that all pending drawing operations are
+ * finished and to restore any temporary modification cairo has made
+ * to its state. A call to cairo_surface_mark_dirty() is required
+ * after the state or the content of the CGContextRef has been
+ * modified.
+ *
+ * Return value: the CGContextRef for the given surface.
+ *
+ * Since: 1.6
+ **/
+CGContextRef
+cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
+{
+ if (surface && _cairo_surface_is_quartz (surface)) {
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
+ return quartz->cgContext;
+ } else
+ return NULL;
+}
+
+static cairo_bool_t
+_cairo_surface_is_quartz (const cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_quartz_surface_backend;
+}
+
+/* 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/src/cairo-quartz.h b/src/cairo-quartz.h
new file mode 100755
index 000000000..9be5f9ae5
--- /dev/null
+++ b/src/cairo-quartz.h
@@ -0,0 +1,82 @@
+/* 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 <ApplicationServices/ApplicationServices.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_quartz_surface_create (cairo_format_t format,
+ unsigned int width,
+ unsigned int height);
+
+cairo_public cairo_surface_t *
+cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+ unsigned int width,
+ unsigned int height);
+
+cairo_public CGContextRef
+cairo_quartz_surface_get_cg_context (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);
+
+cairo_public cairo_font_face_t *
+cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+
+#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/src/cairo-raster-source-pattern.c b/src/cairo-raster-source-pattern.c
new file mode 100755
index 000000000..2fd8bdb63
--- /dev/null
+++ b/src/cairo-raster-source-pattern.c
@@ -0,0 +1,432 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+
+/**
+ * SECTION:cairo-raster-source
+ * @Title: Raster Sources
+ * @Short_Description: Supplying arbitrary image data
+ * @See_Also: #cairo_pattern_t
+ *
+ * The raster source provides the ability to supply arbitrary pixel data
+ * whilst rendering. The pixels are queried at the time of rasterisation
+ * by means of user callback functions, allowing for the ultimate
+ * flexibility. For example, in handling compressed image sources, you
+ * may keep a MRU cache of decompressed images and decompress sources on the
+ * fly and discard old ones to conserve memory.
+ *
+ * For the raster source to be effective, you must at least specify
+ * the acquire and release callbacks which are used to retrieve the pixel
+ * data for the region of interest and demark when it can be freed afterwards.
+ * Other callbacks are provided for when the pattern is copied temporarily
+ * during rasterisation, or more permanently as a snapshot in order to keep
+ * the pixel data available for printing.
+ *
+ * Since: 1.12
+ **/
+
+cairo_surface_t *
+_cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern,
+ cairo_surface_t *target,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_raster_source_pattern_t *pattern =
+ (cairo_raster_source_pattern_t *) abstract_pattern;
+
+ if (pattern->acquire == NULL)
+ return NULL;
+
+ if (extents == NULL)
+ extents = &pattern->extents;
+
+ return pattern->acquire (&pattern->base, pattern->user_data,
+ target, extents);
+}
+
+void
+_cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern,
+ cairo_surface_t *surface)
+{
+ cairo_raster_source_pattern_t *pattern =
+ (cairo_raster_source_pattern_t *) abstract_pattern;
+
+ if (pattern->release == NULL)
+ return;
+
+ pattern->release (&pattern->base, pattern->user_data, surface);
+}
+
+cairo_status_t
+_cairo_raster_source_pattern_init_copy (cairo_pattern_t *abstract_pattern,
+ const cairo_pattern_t *other)
+{
+ cairo_raster_source_pattern_t *pattern =
+ (cairo_raster_source_pattern_t *) abstract_pattern;
+ cairo_status_t status;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_raster_source_pattern_t)));
+ memcpy(pattern, (cairo_raster_source_pattern_t *) other, sizeof (cairo_raster_source_pattern_t));
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (pattern->copy)
+ status = pattern->copy (&pattern->base, pattern->user_data, other);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern)
+{
+ cairo_raster_source_pattern_t *pattern =
+ (cairo_raster_source_pattern_t *) abstract_pattern;
+
+ if (pattern->snapshot == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ return pattern->snapshot (&pattern->base, pattern->user_data);
+}
+
+void
+_cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern)
+{
+ cairo_raster_source_pattern_t *pattern =
+ (cairo_raster_source_pattern_t *) abstract_pattern;
+
+ if (pattern->finish == NULL)
+ return;
+
+ pattern->finish (&pattern->base, pattern->user_data);
+}
+
+/* Public interface */
+
+/**
+ * cairo_pattern_create_raster_source:
+ * @user_data: the user data to be passed to all callbacks
+ * @content: content type for the pixel data that will be returned. Knowing
+ * the content type ahead of time is used for analysing the operation and
+ * picking the appropriate rendering path.
+ * @width: maximum size of the sample area
+ * @height: maximum size of the sample area
+ *
+ * Creates a new user pattern for providing pixel data.
+ *
+ * Use the setter functions to associate callbacks with the returned
+ * pattern. The only mandatory callback is acquire.
+ *
+ * Return value: a newly created #cairo_pattern_t. Free with
+ * cairo_pattern_destroy() when you are done using it.
+ *
+ * Since: 1.12
+ **/
+cairo_pattern_t *
+cairo_pattern_create_raster_source (void *user_data,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ if (width < 0 || height < 0)
+ return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+ if (! CAIRO_CONTENT_VALID (content))
+ return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
+
+ pattern = calloc (1, sizeof (*pattern));
+ if (unlikely (pattern == NULL))
+ return _cairo_pattern_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_pattern_init (&pattern->base,
+ CAIRO_PATTERN_TYPE_RASTER_SOURCE);
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+ pattern->content = content;
+
+ pattern->extents.x = 0;
+ pattern->extents.y = 0;
+ pattern->extents.width = width;
+ pattern->extents.height = height;
+
+ pattern->user_data = user_data;
+
+ return &pattern->base;
+}
+
+/**
+ * cairo_raster_source_pattern_set_callback_data:
+ * @pattern: the pattern to update
+ * @data: the user data to be passed to all callbacks
+ *
+ * Updates the user data that is provided to all callbacks.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *abstract_pattern,
+ void *data)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ pattern->user_data = data;
+}
+
+/**
+ * cairo_raster_source_pattern_get_callback_data:
+ * @pattern: the pattern to update
+ *
+ * Queries the current user data.
+ *
+ * Return value: the current user-data passed to each callback
+ *
+ * Since: 1.12
+ **/
+void *
+cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *abstract_pattern)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return NULL;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ return pattern->user_data;
+}
+
+/**
+ * cairo_raster_source_pattern_set_acquire:
+ * @pattern: the pattern to update
+ * @acquire: acquire callback
+ * @release: release callback
+ *
+ * Specifies the callbacks used to generate the image surface for a rendering
+ * operation (acquire) and the function used to cleanup that surface afterwards.
+ *
+ * The @acquire callback should create a surface (preferably an image
+ * surface created to match the target using
+ * cairo_surface_create_similar_image()) that defines at least the region
+ * of interest specified by extents. The surface is allowed to be the entire
+ * sample area, but if it does contain a subsection of the sample area,
+ * the surface extents should be provided by setting the device offset (along
+ * with its width and height) using cairo_surface_set_device_offset().
+ *
+ * Since: 1.12
+ **/
+void
+cairo_raster_source_pattern_set_acquire (cairo_pattern_t *abstract_pattern,
+ cairo_raster_source_acquire_func_t acquire,
+ cairo_raster_source_release_func_t release)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ pattern->acquire = acquire;
+ pattern->release = release;
+}
+
+/**
+ * cairo_raster_source_pattern_get_acquire:
+ * @pattern: the pattern to query
+ * @acquire: return value for the current acquire callback
+ * @release: return value for the current release callback
+ *
+ * Queries the current acquire and release callbacks.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_raster_source_pattern_get_acquire (cairo_pattern_t *abstract_pattern,
+ cairo_raster_source_acquire_func_t *acquire,
+ cairo_raster_source_release_func_t *release)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ if (acquire)
+ *acquire = pattern->acquire;
+ if (release)
+ *release = pattern->release;
+}
+
+/**
+ * cairo_raster_source_pattern_set_snapshot:
+ * @pattern: the pattern to update
+ * @snapshot: snapshot callback
+ *
+ * Sets the callback that will be used whenever a snapshot is taken of the
+ * pattern, that is whenever the current contents of the pattern should be
+ * preserved for later use. This is typically invoked whilst printing.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *abstract_pattern,
+ cairo_raster_source_snapshot_func_t snapshot)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ pattern->snapshot = snapshot;
+}
+
+/**
+ * cairo_raster_source_pattern_get_snapshot:
+ * @pattern: the pattern to query
+ *
+ * Queries the current snapshot callback.
+ *
+ * Return value: the current snapshot callback
+ *
+ * Since: 1.12
+ **/
+cairo_raster_source_snapshot_func_t
+cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *abstract_pattern)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return NULL;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ return pattern->snapshot;
+}
+
+/**
+ * cairo_raster_source_pattern_set_copy:
+ * @pattern: the pattern to update
+ * @copy: the copy callback
+ *
+ * Updates the copy callback which is used whenever a temporary copy of the
+ * pattern is taken.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_raster_source_pattern_set_copy (cairo_pattern_t *abstract_pattern,
+ cairo_raster_source_copy_func_t copy)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ pattern->copy = copy;
+}
+
+/**
+ * cairo_raster_source_pattern_get_copy:
+ * @pattern: the pattern to query
+ *
+ * Queries the current copy callback.
+ *
+ * Return value: the current copy callback
+ *
+ * Since: 1.12
+ **/
+cairo_raster_source_copy_func_t
+cairo_raster_source_pattern_get_copy (cairo_pattern_t *abstract_pattern)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return NULL;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ return pattern->copy;
+}
+
+/**
+ * cairo_raster_source_pattern_set_finish:
+ * @pattern: the pattern to update
+ * @finish: the finish callback
+ *
+ * Updates the finish callback which is used whenever a pattern (or a copy
+ * thereof) will no longer be used.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_raster_source_pattern_set_finish (cairo_pattern_t *abstract_pattern,
+ cairo_raster_source_finish_func_t finish)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ pattern->finish = finish;
+}
+
+/**
+ * cairo_raster_source_pattern_get_finish:
+ * @pattern: the pattern to query
+ *
+ * Queries the current finish callback.
+ *
+ * Return value: the current finish callback
+ *
+ * Since: 1.12
+ **/
+cairo_raster_source_finish_func_t
+cairo_raster_source_pattern_get_finish (cairo_pattern_t *abstract_pattern)
+{
+ cairo_raster_source_pattern_t *pattern;
+
+ if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
+ return NULL;
+
+ pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
+ return pattern->finish;
+}
diff --git a/src/cairo-recording-surface-inline.h b/src/cairo-recording-surface-inline.h
new file mode 100755
index 000000000..9002ccd69
--- /dev/null
+++ b/src/cairo-recording-surface-inline.h
@@ -0,0 +1,68 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_RECORDING_SURFACE_INLINE_H
+#define CAIRO_RECORDING_SURFACE_INLINE_H
+
+#include "cairo-recording-surface-private.h"
+
+static inline cairo_bool_t
+_cairo_recording_surface_get_bounds (cairo_surface_t *surface,
+ cairo_rectangle_t *extents)
+{
+ cairo_recording_surface_t *recording = (cairo_recording_surface_t *)surface;
+ if (recording->unbounded)
+ return FALSE;
+
+ *extents = recording->extents_pixels;
+ return TRUE;
+}
+
+/**
+ * _cairo_surface_is_recording:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a #cairo_recording_surface_t
+ *
+ * Return value: %TRUE if the surface is a recording surface
+ **/
+static inline cairo_bool_t
+_cairo_surface_is_recording (const cairo_surface_t *surface)
+{
+ return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING;
+}
+
+#endif /* CAIRO_RECORDING_SURFACE_INLINE_H */
diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h
new file mode 100755
index 000000000..0235b0f3f
--- /dev/null
+++ b/src/cairo-recording-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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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-pattern-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef enum {
+ /* The 5 basic drawing operations. */
+ CAIRO_COMMAND_PAINT,
+ CAIRO_COMMAND_MASK,
+ CAIRO_COMMAND_STROKE,
+ CAIRO_COMMAND_FILL,
+ CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
+} cairo_command_type_t;
+
+typedef enum {
+ CAIRO_RECORDING_REGION_ALL,
+ CAIRO_RECORDING_REGION_NATIVE,
+ CAIRO_RECORDING_REGION_IMAGE_FALLBACK
+} cairo_recording_region_type_t;
+
+typedef struct _cairo_command_header {
+ cairo_command_type_t type;
+ cairo_recording_region_type_t region;
+ cairo_operator_t op;
+ cairo_rectangle_int_t extents;
+ cairo_clip_t *clip;
+
+ int index;
+ struct _cairo_command_header *chain;
+} cairo_command_header_t;
+
+typedef struct _cairo_command_paint {
+ cairo_command_header_t header;
+ cairo_pattern_union_t source;
+} cairo_command_paint_t;
+
+typedef struct _cairo_command_mask {
+ cairo_command_header_t header;
+ cairo_pattern_union_t source;
+ cairo_pattern_union_t mask;
+} cairo_command_mask_t;
+
+typedef struct _cairo_command_stroke {
+ cairo_command_header_t header;
+ cairo_pattern_union_t source;
+ cairo_path_fixed_t path;
+ cairo_stroke_style_t style;
+ cairo_matrix_t ctm;
+ cairo_matrix_t ctm_inverse;
+ double tolerance;
+ cairo_antialias_t antialias;
+} cairo_command_stroke_t;
+
+typedef struct _cairo_command_fill {
+ cairo_command_header_t header;
+ cairo_pattern_union_t source;
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ double tolerance;
+ cairo_antialias_t antialias;
+} cairo_command_fill_t;
+
+typedef struct _cairo_command_show_text_glyphs {
+ cairo_command_header_t header;
+ cairo_pattern_union_t source;
+ char *utf8;
+ int utf8_len;
+ cairo_glyph_t *glyphs;
+ unsigned int num_glyphs;
+ cairo_text_cluster_t *clusters;
+ int num_clusters;
+ cairo_text_cluster_flags_t cluster_flags;
+ cairo_scaled_font_t *scaled_font;
+} cairo_command_show_text_glyphs_t;
+
+typedef 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;
+
+ /* A recording-surface is logically unbounded, but when used as a
+ * source we need to render it to an image, so we need a size at
+ * which to create that image. */
+ cairo_rectangle_t extents_pixels;
+ cairo_rectangle_int_t extents;
+ cairo_bool_t unbounded;
+
+ cairo_array_t commands;
+ int *indices;
+ int num_indices;
+ cairo_bool_t optimize_clears;
+
+ struct bbtree {
+ cairo_box_t extents;
+ struct bbtree *left, *right;
+ cairo_command_header_t *chain;
+ } bbtree;
+} cairo_recording_surface_t;
+
+slim_hidden_proto (cairo_recording_surface_create);
+
+cairo_private cairo_int_status_t
+_cairo_recording_surface_get_path (cairo_surface_t *surface,
+ cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
+ long unsigned index,
+ cairo_surface_t *target);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay (cairo_surface_t *surface,
+ cairo_surface_t *target);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
+ const cairo_matrix_t *surface_transform,
+ cairo_surface_t *target,
+ const cairo_clip_t *target_clip);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
+ 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_status_t
+_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
+ cairo_box_t *bbox,
+ const cairo_matrix_t *transform);
+
+#endif /* CAIRO_RECORDING_SURFACE_H */
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
new file mode 100755
index 000000000..9e672df2c
--- /dev/null
+++ b/src/cairo-recording-surface.c
@@ -0,0 +1,2085 @@
+/* -*- 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-array-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-combsort-inline.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-surface-wrapper-private.h"
+#include "cairo-traps-private.h"
+
+typedef enum {
+ CAIRO_RECORDING_REPLAY,
+ CAIRO_RECORDING_CREATE_REGIONS
+} cairo_recording_replay_type_t;
+
+static const cairo_surface_backend_t cairo_recording_surface_backend;
+
+/**
+ * CAIRO_HAS_RECORDING_SURFACE:
+ *
+ * Defined if the recording surface backend is available.
+ * The recording surface backend is always built in.
+ * This macro was added for completeness in cairo 1.10.
+ *
+ * Since: 1.10
+ **/
+
+/* Currently all recording surfaces do have a size which should be passed
+ * in as the maximum size of any target surface against which the
+ * recording-surface will ever be replayed.
+ *
+ * XXX: The naming of "pixels" in the size here is a misnomer. It's
+ * actually a size in whatever device-space units are desired (again,
+ * according to the intended replay target).
+ */
+
+static int bbtree_left_or_right (struct bbtree *bbt,
+ const cairo_box_t *box)
+{
+ int left, right;
+
+ if (bbt->left) {
+ cairo_box_t *e = &bbt->left->extents;
+ cairo_box_t b;
+
+ b.p1.x = MIN (e->p1.x, box->p1.x);
+ b.p1.y = MIN (e->p1.y, box->p1.y);
+ b.p2.x = MAX (e->p2.x, box->p2.x);
+ b.p2.y = MAX (e->p2.y, box->p2.y);
+
+ left = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
+ left -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
+ } else
+ left = 0;
+
+ if (bbt->right) {
+ cairo_box_t *e = &bbt->right->extents;
+ cairo_box_t b;
+
+ b.p1.x = MIN (e->p1.x, box->p1.x);
+ b.p1.y = MIN (e->p1.y, box->p1.y);
+ b.p2.x = MAX (e->p2.x, box->p2.x);
+ b.p2.y = MAX (e->p2.y, box->p2.y);
+
+ right = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
+ right -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
+ } else
+ right = 0;
+
+ return left <= right;
+}
+
+#define INVALID_CHAIN ((cairo_command_header_t *)-1)
+
+static struct bbtree *
+bbtree_new (const cairo_box_t *box, cairo_command_header_t *chain)
+{
+ struct bbtree *bbt = malloc (sizeof (*bbt));
+ if (bbt == NULL)
+ return NULL;
+ bbt->extents = *box;
+ bbt->left = bbt->right = NULL;
+ bbt->chain = chain;
+ return bbt;
+}
+
+static void
+bbtree_init (struct bbtree *bbt, cairo_command_header_t *header)
+{
+ _cairo_box_from_rectangle (&bbt->extents, &header->extents);
+ bbt->chain = header;
+}
+
+static cairo_status_t
+bbtree_add (struct bbtree *bbt,
+ cairo_command_header_t *header,
+ const cairo_box_t *box)
+{
+ if (box->p1.x < bbt->extents.p1.x || box->p1.y < bbt->extents.p1.y ||
+ box->p2.x > bbt->extents.p2.x || box->p2.y > bbt->extents.p2.y)
+ {
+ if (bbt->chain) {
+ if (bbtree_left_or_right (bbt, &bbt->extents)) {
+ if (bbt->left == NULL) {
+ bbt->left = bbtree_new (&bbt->extents, bbt->chain);
+ if (unlikely (bbt->left == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else
+ bbtree_add (bbt->left, bbt->chain, &bbt->extents);
+ } else {
+ if (bbt->right == NULL) {
+ bbt->right = bbtree_new (&bbt->extents, bbt->chain);
+ if (unlikely (bbt->right == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else
+ bbtree_add (bbt->right, bbt->chain, &bbt->extents);
+ }
+
+ bbt->chain = NULL;
+ }
+
+ bbt->extents.p1.x = MIN (bbt->extents.p1.x, box->p1.x);
+ bbt->extents.p1.y = MIN (bbt->extents.p1.y, box->p1.y);
+ bbt->extents.p2.x = MAX (bbt->extents.p2.x, box->p2.x);
+ bbt->extents.p2.y = MAX (bbt->extents.p2.y, box->p2.y);
+ }
+
+ if (box->p1.x == bbt->extents.p1.x && box->p1.y == bbt->extents.p1.y &&
+ box->p2.x == bbt->extents.p2.x && box->p2.y == bbt->extents.p2.y)
+ {
+ cairo_command_header_t *last = header;
+ while (last->chain) /* expected to be infrequent */
+ last = last->chain;
+ last->chain = bbt->chain;
+ bbt->chain = header;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (bbtree_left_or_right (bbt, box)) {
+ if (bbt->left == NULL) {
+ bbt->left = bbtree_new (box, header);
+ if (unlikely (bbt->left == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else
+ return bbtree_add (bbt->left, header, box);
+ } else {
+ if (bbt->right == NULL) {
+ bbt->right = bbtree_new (box, header);
+ if (unlikely (bbt->right == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else
+ return bbtree_add (bbt->right, header, box);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void bbtree_del (struct bbtree *bbt)
+{
+ if (bbt->left)
+ bbtree_del (bbt->left);
+ if (bbt->right)
+ bbtree_del (bbt->right);
+
+ free (bbt);
+}
+
+static cairo_bool_t box_outside (const cairo_box_t *a, const cairo_box_t *b)
+{
+ return
+ a->p1.x >= b->p2.x || a->p1.y >= b->p2.y ||
+ a->p2.x <= b->p1.x || a->p2.y <= b->p1.y;
+}
+
+static void
+bbtree_foreach_mark_visible (struct bbtree *bbt,
+ const cairo_box_t *box,
+ int **indices)
+{
+ cairo_command_header_t *chain;
+
+ for (chain = bbt->chain; chain; chain = chain->chain)
+ *(*indices)++ = chain->index;
+
+ if (bbt->left && ! box_outside (box, &bbt->left->extents))
+ bbtree_foreach_mark_visible (bbt->left, box, indices);
+ if (bbt->right && ! box_outside (box, &bbt->right->extents))
+ bbtree_foreach_mark_visible (bbt->right, box, indices);
+}
+
+static inline int intcmp (const int a, const int b)
+{
+ return a - b;
+}
+CAIRO_COMBSORT_DECLARE (sort_indices, int, intcmp)
+
+static inline int sizecmp (int a, int b, cairo_command_header_t **elements)
+{
+ const cairo_rectangle_int_t *r;
+
+ r = &elements[a]->extents;
+ a = r->width * r->height;
+
+ r = &elements[b]->extents;
+ b = r->width * r->height;
+
+ return b - a;
+}
+CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_commands, int, sizecmp)
+
+static void
+_cairo_recording_surface_destroy_bbtree (cairo_recording_surface_t *surface)
+{
+ cairo_command_t **elements;
+ int i, num_elements;
+
+ if (surface->bbtree.chain == INVALID_CHAIN)
+ return;
+
+ if (surface->bbtree.left) {
+ bbtree_del (surface->bbtree.left);
+ surface->bbtree.left = NULL;
+ }
+ if (surface->bbtree.right) {
+ bbtree_del (surface->bbtree.right);
+ surface->bbtree.right = NULL;
+ }
+
+ elements = _cairo_array_index (&surface->commands, 0);
+ num_elements = surface->commands.num_elements;
+ for (i = 0; i < num_elements; i++)
+ elements[i]->header.chain = NULL;
+
+ surface->bbtree.chain = INVALID_CHAIN;
+}
+
+static cairo_status_t
+_cairo_recording_surface_create_bbtree (cairo_recording_surface_t *surface)
+{
+ cairo_command_t **elements = _cairo_array_index (&surface->commands, 0);
+ if (unlikely (elements == NULL))
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ cairo_status_t status;
+ int i, count;
+ int *indices;
+
+ count = surface->commands.num_elements;
+ if (count > surface->num_indices) {
+ free (surface->indices);
+ surface->indices = _cairo_malloc_ab (count, sizeof (int));
+ if (unlikely (surface->indices == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ surface->num_indices = count;
+ }
+
+ indices = surface->indices;
+ for (i = 0; i < count; i++)
+ indices[i] = i;
+
+ sort_commands (indices, count, elements);
+
+ bbtree_init (&surface->bbtree, &elements[indices[0]]->header);
+ for (i = 1; i < count; i++) {
+ cairo_command_header_t *header = &elements[indices[i]]->header;
+ cairo_box_t box;
+
+ _cairo_box_from_rectangle (&box, &header->extents);
+ status = bbtree_add (&surface->bbtree, header, &box);
+ if (unlikely (status))
+ goto cleanup;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+cleanup:
+ bbtree_del (&surface->bbtree);
+ return status;
+}
+
+/**
+ * cairo_recording_surface_create:
+ * @content: the content of the recording surface
+ * @extents: the extents to record in pixels, can be %NULL to record
+ * unbounded operations.
+ *
+ * Creates a recording-surface which can be used to record all drawing operations
+ * at the highest level (that is, the level of paint, mask, stroke, fill
+ * and show_text_glyphs). The recording surface can then be "replayed" against
+ * any target surface by using it as a source to drawing operations.
+ *
+ * The recording phase of the recording surface is careful to snapshot all
+ * necessary objects (paths, patterns, etc.), in order to achieve
+ * accurate replay.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_recording_surface_create (cairo_content_t content,
+ const cairo_rectangle_t *extents)
+{
+ cairo_recording_surface_t *surface;
+
+ surface = malloc (sizeof (cairo_recording_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &cairo_recording_surface_backend,
+ NULL, /* device */
+ content);
+
+
+ surface->unbounded = TRUE;
+
+ /* unbounded -> 'infinite' extents */
+ if (extents != NULL) {
+ surface->extents_pixels = *extents;
+
+ /* XXX check for overflow */
+ surface->extents.x = floor (extents->x);
+ surface->extents.y = floor (extents->y);
+ surface->extents.width = ceil (extents->x + extents->width) - surface->extents.x;
+ surface->extents.height = ceil (extents->y + extents->height) - surface->extents.y;
+
+ surface->unbounded = FALSE;
+ }
+
+ _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
+
+ surface->base.is_clear = TRUE;
+
+ surface->bbtree.left = surface->bbtree.right = NULL;
+ surface->bbtree.chain = INVALID_CHAIN;
+
+ surface->indices = NULL;
+ surface->num_indices = 0;
+ surface->optimize_clears = TRUE;
+
+ return &surface->base;
+}
+slim_hidden_def (cairo_recording_surface_create);
+
+static cairo_surface_t *
+_cairo_recording_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_rectangle_t extents;
+ extents.x = extents.y = 0;
+ extents.width = width;
+ extents.height = height;
+ return cairo_recording_surface_create (content, &extents);
+}
+
+static cairo_status_t
+_cairo_recording_surface_finish (void *abstract_surface)
+{
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_command_t **elements;
+ int i, num_elements;
+
+ num_elements = surface->commands.num_elements;
+ elements = _cairo_array_index (&surface->commands, 0);
+ for (i = 0; i < num_elements; i++) {
+ cairo_command_t *command = elements[i];
+
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ _cairo_pattern_fini (&command->paint.source.base);
+ break;
+
+ case CAIRO_COMMAND_MASK:
+ _cairo_pattern_fini (&command->mask.source.base);
+ _cairo_pattern_fini (&command->mask.mask.base);
+ break;
+
+ case CAIRO_COMMAND_STROKE:
+ _cairo_pattern_fini (&command->stroke.source.base);
+ _cairo_path_fixed_fini (&command->stroke.path);
+ _cairo_stroke_style_fini (&command->stroke.style);
+ break;
+
+ case CAIRO_COMMAND_FILL:
+ _cairo_pattern_fini (&command->fill.source.base);
+ _cairo_path_fixed_fini (&command->fill.path);
+ break;
+
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ _cairo_pattern_fini (&command->show_text_glyphs.source.base);
+ free (command->show_text_glyphs.utf8);
+ free (command->show_text_glyphs.glyphs);
+ free (command->show_text_glyphs.clusters);
+ cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_clip_destroy (command->header.clip);
+ free (command);
+ }
+
+ _cairo_array_fini (&surface->commands);
+
+ if (surface->bbtree.left)
+ bbtree_del (surface->bbtree.left);
+ if (surface->bbtree.right)
+ bbtree_del (surface->bbtree.right);
+
+ free (surface->indices);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct proxy {
+ cairo_surface_t base;
+ cairo_surface_t *image;
+};
+
+static cairo_status_t
+proxy_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ struct proxy *proxy = abstract_surface;
+ return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
+}
+
+static void
+proxy_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ struct proxy *proxy = abstract_surface;
+ _cairo_surface_release_source_image (proxy->image, image, image_extra);
+}
+
+static cairo_status_t
+proxy_finish (void *abstract_surface)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t proxy_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+ proxy_finish,
+ NULL,
+
+ NULL, /* create similar */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ proxy_acquire_source_image,
+ proxy_release_source_image,
+};
+
+static cairo_surface_t *
+attach_proxy (cairo_surface_t *source,
+ cairo_surface_t *image)
+{
+ struct proxy *proxy;
+
+ proxy = malloc (sizeof (*proxy));
+ if (unlikely (proxy == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content);
+
+ proxy->image = image;
+ _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
+
+ return &proxy->base;
+}
+
+static void
+detach_proxy (cairo_surface_t *source,
+ cairo_surface_t *proxy)
+{
+ cairo_surface_finish (proxy);
+ cairo_surface_destroy (proxy);
+}
+
+static cairo_surface_t *
+get_proxy (cairo_surface_t *proxy)
+{
+ return ((struct proxy *)proxy)->image;
+}
+
+static cairo_status_t
+_cairo_recording_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_surface_t *image, *proxy;
+ cairo_status_t status;
+
+ proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend);
+ if (proxy != NULL) {
+ *image_out = (cairo_image_surface_t *)
+ cairo_surface_reference (get_proxy (proxy));
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ assert (! surface->unbounded);
+ image = _cairo_image_surface_create_with_content (surface->base.content,
+ surface->extents.width,
+ surface->extents.height);
+ if (unlikely (image->status)) {
+ status = image->status;
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ /* Handle recursion by returning future reads from the current image */
+ proxy = attach_proxy (abstract_surface, image);
+ status = _cairo_recording_surface_replay (&surface->base, image);
+ detach_proxy (abstract_surface, proxy);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ *image_out = (cairo_image_surface_t *) image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_recording_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_command_init (cairo_recording_surface_t *surface,
+ cairo_command_header_t *command,
+ cairo_command_type_t type,
+ cairo_operator_t op,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ command->type = type;
+ command->op = op;
+ command->region = CAIRO_RECORDING_REGION_ALL;
+
+ command->extents = composite->unbounded;
+ command->chain = NULL;
+ command->index = surface->commands.num_elements;
+
+ /* steal the clip */
+ command->clip = NULL;
+ if (! _cairo_composite_rectangles_can_reduce_clip (composite,
+ composite->clip))
+ {
+ command->clip = composite->clip;
+ composite->clip = NULL;
+ }
+
+ return status;
+}
+
+static void
+_cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface)
+{
+ cairo_surface_flush (&surface->base);
+}
+
+static cairo_status_t
+_cairo_recording_surface_commit (cairo_recording_surface_t *surface,
+ cairo_command_header_t *command)
+{
+ _cairo_recording_surface_break_self_copy_loop (surface);
+ return _cairo_array_append (&surface->commands, &command);
+}
+
+static void
+_cairo_recording_surface_reset (cairo_recording_surface_t *surface)
+{
+ /* Reset the commands and temporaries */
+ _cairo_recording_surface_finish (surface);
+
+ surface->bbtree.left = surface->bbtree.right = NULL;
+ surface->bbtree.chain = INVALID_CHAIN;
+
+ surface->indices = NULL;
+ surface->num_indices = 0;
+
+ _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
+}
+
+static cairo_bool_t
+is_identity_recording_pattern (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return FALSE;
+
+ if (!_cairo_matrix_is_identity(&pattern->matrix))
+ return FALSE;
+
+ surface = ((cairo_surface_pattern_t *)pattern)->surface;
+ return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_command_paint_t *command;
+ cairo_composite_rectangles_t composite;
+
+ TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
+
+ if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
+ if (surface->optimize_clears) {
+ _cairo_recording_surface_reset (surface);
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ if (clip == NULL && surface->optimize_clears &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ (op == CAIRO_OPERATOR_OVER &&
+ (surface->base.is_clear || _cairo_pattern_is_opaque_solid (source)))))
+ {
+ _cairo_recording_surface_reset (surface);
+ if (is_identity_recording_pattern (source)) {
+ cairo_surface_t *src = ((cairo_surface_pattern_t *)source)->surface;
+ return _cairo_recording_surface_replay (src, &surface->base);
+ }
+ }
+
+ status = _cairo_composite_rectangles_init_for_paint (&composite,
+ &surface->base,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ command = malloc (sizeof (cairo_command_paint_t));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_COMPOSITE;
+ }
+
+ status = _command_init (surface,
+ &command->header, CAIRO_COMMAND_PAINT, op,
+ &composite);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_pattern_init_snapshot (&command->source.base, source);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto CLEANUP_SOURCE;
+
+ _cairo_recording_surface_destroy_bbtree (surface);
+
+ _cairo_composite_rectangles_fini (&composite);
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_SOURCE:
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_destroy (command->header.clip);
+ free (command);
+CLEANUP_COMPOSITE:
+ _cairo_composite_rectangles_fini (&composite);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_command_mask_t *command;
+ cairo_composite_rectangles_t composite;
+
+ TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
+
+ status = _cairo_composite_rectangles_init_for_mask (&composite,
+ &surface->base,
+ op, source, mask,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ command = malloc (sizeof (cairo_command_mask_t));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_COMPOSITE;
+ }
+
+ status = _command_init (surface,
+ &command->header, CAIRO_COMMAND_MASK, op,
+ &composite);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_pattern_init_snapshot (&command->source.base, source);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_pattern_init_snapshot (&command->mask.base, mask);
+ if (unlikely (status))
+ goto CLEANUP_SOURCE;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto CLEANUP_MASK;
+
+ _cairo_recording_surface_destroy_bbtree (surface);
+
+ _cairo_composite_rectangles_fini (&composite);
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_MASK:
+ _cairo_pattern_fini (&command->mask.base);
+ CLEANUP_SOURCE:
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_destroy (command->header.clip);
+ free (command);
+CLEANUP_COMPOSITE:
+ _cairo_composite_rectangles_fini (&composite);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_command_stroke_t *command;
+ cairo_composite_rectangles_t composite;
+
+ TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
+
+ status = _cairo_composite_rectangles_init_for_stroke (&composite,
+ &surface->base,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ command = malloc (sizeof (cairo_command_stroke_t));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_COMPOSITE;
+ }
+
+ status = _command_init (surface,
+ &command->header, CAIRO_COMMAND_STROKE, op,
+ &composite);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_pattern_init_snapshot (&command->source.base, source);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_path_fixed_init_copy (&command->path, path);
+ if (unlikely (status))
+ goto CLEANUP_SOURCE;
+
+ status = _cairo_stroke_style_init_copy (&command->style, style);
+ if (unlikely (status))
+ goto CLEANUP_PATH;
+
+ command->ctm = *ctm;
+ command->ctm_inverse = *ctm_inverse;
+ command->tolerance = tolerance;
+ command->antialias = antialias;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto CLEANUP_STYLE;
+
+ _cairo_recording_surface_destroy_bbtree (surface);
+
+ _cairo_composite_rectangles_fini (&composite);
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_STYLE:
+ _cairo_stroke_style_fini (&command->style);
+ CLEANUP_PATH:
+ _cairo_path_fixed_fini (&command->path);
+ CLEANUP_SOURCE:
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_destroy (command->header.clip);
+ free (command);
+CLEANUP_COMPOSITE:
+ _cairo_composite_rectangles_fini (&composite);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_command_fill_t *command;
+ cairo_composite_rectangles_t composite;
+
+ TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
+
+ status = _cairo_composite_rectangles_init_for_fill (&composite,
+ &surface->base,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ command = malloc (sizeof (cairo_command_fill_t));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_COMPOSITE;
+ }
+
+ status =_command_init (surface,
+ &command->header, CAIRO_COMMAND_FILL, op,
+ &composite);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_pattern_init_snapshot (&command->source.base, source);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_path_fixed_init_copy (&command->path, path);
+ if (unlikely (status))
+ goto CLEANUP_SOURCE;
+
+ command->fill_rule = fill_rule;
+ command->tolerance = tolerance;
+ command->antialias = antialias;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto CLEANUP_PATH;
+
+ _cairo_recording_surface_destroy_bbtree (surface);
+
+ _cairo_composite_rectangles_fini (&composite);
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_PATH:
+ _cairo_path_fixed_fini (&command->path);
+ CLEANUP_SOURCE:
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_destroy (command->header.clip);
+ free (command);
+CLEANUP_COMPOSITE:
+ _cairo_composite_rectangles_fini (&composite);
+ return status;
+}
+
+static cairo_bool_t
+_cairo_recording_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_command_show_text_glyphs_t *command;
+ cairo_composite_rectangles_t composite;
+
+ TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&composite,
+ &surface->base,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ NULL);
+ if (unlikely (status))
+ return status;
+
+ command = malloc (sizeof (cairo_command_show_text_glyphs_t));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_COMPOSITE;
+ }
+
+ status = _command_init (surface,
+ &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
+ op, &composite);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_pattern_init_snapshot (&command->source.base, source);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ command->utf8 = NULL;
+ command->utf8_len = utf8_len;
+ command->glyphs = NULL;
+ command->num_glyphs = num_glyphs;
+ command->clusters = NULL;
+ command->num_clusters = num_clusters;
+
+ if (utf8_len) {
+ command->utf8 = malloc (utf8_len);
+ if (unlikely (command->utf8 == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_ARRAYS;
+ }
+ memcpy (command->utf8, utf8, utf8_len);
+ }
+ if (num_glyphs) {
+ command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0]));
+ if (unlikely (command->glyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_ARRAYS;
+ }
+ memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs);
+ }
+ if (num_clusters) {
+ command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0]));
+ if (unlikely (command->clusters == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_ARRAYS;
+ }
+ memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters);
+ }
+
+ command->cluster_flags = cluster_flags;
+
+ command->scaled_font = cairo_scaled_font_reference (scaled_font);
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto CLEANUP_SCALED_FONT;
+
+ _cairo_composite_rectangles_fini (&composite);
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_SCALED_FONT:
+ cairo_scaled_font_destroy (command->scaled_font);
+ CLEANUP_ARRAYS:
+ free (command->utf8);
+ free (command->glyphs);
+ free (command->clusters);
+
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_destroy (command->header.clip);
+ free (command);
+CLEANUP_COMPOSITE:
+ _cairo_composite_rectangles_fini (&composite);
+ return status;
+}
+
+static void
+_command_init_copy (cairo_recording_surface_t *surface,
+ cairo_command_header_t *dst,
+ const cairo_command_header_t *src)
+{
+ dst->type = src->type;
+ dst->op = src->op;
+ dst->region = CAIRO_RECORDING_REGION_ALL;
+
+ dst->extents = src->extents;
+ dst->chain = NULL;
+ dst->index = surface->commands.num_elements;
+
+ dst->clip = _cairo_clip_copy (src->clip);
+}
+
+static cairo_status_t
+_cairo_recording_surface_copy__paint (cairo_recording_surface_t *surface,
+ const cairo_command_t *src)
+{
+ cairo_command_paint_t *command;
+ cairo_status_t status;
+
+ command = malloc (sizeof (*command));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err;
+ }
+
+ _command_init_copy (surface, &command->header, &src->header);
+
+ status = _cairo_pattern_init_copy (&command->source.base,
+ &src->paint.source.base);
+ if (unlikely (status))
+ goto err_command;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto err_source;
+
+ return CAIRO_STATUS_SUCCESS;
+
+err_source:
+ _cairo_pattern_fini (&command->source.base);
+err_command:
+ free(command);
+err:
+ return status;
+}
+
+static cairo_status_t
+_cairo_recording_surface_copy__mask (cairo_recording_surface_t *surface,
+ const cairo_command_t *src)
+{
+ cairo_command_mask_t *command;
+ cairo_status_t status;
+
+ command = malloc (sizeof (*command));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err;
+ }
+
+ _command_init_copy (surface, &command->header, &src->header);
+
+ status = _cairo_pattern_init_copy (&command->source.base,
+ &src->mask.source.base);
+ if (unlikely (status))
+ goto err_command;
+
+ status = _cairo_pattern_init_copy (&command->mask.base,
+ &src->mask.source.base);
+ if (unlikely (status))
+ goto err_source;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto err_mask;
+
+ return CAIRO_STATUS_SUCCESS;
+
+err_mask:
+ _cairo_pattern_fini (&command->mask.base);
+err_source:
+ _cairo_pattern_fini (&command->source.base);
+err_command:
+ free(command);
+err:
+ return status;
+}
+
+static cairo_status_t
+_cairo_recording_surface_copy__stroke (cairo_recording_surface_t *surface,
+ const cairo_command_t *src)
+{
+ cairo_command_stroke_t *command;
+ cairo_status_t status;
+
+ command = malloc (sizeof (*command));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err;
+ }
+
+ _command_init_copy (surface, &command->header, &src->header);
+
+ status = _cairo_pattern_init_copy (&command->source.base,
+ &src->stroke.source.base);
+ if (unlikely (status))
+ goto err_command;
+
+ status = _cairo_path_fixed_init_copy (&command->path, &src->stroke.path);
+ if (unlikely (status))
+ goto err_source;
+
+ status = _cairo_stroke_style_init_copy (&command->style,
+ &src->stroke.style);
+ if (unlikely (status))
+ goto err_path;
+
+ command->ctm = src->stroke.ctm;
+ command->ctm_inverse = src->stroke.ctm_inverse;
+ command->tolerance = src->stroke.tolerance;
+ command->antialias = src->stroke.antialias;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto err_style;
+
+ return CAIRO_STATUS_SUCCESS;
+
+err_style:
+ _cairo_stroke_style_fini (&command->style);
+err_path:
+ _cairo_path_fixed_fini (&command->path);
+err_source:
+ _cairo_pattern_fini (&command->source.base);
+err_command:
+ free(command);
+err:
+ return status;
+}
+
+static cairo_status_t
+_cairo_recording_surface_copy__fill (cairo_recording_surface_t *surface,
+ const cairo_command_t *src)
+{
+ cairo_command_fill_t *command;
+ cairo_status_t status;
+
+ command = malloc (sizeof (*command));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err;
+ }
+
+ _command_init_copy (surface, &command->header, &src->header);
+
+ status = _cairo_pattern_init_copy (&command->source.base,
+ &src->fill.source.base);
+ if (unlikely (status))
+ goto err_command;
+
+ status = _cairo_path_fixed_init_copy (&command->path, &src->fill.path);
+ if (unlikely (status))
+ goto err_source;
+
+ command->fill_rule = src->fill.fill_rule;
+ command->tolerance = src->fill.tolerance;
+ command->antialias = src->fill.antialias;
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto err_path;
+
+ return CAIRO_STATUS_SUCCESS;
+
+err_path:
+ _cairo_path_fixed_fini (&command->path);
+err_source:
+ _cairo_pattern_fini (&command->source.base);
+err_command:
+ free(command);
+err:
+ return status;
+}
+
+static cairo_status_t
+_cairo_recording_surface_copy__glyphs (cairo_recording_surface_t *surface,
+ const cairo_command_t *src)
+{
+ cairo_command_show_text_glyphs_t *command;
+ cairo_status_t status;
+
+ command = malloc (sizeof (*command));
+ if (unlikely (command == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err;
+ }
+
+ _command_init_copy (surface, &command->header, &src->header);
+
+ status = _cairo_pattern_init_copy (&command->source.base,
+ &src->show_text_glyphs.source.base);
+ if (unlikely (status))
+ goto err_command;
+
+ command->utf8 = NULL;
+ command->utf8_len = src->show_text_glyphs.utf8_len;
+ command->glyphs = NULL;
+ command->num_glyphs = src->show_text_glyphs.num_glyphs;
+ command->clusters = NULL;
+ command->num_clusters = src->show_text_glyphs.num_clusters;
+
+ if (command->utf8_len) {
+ command->utf8 = malloc (command->utf8_len);
+ if (unlikely (command->utf8 == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err_arrays;
+ }
+ memcpy (command->utf8, src->show_text_glyphs.utf8, command->utf8_len);
+ }
+ if (command->num_glyphs) {
+ command->glyphs = _cairo_malloc_ab (command->num_glyphs,
+ sizeof (command->glyphs[0]));
+ if (unlikely (command->glyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err_arrays;
+ }
+ memcpy (command->glyphs, src->show_text_glyphs.glyphs,
+ sizeof (command->glyphs[0]) * command->num_glyphs);
+ }
+ if (command->num_clusters) {
+ command->clusters = _cairo_malloc_ab (command->num_clusters,
+ sizeof (command->clusters[0]));
+ if (unlikely (command->clusters == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto err_arrays;
+ }
+ memcpy (command->clusters, src->show_text_glyphs.clusters,
+ sizeof (command->clusters[0]) * command->num_clusters);
+ }
+
+ command->cluster_flags = src->show_text_glyphs.cluster_flags;
+
+ command->scaled_font =
+ cairo_scaled_font_reference (src->show_text_glyphs.scaled_font);
+
+ status = _cairo_recording_surface_commit (surface, &command->header);
+ if (unlikely (status))
+ goto err_arrays;
+
+ return CAIRO_STATUS_SUCCESS;
+
+err_arrays:
+ free (command->utf8);
+ free (command->glyphs);
+ free (command->clusters);
+ _cairo_pattern_fini (&command->source.base);
+err_command:
+ free(command);
+err:
+ return status;
+}
+
+static cairo_status_t
+_cairo_recording_surface_copy (cairo_recording_surface_t *dst,
+ cairo_recording_surface_t *src)
+{
+ cairo_command_t **elements;
+ int i, num_elements;
+ cairo_status_t status;
+
+ elements = _cairo_array_index (&src->commands, 0);
+ num_elements = src->commands.num_elements;
+ for (i = 0; i < num_elements; i++) {
+ const cairo_command_t *command = elements[i];
+
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ status = _cairo_recording_surface_copy__paint (dst, command);
+ break;
+
+ case CAIRO_COMMAND_MASK:
+ status = _cairo_recording_surface_copy__mask (dst, command);
+ break;
+
+ case CAIRO_COMMAND_STROKE:
+ status = _cairo_recording_surface_copy__stroke (dst, command);
+ break;
+
+ case CAIRO_COMMAND_FILL:
+ status = _cairo_recording_surface_copy__fill (dst, command);
+ break;
+
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ status = _cairo_recording_surface_copy__glyphs (dst, command);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_recording_surface_snapshot:
+ * @surface: a #cairo_surface_t which must be a recording surface
+ *
+ * Make an immutable copy of @surface. It is an error to call a
+ * surface-modifying function on the result of this function.
+ *
+ * The caller owns the return value and should call
+ * cairo_surface_destroy() when finished with it. This function will not
+ * return %NULL, but will return a nil surface instead.
+ *
+ * Return value: The snapshot surface.
+ **/
+static cairo_surface_t *
+_cairo_recording_surface_snapshot (void *abstract_other)
+{
+ cairo_recording_surface_t *other = abstract_other;
+ cairo_recording_surface_t *surface;
+ cairo_status_t status;
+
+ surface = malloc (sizeof (cairo_recording_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &cairo_recording_surface_backend,
+ NULL, /* device */
+ other->base.content);
+
+ surface->extents_pixels = other->extents_pixels;
+ surface->extents = other->extents;
+ surface->unbounded = other->unbounded;
+
+ surface->base.is_clear = other->base.is_clear;
+
+ surface->bbtree.left = surface->bbtree.right = NULL;
+ surface->bbtree.chain = INVALID_CHAIN;
+
+ surface->indices = NULL;
+ surface->num_indices = 0;
+ surface->optimize_clears = TRUE;
+
+ _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
+ status = _cairo_recording_surface_copy (surface, other);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &surface->base;
+}
+
+static cairo_bool_t
+_cairo_recording_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_recording_surface_t *surface = abstract_surface;
+
+ if (surface->unbounded)
+ return FALSE;
+
+ *rectangle = surface->extents;
+ return TRUE;
+}
+
+static const cairo_surface_backend_t cairo_recording_surface_backend = {
+ CAIRO_SURFACE_TYPE_RECORDING,
+ _cairo_recording_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_recording_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ _cairo_recording_surface_acquire_source_image,
+ _cairo_recording_surface_release_source_image,
+ _cairo_recording_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_recording_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ /* Here are the 5 basic drawing operations, (which are in some
+ * sense the only things that cairo_recording_surface should need to
+ * implement). However, we implement the more generic show_text_glyphs
+ * instead of show_glyphs. One or the other is eough. */
+
+ _cairo_recording_surface_paint,
+ _cairo_recording_surface_mask,
+ _cairo_recording_surface_stroke,
+ _cairo_recording_surface_fill,
+ NULL, /* fill-stroke */
+ NULL,
+ _cairo_recording_surface_has_show_text_glyphs,
+ _cairo_recording_surface_show_text_glyphs,
+};
+
+cairo_int_status_t
+_cairo_recording_surface_get_path (cairo_surface_t *abstract_surface,
+ cairo_path_fixed_t *path)
+{
+ cairo_recording_surface_t *surface;
+ cairo_command_t **elements;
+ int i, num_elements;
+ cairo_int_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return abstract_surface->status;
+
+ surface = (cairo_recording_surface_t *) abstract_surface;
+ status = CAIRO_STATUS_SUCCESS;
+
+ num_elements = surface->commands.num_elements;
+ elements = _cairo_array_index (&surface->commands, 0);
+ for (i = 0; i < num_elements; i++) {
+ cairo_command_t *command = elements[i];
+
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ case CAIRO_COMMAND_MASK:
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ break;
+
+ case CAIRO_COMMAND_STROKE:
+ {
+ cairo_traps_t traps;
+
+ _cairo_traps_init (&traps);
+
+ /* XXX call cairo_stroke_to_path() when that is implemented */
+ status = _cairo_path_fixed_stroke_polygon_to_traps (&command->stroke.path,
+ &command->stroke.style,
+ &command->stroke.ctm,
+ &command->stroke.ctm_inverse,
+ command->stroke.tolerance,
+ &traps);
+
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = _cairo_traps_path (&traps, path);
+
+ _cairo_traps_fini (&traps);
+ break;
+ }
+ case CAIRO_COMMAND_FILL:
+ {
+ status = _cairo_path_fixed_append (path,
+ &command->fill.path,
+ 0, 0);
+ break;
+ }
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ {
+ status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font,
+ command->show_text_glyphs.glyphs,
+ command->show_text_glyphs.num_glyphs,
+ path);
+ break;
+ }
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ if (unlikely (status))
+ break;
+ }
+
+ return status;
+}
+
+static int
+_cairo_recording_surface_get_visible_commands (cairo_recording_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ int num_visible, *indices;
+ cairo_box_t box;
+
+ _cairo_box_from_rectangle (&box, extents);
+
+ if (surface->bbtree.chain == INVALID_CHAIN)
+ _cairo_recording_surface_create_bbtree (surface);
+
+ indices = surface->indices;
+ bbtree_foreach_mark_visible (&surface->bbtree, &box, &indices);
+ num_visible = indices - surface->indices;
+ if (num_visible > 1)
+ sort_indices (surface->indices, num_visible);
+
+ return num_visible;
+}
+
+static cairo_status_t
+_cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
+ const cairo_rectangle_int_t *surface_extents,
+ const cairo_matrix_t *surface_transform,
+ cairo_surface_t *target,
+ const cairo_clip_t *target_clip,
+ cairo_recording_replay_type_t type,
+ cairo_recording_region_type_t region)
+{
+ cairo_surface_wrapper_t wrapper;
+ cairo_command_t **elements;
+ cairo_bool_t replay_all =
+ type == CAIRO_RECORDING_REPLAY &&
+ region == CAIRO_RECORDING_REGION_ALL;
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_rectangle_int_t extents;
+ cairo_bool_t use_indices = FALSE;
+ const cairo_rectangle_int_t *r;
+ int i, num_elements;
+
+ if (unlikely (surface->base.status))
+ return surface->base.status;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (unlikely (surface->base.finished))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ if (surface->base.is_clear)
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (_cairo_surface_is_recording (&surface->base));
+
+ _cairo_surface_wrapper_init (&wrapper, target);
+ if (surface_extents)
+ _cairo_surface_wrapper_intersect_extents (&wrapper, surface_extents);
+ r = &_cairo_unbounded_rectangle;
+ if (! surface->unbounded) {
+ _cairo_surface_wrapper_intersect_extents (&wrapper, &surface->extents);
+ r = &surface->extents;
+ }
+ _cairo_surface_wrapper_set_inverse_transform (&wrapper, surface_transform);
+ _cairo_surface_wrapper_set_clip (&wrapper, target_clip);
+
+ /* Compute the extents of the target clip in recorded device space */
+ if (! _cairo_surface_wrapper_get_target_extents (&wrapper, &extents))
+ goto done;
+
+ num_elements = surface->commands.num_elements;
+ elements = _cairo_array_index (&surface->commands, 0);
+ if (elements == NULL) {
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto done;
+ }
+
+ if (extents.width < r->width || extents.height < r->height) {
+ num_elements =
+ _cairo_recording_surface_get_visible_commands (surface, &extents);
+ use_indices = TRUE;
+ }
+
+ for (i = 0; i < num_elements; i++) {
+ cairo_command_t *command = elements[use_indices ? surface->indices[i] : i];
+
+ if (! replay_all && command->header.region != region)
+ continue;
+
+ if (! _cairo_rectangle_intersects (&extents, &command->header.extents))
+ continue;
+
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ status = _cairo_surface_wrapper_paint (&wrapper,
+ command->header.op,
+ &command->paint.source.base,
+ command->header.clip);
+ break;
+
+ case CAIRO_COMMAND_MASK:
+ status = _cairo_surface_wrapper_mask (&wrapper,
+ command->header.op,
+ &command->mask.source.base,
+ &command->mask.mask.base,
+ command->header.clip);
+ break;
+
+ case CAIRO_COMMAND_STROKE:
+ status = _cairo_surface_wrapper_stroke (&wrapper,
+ command->header.op,
+ &command->stroke.source.base,
+ &command->stroke.path,
+ &command->stroke.style,
+ &command->stroke.ctm,
+ &command->stroke.ctm_inverse,
+ command->stroke.tolerance,
+ command->stroke.antialias,
+ command->header.clip);
+ break;
+
+ case CAIRO_COMMAND_FILL:
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) {
+ cairo_command_t *stroke_command;
+
+ stroke_command = NULL;
+ if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1)
+ stroke_command = elements[i + 1];
+
+ if (stroke_command != NULL &&
+ type == CAIRO_RECORDING_REPLAY &&
+ region != CAIRO_RECORDING_REGION_ALL)
+ {
+ if (stroke_command->header.region != region)
+ stroke_command = NULL;
+ }
+
+ if (stroke_command != NULL &&
+ stroke_command->header.type == CAIRO_COMMAND_STROKE &&
+ _cairo_path_fixed_equal (&command->fill.path,
+ &stroke_command->stroke.path) &&
+ _cairo_clip_equal (command->header.clip,
+ stroke_command->header.clip))
+ {
+ status = _cairo_surface_wrapper_fill_stroke (&wrapper,
+ command->header.op,
+ &command->fill.source.base,
+ command->fill.fill_rule,
+ command->fill.tolerance,
+ command->fill.antialias,
+ &command->fill.path,
+ stroke_command->header.op,
+ &stroke_command->stroke.source.base,
+ &stroke_command->stroke.style,
+ &stroke_command->stroke.ctm,
+ &stroke_command->stroke.ctm_inverse,
+ stroke_command->stroke.tolerance,
+ stroke_command->stroke.antialias,
+ command->header.clip);
+ i++;
+ }
+ }
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_surface_wrapper_fill (&wrapper,
+ command->header.op,
+ &command->fill.source.base,
+ &command->fill.path,
+ command->fill.fill_rule,
+ command->fill.tolerance,
+ command->fill.antialias,
+ command->header.clip);
+ }
+ break;
+
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
+ command->header.op,
+ &command->show_text_glyphs.source.base,
+ command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
+ command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
+ command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
+ command->show_text_glyphs.cluster_flags,
+ command->show_text_glyphs.scaled_font,
+ command->header.clip);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ if (type == CAIRO_RECORDING_CREATE_REGIONS) {
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ command->header.region = CAIRO_RECORDING_REGION_NATIVE;
+ } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
+ command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
+ status = CAIRO_INT_STATUS_SUCCESS;
+ } else {
+ assert (_cairo_int_status_is_error (status));
+ }
+ }
+
+ if (unlikely (status))
+ break;
+ }
+
+done:
+ _cairo_surface_wrapper_fini (&wrapper);
+ return _cairo_surface_set_error (&surface->base, status);
+}
+
+cairo_status_t
+_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
+ long unsigned index,
+ cairo_surface_t *target)
+{
+ cairo_surface_wrapper_t wrapper;
+ cairo_command_t **elements, *command;
+ cairo_int_status_t status;
+
+ if (unlikely (surface->base.status))
+ return surface->base.status;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (unlikely (surface->base.finished))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ assert (_cairo_surface_is_recording (&surface->base));
+
+ /* XXX
+ * Use a surface wrapper because we may want to do transformed
+ * replay in the future.
+ */
+ _cairo_surface_wrapper_init (&wrapper, target);
+
+ if (index > surface->commands.num_elements)
+ return _cairo_error (CAIRO_STATUS_READ_ERROR);
+
+ elements = _cairo_array_index (&surface->commands, 0);
+ if (elements == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ command = elements[index];
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ status = _cairo_surface_wrapper_paint (&wrapper,
+ command->header.op,
+ &command->paint.source.base,
+ command->header.clip);
+ break;
+
+ case CAIRO_COMMAND_MASK:
+ status = _cairo_surface_wrapper_mask (&wrapper,
+ command->header.op,
+ &command->mask.source.base,
+ &command->mask.mask.base,
+ command->header.clip);
+ break;
+
+ case CAIRO_COMMAND_STROKE:
+ status = _cairo_surface_wrapper_stroke (&wrapper,
+ command->header.op,
+ &command->stroke.source.base,
+ &command->stroke.path,
+ &command->stroke.style,
+ &command->stroke.ctm,
+ &command->stroke.ctm_inverse,
+ command->stroke.tolerance,
+ command->stroke.antialias,
+ command->header.clip);
+ break;
+
+ case CAIRO_COMMAND_FILL:
+ status = _cairo_surface_wrapper_fill (&wrapper,
+ command->header.op,
+ &command->fill.source.base,
+ &command->fill.path,
+ command->fill.fill_rule,
+ command->fill.tolerance,
+ command->fill.antialias,
+ command->header.clip);
+ break;
+
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
+ command->header.op,
+ &command->show_text_glyphs.source.base,
+ command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
+ command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
+ command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
+ command->show_text_glyphs.cluster_flags,
+ command->show_text_glyphs.scaled_font,
+ command->header.clip);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_surface_wrapper_fini (&wrapper);
+ return _cairo_surface_set_error (&surface->base, status);
+}
+/**
+ * _cairo_recording_surface_replay:
+ * @surface: the #cairo_recording_surface_t
+ * @target: a target #cairo_surface_t onto which to replay the operations
+ * @width_pixels: width of the surface, in pixels
+ * @height_pixels: height of the surface, in pixels
+ *
+ * A recording surface can be "replayed" against any target surface,
+ * after which the results in target will be identical to the results
+ * that would have been obtained if the original operations applied to
+ * the recording surface had instead been applied to the target surface.
+ **/
+cairo_status_t
+_cairo_recording_surface_replay (cairo_surface_t *surface,
+ cairo_surface_t *target)
+{
+ return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL,
+ target, NULL,
+ CAIRO_RECORDING_REPLAY,
+ CAIRO_RECORDING_REGION_ALL);
+}
+
+cairo_status_t
+_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
+ const cairo_matrix_t *surface_transform,
+ cairo_surface_t *target,
+ const cairo_clip_t *target_clip)
+{
+ return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform,
+ target, target_clip,
+ 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 ((cairo_recording_surface_t *) surface, NULL, NULL,
+ target, NULL,
+ CAIRO_RECORDING_CREATE_REGIONS,
+ CAIRO_RECORDING_REGION_ALL);
+}
+
+cairo_status_t
+_cairo_recording_surface_replay_region (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_surface_t *target,
+ cairo_recording_region_type_t region)
+{
+ return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface,
+ surface_extents, NULL,
+ target, NULL,
+ CAIRO_RECORDING_REPLAY,
+ region);
+}
+
+static cairo_status_t
+_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
+ cairo_box_t *bbox,
+ const cairo_matrix_t *transform)
+{
+ cairo_surface_t *null_surface;
+ cairo_surface_t *analysis_surface;
+ cairo_status_t status;
+
+ null_surface = _cairo_null_surface_create (surface->base.content);
+ analysis_surface = _cairo_analysis_surface_create (null_surface);
+ cairo_surface_destroy (null_surface);
+
+ status = analysis_surface->status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (analysis_surface);
+ return status;
+ }
+
+ if (transform != NULL)
+ _cairo_analysis_surface_set_ctm (analysis_surface, transform);
+
+ status = _cairo_recording_surface_replay (&surface->base, analysis_surface);
+ _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox);
+ cairo_surface_destroy (analysis_surface);
+
+ return status;
+}
+
+/**
+ * cairo_recording_surface_ink_extents:
+ * @surface: a #cairo_recording_surface_t
+ * @x0: the x-coordinate of the top-left of the ink bounding box
+ * @y0: the y-coordinate of the top-left of the ink bounding box
+ * @width: the width of the ink bounding box
+ * @height: the height of the ink bounding box
+ *
+ * Measures the extents of the operations stored within the recording-surface.
+ * This is useful to compute the required size of an image surface (or
+ * equivalent) into which to replay the full sequence of drawing operations.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_recording_surface_ink_extents (cairo_surface_t *surface,
+ double *x0,
+ double *y0,
+ double *width,
+ double *height)
+{
+ cairo_status_t status;
+ cairo_box_t bbox;
+
+ memset (&bbox, 0, sizeof (bbox));
+
+ if (surface->status || ! _cairo_surface_is_recording (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ goto DONE;
+ }
+
+ status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface,
+ &bbox,
+ NULL);
+ if (unlikely (status))
+ status = _cairo_surface_set_error (surface, status);
+
+DONE:
+ if (x0)
+ *x0 = _cairo_fixed_to_double (bbox.p1.x);
+ if (y0)
+ *y0 = _cairo_fixed_to_double (bbox.p1.y);
+ if (width)
+ *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x);
+ if (height)
+ *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
+}
+
+cairo_status_t
+_cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface,
+ cairo_box_t *bbox,
+ const cairo_matrix_t *transform)
+{
+ if (! surface->unbounded) {
+ _cairo_box_from_rectangle (bbox, &surface->extents);
+ if (transform != NULL)
+ _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _recording_surface_get_ink_bbox (surface, bbox, transform);
+}
+
+cairo_status_t
+_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
+ cairo_box_t *bbox,
+ const cairo_matrix_t *transform)
+{
+ return _recording_surface_get_ink_bbox (surface, bbox, transform);
+}
+
+/**
+ * cairo_recording_surface_get_extents:
+ * @surface: a #cairo_recording_surface_t
+ * @extents: the #cairo_rectangle_t to be assigned the extents
+ *
+ * Get the extents of the recording-surface.
+ *
+ * Return value: %TRUE if the surface is bounded, of recording type, and
+ * not in an error state, otherwise %FALSE
+ *
+ * Since: 1.12
+ **/
+cairo_bool_t
+cairo_recording_surface_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_t *extents)
+{
+ cairo_recording_surface_t *record;
+
+ if (surface->status || ! _cairo_surface_is_recording (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return FALSE;
+ }
+
+ record = (cairo_recording_surface_t *)surface;
+ if (record->unbounded)
+ return FALSE;
+
+ *extents = record->extents_pixels;
+ return TRUE;
+}
diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c
new file mode 100755
index 000000000..c8f90e671
--- /dev/null
+++ b/src/cairo-rectangle.c
@@ -0,0 +1,330 @@
+/* -*- 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"
+
+#include "cairo-box-inline.h"
+
+const cairo_rectangle_int_t _cairo_empty_rectangle = { 0, 0, 0, 0 };
+const cairo_rectangle_int_t _cairo_unbounded_rectangle = {
+ CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN,
+ CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN,
+ CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN,
+};
+
+cairo_private void
+_cairo_box_from_doubles (cairo_box_t *box,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ box->p1.x = _cairo_fixed_from_double (*x1);
+ box->p1.y = _cairo_fixed_from_double (*y1);
+ box->p2.x = _cairo_fixed_from_double (*x2);
+ box->p2.y = _cairo_fixed_from_double (*y2);
+}
+
+cairo_private void
+_cairo_box_to_doubles (const cairo_box_t *box,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ *x1 = _cairo_fixed_to_double (box->p1.x);
+ *y1 = _cairo_fixed_to_double (box->p1.y);
+ *x2 = _cairo_fixed_to_double (box->p2.x);
+ *y2 = _cairo_fixed_to_double (box->p2.y);
+}
+
+void
+_cairo_box_from_rectangle (cairo_box_t *box,
+ const cairo_rectangle_int_t *rect)
+{
+ box->p1.x = _cairo_fixed_from_int (rect->x);
+ box->p1.y = _cairo_fixed_from_int (rect->y);
+ box->p2.x = _cairo_fixed_from_int (rect->x + rect->width);
+ box->p2.y = _cairo_fixed_from_int (rect->y + rect->height);
+}
+
+void
+_cairo_boxes_get_extents (const cairo_box_t *boxes,
+ int num_boxes,
+ cairo_box_t *extents)
+{
+ assert (num_boxes > 0);
+ *extents = *boxes;
+ while (--num_boxes)
+ _cairo_box_add_box (extents, ++boxes);
+}
+
+/* XXX We currently have a confusing mix of boxes and rectangles as
+ * exemplified by this function. A #cairo_box_t is a rectangular area
+ * represented by the coordinates of the upper left and lower right
+ * corners, expressed in fixed point numbers. A #cairo_rectangle_int_t is
+ * also a rectangular area, but represented by the upper left corner
+ * and the width and the height, as integer numbers.
+ *
+ * This function converts a #cairo_box_t to a #cairo_rectangle_int_t by
+ * increasing the area to the nearest integer coordinates. We should
+ * standardize on #cairo_rectangle_fixed_t and #cairo_rectangle_int_t, and
+ * this function could be renamed to the more reasonable
+ * _cairo_rectangle_fixed_round.
+ */
+
+void
+_cairo_box_round_to_rectangle (const cairo_box_t *box,
+ cairo_rectangle_int_t *rectangle)
+{
+ rectangle->x = _cairo_fixed_integer_floor (box->p1.x);
+ rectangle->y = _cairo_fixed_integer_floor (box->p1.y);
+ rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x;
+ rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y;
+}
+
+cairo_bool_t
+_cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
+ const cairo_rectangle_int_t *src)
+{
+ int x1, y1, x2, y2;
+
+ x1 = MAX (dst->x, src->x);
+ y1 = MAX (dst->y, src->y);
+ /* Beware the unsigned promotion, fortunately we have bits to spare
+ * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
+ */
+ x2 = MIN (dst->x + (int) dst->width, src->x + (int) src->width);
+ y2 = MIN (dst->y + (int) dst->height, src->y + (int) src->height);
+
+ if (x1 >= x2 || y1 >= y2) {
+ dst->x = 0;
+ dst->y = 0;
+ dst->width = 0;
+ dst->height = 0;
+
+ return FALSE;
+ } else {
+ dst->x = x1;
+ dst->y = y1;
+ dst->width = x2 - x1;
+ dst->height = y2 - y1;
+
+ return TRUE;
+ }
+}
+
+cairo_bool_t
+_cairo_rectangle_exact_intersect (cairo_rectangle_t *dst,
+ const cairo_rectangle_t *src)
+{
+ double 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 + dst->width, src->x + src->width);
+ y2 = MIN (dst->y + dst->height, src->y + src->height);
+
+ if (x1 >= x2 || y1 >= y2) {
+ dst->x = 0;
+ dst->y = 0;
+ dst->width = 0;
+ dst->height = 0;
+
+ return FALSE;
+ } else {
+ dst->x = x1;
+ dst->y = y1;
+ dst->width = x2 - x1;
+ dst->height = y2 - y1;
+
+ return TRUE;
+ }
+}
+
+/* Extends the dst rectangle to also contain src.
+ * If one of the rectangles is empty, the result is undefined
+ */
+void
+_cairo_rectangle_union (cairo_rectangle_int_t *dst,
+ const cairo_rectangle_int_t *src)
+{
+ int x1, y1, x2, y2;
+
+ x1 = MIN (dst->x, src->x);
+ y1 = MIN (dst->y, src->y);
+ /* Beware the unsigned promotion, fortunately we have bits to spare
+ * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
+ */
+ x2 = MAX (dst->x + (int) dst->width, src->x + (int) src->width);
+ y2 = MAX (dst->y + (int) dst->height, src->y + (int) src->height);
+
+ dst->x = x1;
+ dst->y = y1;
+ dst->width = x2 - x1;
+ dst->height = y2 - y1;
+}
+
+#define P1x (line->p1.x)
+#define P1y (line->p1.y)
+#define P2x (line->p2.x)
+#define P2y (line->p2.y)
+#define B1x (box->p1.x)
+#define B1y (box->p1.y)
+#define B2x (box->p2.x)
+#define B2y (box->p2.y)
+
+/*
+ * Check whether any part of line intersects box. This function essentially
+ * computes whether the ray starting at line->p1 in the direction of line->p2
+ * intersects the box before it reaches p2. Normally, this is done
+ * by dividing by the lengths of the line projected onto each axis. Because
+ * we're in fixed point, this function does a bit more work to avoid having to
+ * do the division -- we don't care about the actual intersection point, so
+ * it's of no interest to us.
+ */
+
+cairo_bool_t
+_cairo_box_intersects_line_segment (const cairo_box_t *box, cairo_line_t *line)
+{
+ cairo_fixed_t t1=0, t2=0, t3=0, t4=0;
+ cairo_int64_t t1y, t2y, t3x, t4x;
+
+ cairo_fixed_t xlen, ylen;
+
+ if (_cairo_box_contains_point (box, &line->p1) ||
+ _cairo_box_contains_point (box, &line->p2))
+ return TRUE;
+
+ xlen = P2x - P1x;
+ ylen = P2y - P1y;
+
+ if (xlen) {
+ if (xlen > 0) {
+ t1 = B1x - P1x;
+ t2 = B2x - P1x;
+ } else {
+ t1 = P1x - B2x;
+ t2 = P1x - B1x;
+ xlen = - xlen;
+ }
+
+ if ((t1 < 0 || t1 > xlen) &&
+ (t2 < 0 || t2 > xlen))
+ return FALSE;
+ } else {
+ /* Fully vertical line -- check that X is in bounds */
+ if (P1x < B1x || P1x > B2x)
+ return FALSE;
+ }
+
+ if (ylen) {
+ if (ylen > 0) {
+ t3 = B1y - P1y;
+ t4 = B2y - P1y;
+ } else {
+ t3 = P1y - B2y;
+ t4 = P1y - B1y;
+ ylen = - ylen;
+ }
+
+ if ((t3 < 0 || t3 > ylen) &&
+ (t4 < 0 || t4 > ylen))
+ return FALSE;
+ } else {
+ /* Fully horizontal line -- check Y */
+ if (P1y < B1y || P1y > B2y)
+ return FALSE;
+ }
+
+ /* If we had a horizontal or vertical line, then it's already been checked */
+ if (P1x == P2x || P1y == P2y)
+ return TRUE;
+
+ /* Check overlap. Note that t1 < t2 and t3 < t4 here. */
+ t1y = _cairo_int32x32_64_mul (t1, ylen);
+ t2y = _cairo_int32x32_64_mul (t2, ylen);
+ t3x = _cairo_int32x32_64_mul (t3, xlen);
+ t4x = _cairo_int32x32_64_mul (t4, xlen);
+
+ if (_cairo_int64_lt(t1y, t4x) &&
+ _cairo_int64_lt(t3x, t2y))
+ return TRUE;
+
+ return FALSE;
+}
+
+static cairo_status_t
+_cairo_box_add_spline_point (void *closure,
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent)
+{
+ _cairo_box_add_point (closure, point);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* assumes a has been previously added */
+void
+_cairo_box_add_curve_to (cairo_box_t *extents,
+ const cairo_point_t *a,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ _cairo_box_add_point (extents, d);
+ if (!_cairo_box_contains_point (extents, b) ||
+ !_cairo_box_contains_point (extents, c))
+ {
+ cairo_status_t status;
+
+ status = _cairo_spline_bound (_cairo_box_add_spline_point,
+ extents, a, b, c, d);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+}
+
+void
+_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti,
+ const cairo_rectangle_t *rectf)
+{
+ recti->x = floor (rectf->x);
+ recti->y = floor (rectf->y);
+ recti->width = ceil (rectf->x + rectf->width) - floor (rectf->x);
+ recti->height = ceil (rectf->y + rectf->height) - floor (rectf->y);
+}
diff --git a/src/cairo-rectangular-scan-converter.c b/src/cairo-rectangular-scan-converter.c
new file mode 100755
index 000000000..e353b34e8
--- /dev/null
+++ b/src/cairo-rectangular-scan-converter.c
@@ -0,0 +1,791 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-combsort-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-spans-private.h"
+
+#include <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++;
+ int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+ sweep->spans[n].x = prev_x;
+ sweep->spans[n].inverse = 0;
+ sweep->spans[n].coverage = c - (c >> 8);
+ prev_coverage = coverage;
+ }
+
+ coverage += cell->covered;
+ if (coverage != prev_coverage) {
+ int n = sweep->num_spans++;
+ int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+ sweep->spans[n].x = cell->x;
+ sweep->spans[n].inverse = 0;
+ sweep->spans[n].coverage = c - (c >> 8);
+ prev_coverage = coverage;
+ }
+ coverage += cell->uncovered;
+ prev_x = cell->x + 1;
+ }
+ _cairo_freepool_reset (&sweep->coverage.pool);
+
+ if (sweep->num_spans) {
+ if (prev_x <= sweep->xmax) {
+ int n = sweep->num_spans++;
+ int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+ sweep->spans[n].x = prev_x;
+ sweep->spans[n].inverse = 0;
+ sweep->spans[n].coverage = c - (c >> 8);
+ }
+
+ if (coverage && prev_x < sweep->xmax) {
+ int n = sweep->num_spans++;
+ sweep->spans[n].x = sweep->xmax;
+ sweep->spans[n].inverse = 1;
+ sweep->spans[n].coverage = 0;
+ }
+ }
+}
+
+static inline void
+sweep_line_delete (sweep_line_t *sweep,
+ rectangle_t *rectangle)
+{
+ if (sweep->insert_cursor == rectangle)
+ sweep->insert_cursor = rectangle->next;
+
+ rectangle->prev->next = rectangle->next;
+ rectangle->next->prev = rectangle->prev;
+
+ pqueue_pop (&sweep->stop);
+}
+
+static inline void
+sweep_line_insert (sweep_line_t *sweep,
+ rectangle_t *rectangle)
+{
+ rectangle_t *pos;
+
+ pos = sweep->insert_cursor;
+ if (pos->left != rectangle->left) {
+ if (pos->left > rectangle->left) {
+ do {
+ UNROLL3({
+ if (pos->prev->left < rectangle->left)
+ break;
+ pos = pos->prev;
+ })
+ } while (TRUE);
+ } else {
+ do {
+ UNROLL3({
+ pos = pos->next;
+ if (pos->left >= rectangle->left)
+ break;
+ });
+ } while (TRUE);
+ }
+ }
+
+ pos->prev->next = rectangle;
+ rectangle->prev = pos->prev;
+ rectangle->next = pos;
+ pos->prev = rectangle;
+ sweep->insert_cursor = rectangle;
+
+ pqueue_push (sweep, rectangle);
+}
+
+static void
+render_rows (sweep_line_t *sweep_line,
+ cairo_span_renderer_t *renderer,
+ int height)
+{
+ cairo_status_t status;
+
+ _active_edges_to_spans (sweep_line);
+
+ status = renderer->render_rows (renderer,
+ sweep_line->current_y, height,
+ sweep_line->spans,
+ sweep_line->num_spans);
+ if (unlikely (status))
+ longjmp (sweep_line->jmpbuf, status);
+}
+
+static cairo_status_t
+generate (cairo_rectangular_scan_converter_t *self,
+ cairo_span_renderer_t *renderer,
+ rectangle_t **rectangles)
+{
+ sweep_line_t sweep_line;
+ rectangle_t *start, *stop;
+ cairo_status_t status;
+
+ sweep_line_init (&sweep_line);
+ sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x);
+ sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x);
+ sweep_line.start = rectangles;
+ if ((status = setjmp (sweep_line.jmpbuf)))
+ goto out;
+
+ sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y);
+ start = *sweep_line.start++;
+ do {
+ if (start->top_y != sweep_line.current_y) {
+ render_rows (&sweep_line, renderer,
+ start->top_y - sweep_line.current_y);
+ sweep_line.current_y = start->top_y;
+ }
+
+ do {
+ sweep_line_insert (&sweep_line, start);
+ start = *sweep_line.start++;
+ if (start == NULL)
+ goto end;
+ if (start->top_y != sweep_line.current_y)
+ break;
+ } while (TRUE);
+
+ render_rows (&sweep_line, renderer, 1);
+
+ stop = peek_stop (&sweep_line);
+ while (stop->bottom_y == sweep_line.current_y) {
+ sweep_line_delete (&sweep_line, stop);
+ stop = peek_stop (&sweep_line);
+ if (stop == NULL)
+ break;
+ }
+
+ sweep_line.current_y++;
+
+ while (stop != NULL && stop->bottom_y < start->top_y) {
+ if (stop->bottom_y != sweep_line.current_y) {
+ render_rows (&sweep_line, renderer,
+ stop->bottom_y - sweep_line.current_y);
+ sweep_line.current_y = stop->bottom_y;
+ }
+
+ render_rows (&sweep_line, renderer, 1);
+
+ do {
+ sweep_line_delete (&sweep_line, stop);
+ stop = peek_stop (&sweep_line);
+ } while (stop != NULL && stop->bottom_y == sweep_line.current_y);
+
+ sweep_line.current_y++;
+ }
+ } while (TRUE);
+
+ end:
+ render_rows (&sweep_line, renderer, 1);
+
+ stop = peek_stop (&sweep_line);
+ while (stop->bottom_y == sweep_line.current_y) {
+ sweep_line_delete (&sweep_line, stop);
+ stop = peek_stop (&sweep_line);
+ if (stop == NULL)
+ goto out;
+ }
+
+ while (++sweep_line.current_y < _cairo_fixed_integer_part (self->extents.p2.y)) {
+ if (stop->bottom_y != sweep_line.current_y) {
+ render_rows (&sweep_line, renderer,
+ stop->bottom_y - sweep_line.current_y);
+ sweep_line.current_y = stop->bottom_y;
+ }
+
+ render_rows (&sweep_line, renderer, 1);
+
+ do {
+ sweep_line_delete (&sweep_line, stop);
+ stop = peek_stop (&sweep_line);
+ if (stop == NULL)
+ goto out;
+ } while (stop->bottom_y == sweep_line.current_y);
+
+ }
+
+ out:
+ sweep_line_fini (&sweep_line);
+
+ return status;
+}
+static void generate_row(cairo_span_renderer_t *renderer,
+ const rectangle_t *r,
+ int y, int h,
+ uint16_t coverage)
+{
+ cairo_half_open_span_t spans[4];
+ unsigned int num_spans = 0;
+ int x1 = _cairo_fixed_integer_part (r->left);
+ int x2 = _cairo_fixed_integer_part (r->right);
+ if (x2 > x1) {
+ if (! _cairo_fixed_is_integer (r->left)) {
+ spans[num_spans].x = x1;
+ spans[num_spans].coverage =
+ coverage * (256 - _cairo_fixed_fractional_part (r->left)) >> 8;
+ num_spans++;
+ x1++;
+ }
+
+ if (x2 > x1) {
+ spans[num_spans].x = x1;
+ spans[num_spans].coverage = coverage - (coverage >> 8);
+ num_spans++;
+ }
+
+ if (! _cairo_fixed_is_integer (r->right)) {
+ spans[num_spans].x = x2++;
+ spans[num_spans].coverage =
+ coverage * _cairo_fixed_fractional_part (r->right) >> 8;
+ num_spans++;
+ }
+ } else {
+ spans[num_spans].x = x2++;
+ spans[num_spans].coverage = coverage * (r->right - r->left) >> 8;
+ num_spans++;
+ }
+
+ spans[num_spans].x = x2;
+ spans[num_spans].coverage = 0;
+ num_spans++;
+
+ renderer->render_rows (renderer, y, h, spans, num_spans);
+}
+
+static cairo_status_t
+generate_box (cairo_rectangular_scan_converter_t *self,
+ cairo_span_renderer_t *renderer)
+{
+ const rectangle_t *r = self->chunks.base;
+ int y1 = _cairo_fixed_integer_part (r->top);
+ int y2 = _cairo_fixed_integer_part (r->bottom);
+ if (y2 > y1) {
+ if (! _cairo_fixed_is_integer (r->top)) {
+ generate_row(renderer, r, y1, 1,
+ 256 - _cairo_fixed_fractional_part (r->top));
+ y1++;
+ }
+
+ if (y2 > y1)
+ generate_row(renderer, r, y1, y2-y1, 256);
+
+ if (! _cairo_fixed_is_integer (r->bottom))
+ generate_row(renderer, r, y2, 1,
+ _cairo_fixed_fractional_part (r->bottom));
+ } else
+ generate_row(renderer, r, y1, 1, r->bottom - r->top);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectangular_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_rectangular_scan_converter_t *self = converter;
+ rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)];
+ rectangle_t **rectangles;
+ struct _cairo_rectangular_scan_converter_chunk *chunk;
+ cairo_status_t status;
+ int i, j;
+
+ if (unlikely (self->num_rectangles == 0)) {
+ return renderer->render_rows (renderer,
+ _cairo_fixed_integer_part (self->extents.p1.y),
+ _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y),
+ NULL, 0);
+ }
+
+ if (self->num_rectangles == 1)
+ return generate_box (self, renderer);
+
+ rectangles = rectangles_stack;
+ if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) {
+ rectangles = _cairo_malloc_ab (self->num_rectangles + 1,
+ sizeof (rectangle_t *));
+ if (unlikely (rectangles == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ j = 0;
+ for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) {
+ rectangle_t *rectangle;
+
+ rectangle = chunk->base;
+ for (i = 0; i < chunk->count; i++)
+ rectangles[j++] = &rectangle[i];
+ }
+ rectangle_sort (rectangles, j);
+ rectangles[j] = NULL;
+
+ status = generate (self, renderer, rectangles);
+
+ if (rectangles != rectangles_stack)
+ free (rectangles);
+
+ return status;
+}
+
+static rectangle_t *
+_allocate_rectangle (cairo_rectangular_scan_converter_t *self)
+{
+ rectangle_t *rectangle;
+ struct _cairo_rectangular_scan_converter_chunk *chunk;
+
+ chunk = self->tail;
+ if (chunk->count == chunk->size) {
+ int size;
+
+ size = chunk->size * 2;
+ chunk->next = _cairo_malloc_ab_plus_c (size,
+ sizeof (rectangle_t),
+ sizeof (struct _cairo_rectangular_scan_converter_chunk));
+
+ if (unlikely (chunk->next == NULL))
+ return NULL;
+
+ chunk = chunk->next;
+ chunk->next = NULL;
+ chunk->count = 0;
+ chunk->size = size;
+ chunk->base = chunk + 1;
+ self->tail = chunk;
+ }
+
+ rectangle = chunk->base;
+ return rectangle + chunk->count++;
+}
+
+cairo_status_t
+_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self,
+ const cairo_box_t *box,
+ int dir)
+{
+ rectangle_t *rectangle;
+
+ rectangle = _allocate_rectangle (self);
+ if (unlikely (rectangle == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ rectangle->dir = dir;
+ rectangle->left = MAX (box->p1.x, self->extents.p1.x);
+ rectangle->right = MIN (box->p2.x, self->extents.p2.x);
+ if (unlikely (rectangle->right <= rectangle->left)) {
+ self->tail->count--;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ rectangle->top = MAX (box->p1.y, self->extents.p1.y);
+ rectangle->top_y = _cairo_fixed_integer_floor (rectangle->top);
+ rectangle->bottom = MIN (box->p2.y, self->extents.p2.y);
+ rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom);
+ if (likely (rectangle->bottom > rectangle->top))
+ self->num_rectangles++;
+ else
+ self->tail->count--;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_rectangular_scan_converter_destroy (void *converter)
+{
+ cairo_rectangular_scan_converter_t *self = converter;
+ struct _cairo_rectangular_scan_converter_chunk *chunk, *next;
+
+ for (chunk = self->chunks.next; chunk != NULL; chunk = next) {
+ next = chunk->next;
+ free (chunk);
+ }
+}
+
+void
+_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self,
+ const cairo_rectangle_int_t *extents)
+{
+ self->base.destroy = _cairo_rectangular_scan_converter_destroy;
+ self->base.generate = _cairo_rectangular_scan_converter_generate;
+
+ _cairo_box_from_rectangle (&self->extents, extents);
+
+ self->chunks.base = self->buf;
+ self->chunks.next = NULL;
+ self->chunks.count = 0;
+ self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t);
+ self->tail = &self->chunks;
+
+ self->num_rectangles = 0;
+}
diff --git a/src/cairo-reference-count-private.h b/src/cairo-reference-count-private.h
new file mode 100755
index 000000000..75fdf3538
--- /dev/null
+++ b/src/cairo-reference-count-private.h
@@ -0,0 +1,62 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <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(RC) _cairo_atomic_int_dec (&(RC)->ref_count)
+#define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count)
+
+#define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE))
+
+#define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count)
+
+#define CAIRO_REFERENCE_COUNT_INVALID_VALUE ((cairo_atomic_int_t) -1)
+#define CAIRO_REFERENCE_COUNT_INVALID {CAIRO_REFERENCE_COUNT_INVALID_VALUE}
+
+#define CAIRO_REFERENCE_COUNT_IS_INVALID(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) == CAIRO_REFERENCE_COUNT_INVALID_VALUE)
+
+#define CAIRO_REFERENCE_COUNT_HAS_REFERENCE(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) > 0)
+
+#endif
diff --git a/src/cairo-region-private.h b/src/cairo-region-private.h
new file mode 100755
index 000000000..549e50878
--- /dev/null
+++ b/src/cairo-region-private.h
@@ -0,0 +1,77 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Owen Taylor <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_private cairo_region_t *
+_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count);
+
+cairo_private cairo_box_t *
+_cairo_region_get_boxes (const cairo_region_t *region, int *nbox);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_REGION_PRIVATE_H */
diff --git a/src/cairo-region.c b/src/cairo-region.c
new file mode 100755
index 000000000..a51e2247f
--- /dev/null
+++ b/src/cairo-region.c
@@ -0,0 +1,950 @@
+/* -*- 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:
+ case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+ case CAIRO_STATUS_DEVICE_FINISHED:
+ default:
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_region_t *) &_cairo_region_nil;
+ }
+}
+
+/**
+ * _cairo_region_set_error:
+ * @region: a region
+ * @status: a status value indicating an error
+ *
+ * Atomically sets region->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal
+ * status values.
+ *
+ * All assignments of an error status to region->status should happen
+ * through _cairo_region_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the
+ * nil objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ *
+ * Return value: the error status.
+ **/
+static cairo_status_t
+_cairo_region_set_error (cairo_region_t *region,
+ cairo_status_t status)
+{
+ if (status == CAIRO_STATUS_SUCCESS)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&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));
+
+ CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
+ region->status = CAIRO_STATUS_SUCCESS;
+
+ if (count == 1) {
+ pixman_region32_init_rect (&region->rgn,
+ rects->x, rects->y,
+ rects->width, rects->height);
+
+ return region;
+ }
+
+ if (count > ARRAY_LENGTH (stack_pboxes)) {
+ pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t));
+ if (unlikely (pboxes == NULL)) {
+ free (region);
+ return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ pboxes[i].x1 = rects[i].x;
+ pboxes[i].y1 = rects[i].y;
+ pboxes[i].x2 = rects[i].x + rects[i].width;
+ pboxes[i].y2 = rects[i].y + rects[i].height;
+ }
+
+ i = pixman_region32_init_rects (&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));
+ }
+
+ return region;
+}
+slim_hidden_def (cairo_region_create_rectangles);
+
+cairo_region_t *
+_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count)
+{
+ cairo_region_t *region;
+
+ region = _cairo_malloc (sizeof (cairo_region_t));
+ if (unlikely (region == NULL))
+ return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
+ region->status = CAIRO_STATUS_SUCCESS;
+
+ if (! pixman_region32_init_rects (&region->rgn,
+ (pixman_box32_t *)boxes, count)) {
+ free (region);
+ return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ return region;
+}
+
+cairo_box_t *
+_cairo_region_get_boxes (const cairo_region_t *region, int *nbox)
+{
+ if (region->status) {
+ nbox = 0;
+ return NULL;
+ }
+
+ return (cairo_box_t *) pixman_region32_rectangles (CONST_CAST &region->rgn, nbox);
+}
+
+/**
+ * cairo_region_create_rectangle:
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Allocates a new region object containing @rectangle.
+ *
+ * Return value: A newly allocated #cairo_region_t. Free with
+ * cairo_region_destroy(). This function always returns a
+ * valid pointer; if memory cannot be allocated, then a special
+ * error object is returned where all operations on the object do nothing.
+ * You can check for this with cairo_region_status().
+ *
+ * Since: 1.10
+ **/
+cairo_region_t *
+cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle)
+{
+ cairo_region_t *region;
+
+ region = _cairo_malloc (sizeof (cairo_region_t));
+ if (unlikely (region == NULL))
+ return (cairo_region_t *) &_cairo_region_nil;
+
+ region->status = CAIRO_STATUS_SUCCESS;
+ CAIRO_REFERENCE_COUNT_INIT (&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 occurred for this
+ * region object.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_status (const cairo_region_t *region)
+{
+ return region->status;
+}
+slim_hidden_def (cairo_region_status);
+
+/**
+ * cairo_region_subtract:
+ * @dst: a #cairo_region_t
+ * @other: another #cairo_region_t
+ *
+ * Subtracts @other from @dst and places the result in @dst
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other)
+{
+ if (dst->status)
+ return dst->status;
+
+ if (other->status)
+ return _cairo_region_set_error (dst, other->status);
+
+ if (! pixman_region32_subtract (&dst->rgn,
+ &dst->rgn,
+ CONST_CAST &other->rgn))
+ {
+ return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_region_subtract);
+
+/**
+ * cairo_region_subtract_rectangle:
+ * @dst: a #cairo_region_t
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Subtracts @rectangle from @dst and places the result in @dst
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_subtract_rectangle (cairo_region_t *dst,
+ const cairo_rectangle_int_t *rectangle)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ pixman_region32_t region;
+
+ if (dst->status)
+ return dst->status;
+
+ pixman_region32_init_rect (&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. (Since 1.10)
+ * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region. (Since 1.10)
+ * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and
+ * partially outside the region. (Since 1.10)
+ *
+ * Used as the return value for cairo_region_contains_rectangle().
+ *
+ * Since: 1.10
+ **/
+
+/**
+ * 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/src/cairo-rtree-private.h b/src/cairo-rtree-private.h
new file mode 100755
index 000000000..27806cab6
--- /dev/null
+++ b/src/cairo-rtree-private.h
@@ -0,0 +1,142 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ */
+
+#ifndef CAIRO_RTREE_PRIVATE_H
+#define CAIRO_RTREE_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-freelist-private.h"
+#include "cairo-list-inline.h"
+
+enum {
+ CAIRO_RTREE_NODE_AVAILABLE,
+ CAIRO_RTREE_NODE_DIVIDED,
+ CAIRO_RTREE_NODE_OCCUPIED,
+};
+
+typedef struct _cairo_rtree_node {
+ struct _cairo_rtree_node *children[4], *parent;
+ cairo_list_t link;
+ uint16_t pinned;
+ uint16_t state;
+ uint16_t x, y;
+ uint16_t width, height;
+} cairo_rtree_node_t;
+
+typedef struct _cairo_rtree {
+ cairo_rtree_node_t root;
+ int min_size;
+ cairo_list_t pinned;
+ cairo_list_t available;
+ cairo_list_t evictable;
+ void (*destroy) (cairo_rtree_node_t *);
+ cairo_freepool_t node_freepool;
+} cairo_rtree_t;
+
+cairo_private cairo_rtree_node_t *
+_cairo_rtree_node_create (cairo_rtree_t *rtree,
+ cairo_rtree_node_t *parent,
+ int x,
+ int y,
+ int width,
+ int height);
+
+cairo_private cairo_status_t
+_cairo_rtree_node_insert (cairo_rtree_t *rtree,
+ cairo_rtree_node_t *node,
+ int width,
+ int height,
+ cairo_rtree_node_t **out);
+
+cairo_private void
+_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
+
+cairo_private void
+_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
+
+cairo_private void
+_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
+
+cairo_private void
+_cairo_rtree_init (cairo_rtree_t *rtree,
+ int width,
+ int height,
+ int min_size,
+ int node_size,
+ void (*destroy)(cairo_rtree_node_t *));
+
+cairo_private cairo_int_status_t
+_cairo_rtree_insert (cairo_rtree_t *rtree,
+ int width,
+ int height,
+ cairo_rtree_node_t **out);
+
+cairo_private cairo_int_status_t
+_cairo_rtree_evict_random (cairo_rtree_t *rtree,
+ int width,
+ int height,
+ cairo_rtree_node_t **out);
+
+cairo_private void
+_cairo_rtree_foreach (cairo_rtree_t *rtree,
+ void (*func)(cairo_rtree_node_t *, void *data),
+ void *data);
+
+static inline void *
+_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+ assert (node->state == CAIRO_RTREE_NODE_OCCUPIED);
+ if (! node->pinned) {
+ cairo_list_move (&node->link, &rtree->pinned);
+ node->pinned = 1;
+ }
+
+ return node;
+}
+
+cairo_private void
+_cairo_rtree_unpin (cairo_rtree_t *rtree);
+
+cairo_private void
+_cairo_rtree_reset (cairo_rtree_t *rtree);
+
+cairo_private void
+_cairo_rtree_fini (cairo_rtree_t *rtree);
+
+#endif /* CAIRO_RTREE_PRIVATE_H */
diff --git a/src/cairo-rtree.c b/src/cairo-rtree.c
new file mode 100755
index 000000000..dbc040929
--- /dev/null
+++ b/src/cairo-rtree.c
@@ -0,0 +1,388 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <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->state = CAIRO_RTREE_NODE_AVAILABLE;
+ node->pinned = FALSE;
+ node->x = x;
+ node->y = y;
+ node->width = width;
+ node->height = height;
+
+ cairo_list_add (&node->link, &rtree->available);
+
+ return node;
+}
+
+void
+_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+ int i;
+
+ cairo_list_del (&node->link);
+
+ if (node->state == CAIRO_RTREE_NODE_OCCUPIED) {
+ rtree->destroy (node);
+ } else {
+ for (i = 0; i < 4 && node->children[i] != NULL; i++)
+ _cairo_rtree_node_destroy (rtree, node->children[i]);
+ }
+
+ _cairo_freepool_free (&rtree->node_freepool, node);
+}
+
+void
+_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+ int i;
+
+ do {
+ assert (node->state == CAIRO_RTREE_NODE_DIVIDED);
+
+ for (i = 0; i < 4 && node->children[i] != NULL; i++)
+ if (node->children[i]->state != CAIRO_RTREE_NODE_AVAILABLE)
+ return;
+
+ for (i = 0; i < 4 && node->children[i] != NULL; i++)
+ _cairo_rtree_node_destroy (rtree, node->children[i]);
+
+ node->children[0] = NULL;
+ node->state = CAIRO_RTREE_NODE_AVAILABLE;
+ cairo_list_move (&node->link, &rtree->available);
+ } while ((node = node->parent) != NULL);
+}
+
+cairo_status_t
+_cairo_rtree_node_insert (cairo_rtree_t *rtree,
+ cairo_rtree_node_t *node,
+ int width,
+ int height,
+ cairo_rtree_node_t **out)
+{
+ int w, h, i;
+
+ assert (node->state == CAIRO_RTREE_NODE_AVAILABLE);
+ assert (node->pinned == FALSE);
+
+ if (node->width - width > rtree->min_size ||
+ node->height - height > rtree->min_size)
+ {
+ w = node->width - width;
+ h = node->height - height;
+
+ i = 0;
+ node->children[i] = _cairo_rtree_node_create (rtree, node,
+ node->x, node->y,
+ width, height);
+ if (unlikely (node->children[i] == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ i++;
+
+ if (w > rtree->min_size) {
+ node->children[i] = _cairo_rtree_node_create (rtree, node,
+ node->x + width,
+ node->y,
+ w, height);
+ if (unlikely (node->children[i] == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ i++;
+ }
+
+ if (h > rtree->min_size) {
+ node->children[i] = _cairo_rtree_node_create (rtree, node,
+ node->x,
+ node->y + height,
+ width, h);
+ if (unlikely (node->children[i] == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ i++;
+
+ if (w > rtree->min_size) {
+ node->children[i] = _cairo_rtree_node_create (rtree, node,
+ node->x + width,
+ node->y + height,
+ w, h);
+ if (unlikely (node->children[i] == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ i++;
+ }
+ }
+
+ if (i < 4)
+ node->children[i] = NULL;
+
+ node->state = CAIRO_RTREE_NODE_DIVIDED;
+ cairo_list_move (&node->link, &rtree->evictable);
+ node = node->children[0];
+ }
+
+ node->state = CAIRO_RTREE_NODE_OCCUPIED;
+ cairo_list_move (&node->link, &rtree->evictable);
+ *out = node;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+ assert (node->state == CAIRO_RTREE_NODE_OCCUPIED);
+ assert (node->pinned == FALSE);
+
+ rtree->destroy (node);
+
+ node->state = CAIRO_RTREE_NODE_AVAILABLE;
+ cairo_list_move (&node->link, &rtree->available);
+
+ _cairo_rtree_node_collapse (rtree, node->parent);
+}
+
+cairo_int_status_t
+_cairo_rtree_insert (cairo_rtree_t *rtree,
+ int width,
+ int height,
+ cairo_rtree_node_t **out)
+{
+ cairo_rtree_node_t *node;
+
+ cairo_list_foreach_entry (node, cairo_rtree_node_t,
+ &rtree->available, link)
+ {
+ if (node->width >= width && node->height >= height)
+ return _cairo_rtree_node_insert (rtree, node, width, height, out);
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+cairo_int_status_t
+_cairo_rtree_evict_random (cairo_rtree_t *rtree,
+ int width,
+ int height,
+ cairo_rtree_node_t **out)
+{
+ cairo_int_status_t ret = CAIRO_INT_STATUS_UNSUPPORTED;
+ cairo_rtree_node_t *node, *next;
+ cairo_list_t tmp_pinned;
+ int i, cnt;
+
+ cairo_list_init (&tmp_pinned);
+
+ /* propagate pinned from children to root */
+ cairo_list_foreach_entry_safe (node, next,
+ cairo_rtree_node_t, &rtree->pinned, link) {
+ node = node->parent;
+ while (node && ! node->pinned) {
+ node->pinned = 1;
+ cairo_list_move (&node->link, &tmp_pinned);
+ node = node->parent;
+ }
+ }
+
+ cnt = 0;
+ cairo_list_foreach_entry (node, cairo_rtree_node_t,
+ &rtree->evictable, link)
+ {
+ if (node->width >= width && node->height >= height)
+ cnt++;
+ }
+
+ if (cnt == 0)
+ goto out;
+
+ cnt = hars_petruska_f54_1_random () % cnt;
+ cairo_list_foreach_entry (node, cairo_rtree_node_t,
+ &rtree->evictable, link)
+ {
+ if (node->width >= width && node->height >= height && cnt-- == 0) {
+ if (node->state == CAIRO_RTREE_NODE_OCCUPIED) {
+ rtree->destroy (node);
+ } else {
+ for (i = 0; i < 4 && node->children[i] != NULL; i++)
+ _cairo_rtree_node_destroy (rtree, node->children[i]);
+ node->children[0] = NULL;
+ }
+
+ node->state = CAIRO_RTREE_NODE_AVAILABLE;
+ cairo_list_move (&node->link, &rtree->available);
+
+ *out = node;
+ ret = CAIRO_STATUS_SUCCESS;
+ break;
+ }
+ }
+
+out:
+ while (! cairo_list_is_empty (&tmp_pinned)) {
+ node = cairo_list_first_entry (&tmp_pinned, cairo_rtree_node_t, link);
+ node->pinned = 0;
+ cairo_list_move (&node->link, &rtree->evictable);
+ }
+ return ret;
+}
+
+void
+_cairo_rtree_unpin (cairo_rtree_t *rtree)
+{
+ while (! cairo_list_is_empty (&rtree->pinned)) {
+ cairo_rtree_node_t *node = cairo_list_first_entry (&rtree->pinned,
+ cairo_rtree_node_t,
+ link);
+ node->pinned = 0;
+ cairo_list_move (&node->link, &rtree->evictable);
+ }
+}
+
+void
+_cairo_rtree_init (cairo_rtree_t *rtree,
+ int width,
+ int height,
+ int min_size,
+ int node_size,
+ void (*destroy) (cairo_rtree_node_t *))
+{
+ assert (node_size >= (int) sizeof (cairo_rtree_node_t));
+ _cairo_freepool_init (&rtree->node_freepool, node_size);
+
+ cairo_list_init (&rtree->available);
+ cairo_list_init (&rtree->pinned);
+ cairo_list_init (&rtree->evictable);
+
+ rtree->min_size = min_size;
+ rtree->destroy = destroy;
+
+ memset (&rtree->root, 0, sizeof (rtree->root));
+ rtree->root.width = width;
+ rtree->root.height = height;
+ rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE;
+ cairo_list_add (&rtree->root.link, &rtree->available);
+}
+
+void
+_cairo_rtree_reset (cairo_rtree_t *rtree)
+{
+ int i;
+
+ if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
+ rtree->destroy (&rtree->root);
+ } else {
+ for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
+ _cairo_rtree_node_destroy (rtree, rtree->root.children[i]);
+ rtree->root.children[0] = NULL;
+ }
+
+ cairo_list_init (&rtree->available);
+ cairo_list_init (&rtree->evictable);
+ cairo_list_init (&rtree->pinned);
+
+ rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE;
+ rtree->root.pinned = FALSE;
+ cairo_list_add (&rtree->root.link, &rtree->available);
+}
+
+static void
+_cairo_rtree_node_foreach (cairo_rtree_node_t *node,
+ void (*func)(cairo_rtree_node_t *, void *data),
+ void *data)
+{
+ int i;
+
+ for (i = 0; i < 4 && node->children[i] != NULL; i++)
+ _cairo_rtree_node_foreach(node->children[i], func, data);
+
+ func(node, data);
+}
+
+void
+_cairo_rtree_foreach (cairo_rtree_t *rtree,
+ void (*func)(cairo_rtree_node_t *, void *data),
+ void *data)
+{
+ int i;
+
+ if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
+ func(&rtree->root, data);
+ } else {
+ for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
+ _cairo_rtree_node_foreach (rtree->root.children[i], func, data);
+ }
+}
+
+void
+_cairo_rtree_fini (cairo_rtree_t *rtree)
+{
+ int i;
+
+ if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
+ rtree->destroy (&rtree->root);
+ } else {
+ for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
+ _cairo_rtree_node_destroy (rtree, rtree->root.children[i]);
+ }
+
+ _cairo_freepool_fini (&rtree->node_freepool);
+}
diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
new file mode 100755
index 000000000..da7b34698
--- /dev/null
+++ b/src/cairo-scaled-font-private.h
@@ -0,0 +1,183 @@
+/* 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"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t;
+
+struct _cairo_scaled_font {
+ /* For most cairo objects, the rule for multiple threads is that
+ * the user is responsible for any locking if the same object is
+ * manipulated from multiple threads simultaneously.
+ *
+ * However, with the caching that cairo does for scaled fonts, a
+ * user can easily end up with the same cairo_scaled_font object
+ * being manipulated from multiple threads without the user ever
+ * being aware of this, (and in fact, unable to control it).
+ *
+ * So, as a special exception, the cairo implementation takes care
+ * of all locking needed for cairo_scaled_font_t. Most of what is
+ * in the scaled font is immutable, (which is what allows for the
+ * sharing in the first place). The things that are modified and
+ * the locks protecting them are as follows:
+ *
+ * 1. The reference count (scaled_font->ref_count)
+ *
+ * Modifications to the reference count are protected by the
+ * _cairo_scaled_font_map_mutex. This is because the reference
+ * count of a scaled font is intimately related with the font
+ * map itself, (and the magic holdovers array).
+ *
+ * 2. The cache of glyphs (scaled_font->glyphs)
+ * 3. The backend private data (scaled_font->surface_backend,
+ * scaled_font->surface_private)
+ *
+ * Modifications to these fields are protected with locks on
+ * scaled_font->mutex in the generic scaled_font code.
+ */
+
+ cairo_hash_entry_t hash_entry;
+
+ /* useful bits for _cairo_scaled_font_nil */
+ cairo_status_t status;
+ cairo_reference_count_t ref_count;
+ cairo_user_data_array_t user_data;
+
+ cairo_font_face_t *original_font_face; /* may be NULL */
+
+ /* hash key members */
+ cairo_font_face_t *font_face; /* may be NULL */
+ cairo_matrix_t font_matrix; /* font space => user space */
+ cairo_matrix_t ctm; /* user space => device space */
+ cairo_font_options_t options;
+
+ unsigned int placeholder : 1; /* protected by fontmap mutex */
+ unsigned int holdover : 1;
+ unsigned int finished : 1;
+
+ /* "live" scaled_font members */
+ cairo_matrix_t scale; /* font space => device space */
+ cairo_matrix_t scale_inverse; /* device space => font space */
+ double max_scale; /* maximum x/y expansion of scale */
+ cairo_font_extents_t extents; /* user space */
+ cairo_font_extents_t fs_extents; /* font space */
+
+ /* The mutex protects modification to all subsequent fields. */
+ cairo_mutex_t mutex;
+
+ cairo_hash_table_t *glyphs;
+ cairo_list_t glyph_pages;
+ cairo_bool_t cache_frozen;
+ cairo_bool_t global_cache_frozen;
+
+ cairo_list_t dev_privates;
+
+ /* font backend managing this scaled font */
+ const cairo_scaled_font_backend_t *backend;
+ cairo_list_t link;
+};
+
+struct _cairo_scaled_font_private {
+ cairo_list_t link;
+ const void *key;
+ void (*destroy) (cairo_scaled_font_private_t *,
+ cairo_scaled_font_t *);
+};
+
+struct _cairo_scaled_glyph {
+ cairo_hash_entry_t hash_entry;
+
+ cairo_text_extents_t metrics; /* user-space metrics */
+ cairo_text_extents_t fs_metrics; /* font-space metrics */
+ cairo_box_t bbox; /* device-space bounds */
+ int16_t x_advance; /* device-space rounded X advance */
+ int16_t y_advance; /* device-space rounded Y advance */
+
+ unsigned int has_info;
+ cairo_image_surface_t *surface; /* device-space image */
+ cairo_path_fixed_t *path; /* device-space outline */
+ cairo_surface_t *recording_surface; /* device-space recording-surface */
+
+ const void *dev_private_key;
+ void *dev_private;
+ cairo_list_t dev_privates;
+};
+
+struct _cairo_scaled_glyph_private {
+ cairo_list_t link;
+ const void *key;
+ void (*destroy) (cairo_scaled_glyph_private_t *,
+ cairo_scaled_glyph_t *,
+ cairo_scaled_font_t *);
+};
+
+cairo_private cairo_scaled_font_private_t *
+_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font,
+ const void *key);
+
+cairo_private void
+_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_font_private_t *priv,
+ const void *key,
+ void (*destroy) (cairo_scaled_font_private_t *,
+ cairo_scaled_font_t *));
+
+cairo_private cairo_scaled_glyph_private_t *
+_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph,
+ const void *key);
+
+cairo_private void
+_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_glyph_private_t *priv,
+ const void *key,
+ void (*destroy) (cairo_scaled_glyph_private_t *,
+ cairo_scaled_glyph_t *,
+ cairo_scaled_font_t *));
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SCALED_FONT_PRIVATE_H */
diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h
new file mode 100755
index 000000000..dd1996258
--- /dev/null
+++ b/src/cairo-scaled-font-subsets-private.h
@@ -0,0 +1,720 @@
+/* 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;
+ cairo_bool_t is_latin;
+ double x_advance;
+ double y_advance;
+ cairo_bool_t utf8_is_mapped;
+ uint32_t unicode;
+} cairo_scaled_font_subsets_glyph_t;
+
+/**
+ * _cairo_scaled_font_subsets_create_scaled:
+ *
+ * Create a new #cairo_scaled_font_subsets_t object which can be used
+ * to create subsets of any number of #cairo_scaled_font_t
+ * objects. This allows the (arbitrarily large and sparse) glyph
+ * indices of a #cairo_scaled_font_t to be mapped to one or more font
+ * subsets with glyph indices packed into the range
+ * [0 .. max_glyphs_per_subset).
+ *
+ * Return value: a pointer to the newly creates font subsets. The
+ * caller owns this object and should call
+ * _cairo_scaled_font_subsets_destroy() when done with it.
+ **/
+cairo_private cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_scaled (void);
+
+/**
+ * _cairo_scaled_font_subsets_create_simple:
+ *
+ * Create a new #cairo_scaled_font_subsets_t object which can be used
+ * to create font subsets suitable for embedding as Postscript or PDF
+ * simple fonts.
+ *
+ * Glyphs with an outline path available will be mapped to one font
+ * subset for each font face. Glyphs from bitmap fonts will mapped to
+ * separate font subsets for each #cairo_scaled_font_t object.
+ *
+ * The maximum number of glyphs per subset is 256. Each subset
+ * reserves the first glyph for the .notdef glyph.
+ *
+ * Return value: a pointer to the newly creates font subsets. The
+ * caller owns this object and should call
+ * _cairo_scaled_font_subsets_destroy() when done with it.
+ **/
+cairo_private cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_simple (void);
+
+/**
+ * _cairo_scaled_font_subsets_create_composite:
+ *
+ * Create a new #cairo_scaled_font_subsets_t object which can be used
+ * to create font subsets suitable for embedding as Postscript or PDF
+ * composite fonts.
+ *
+ * Glyphs with an outline path available will be mapped to one font
+ * subset for each font face. Each unscaled subset has a maximum of
+ * 65536 glyphs except for Type1 fonts which have a maximum of 256 glyphs.
+ *
+ * Glyphs from bitmap fonts will mapped to separate font subsets for
+ * each #cairo_scaled_font_t object. Each unscaled subset has a maximum
+ * of 256 glyphs.
+ *
+ * Each subset reserves the first glyph for the .notdef glyph.
+ *
+ * Return value: a pointer to the newly creates font subsets. The
+ * caller owns this object and should call
+ * _cairo_scaled_font_subsets_destroy() when done with it.
+ **/
+cairo_private cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_composite (void);
+
+/**
+ * _cairo_scaled_font_subsets_destroy:
+ * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed
+ *
+ * Destroys @font_subsets and all resources associated with it.
+ **/
+cairo_private void
+_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets);
+
+/**
+ * _cairo_scaled_font_subsets_enable_latin_subset:
+ * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed
+ * @use_latin: a #cairo_bool_t indicating if a latin subset is to be used
+ *
+ * If enabled, all CP1252 characters will be placed in a separate
+ * 8-bit latin subset.
+ **/
+cairo_private void
+_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_bool_t use_latin);
+
+/**
+ * _cairo_scaled_font_subsets_map_glyph:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @scaled_font: the font of the glyph to be mapped
+ * @scaled_font_glyph_index: the index of the glyph to be mapped
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes
+ * @subset_glyph_ret: return structure containing subset font and glyph id
+ *
+ * Map a glyph from a #cairo_scaled_font to a new index within a
+ * subset of that font. The mapping performed is from the tuple:
+ *
+ * (scaled_font, scaled_font_glyph_index)
+ *
+ * to the tuple:
+ *
+ * (font_id, subset_id, subset_glyph_index)
+ *
+ * This mapping is 1:1. If the input tuple has previously mapped, the
+ * the output tuple previously returned will be returned again.
+ *
+ * Otherwise, the return tuple will be constructed as follows:
+ *
+ * 1) There is a 1:1 correspondence between the input scaled_font
+ * value and the output font_id value. If no mapping has been
+ * previously performed with the scaled_font value then the
+ * smallest unused font_id value will be returned.
+ *
+ * 2) Within the set of output tuples of the same font_id value the
+ * smallest value of subset_id will be returned such that
+ * subset_glyph_index does not exceed max_glyphs_per_subset (as
+ * passed to _cairo_scaled_font_subsets_create()) and that the
+ * resulting tuple is unique.
+ *
+ * 3) The smallest value of subset_glyph_index is returned such that
+ * the resulting tuple is unique.
+ *
+ * The net result is that any #cairo_scaled_font_t will be represented
+ * by one or more font subsets. Each subset is effectively a tuple of
+ * (scaled_font, font_id, subset_id) and within each subset there
+ * exists a mapping of scaled_glyph_font_index to subset_glyph_index.
+ *
+ * This final description of a font subset is the same representation
+ * used by #cairo_scaled_font_subset_t as provided by
+ * _cairo_scaled_font_subsets_foreach.
+ *
+ * @utf8 and @utf8_len specify a string of unicode characters that the
+ * glyph @scaled_font_glyph_index maps to. If @utf8_is_mapped in
+ * @subset_glyph_ret is %TRUE, the font subsetting will (where index to
+ * unicode mapping is supported) ensure that @scaled_font_glyph_index
+ * maps to @utf8. If @utf8_is_mapped is %FALSE,
+ * @scaled_font_glyph_index has already been mapped to a different
+ * unicode string.
+ *
+ * The returned values in the #cairo_scaled_font_subsets_glyph_t struct are:
+ *
+ * @font_id: The font ID of the mapped glyph
+ * @subset_id : The subset ID of the mapped glyph within the @font_id
+ * @subset_glyph_index: The index of the mapped glyph within the @subset_id subset
+ * @is_scaled: If true, the mapped glyph is from a bitmap font, and separate font
+ * subset is created for each font scale used. If false, the outline of the mapped glyph
+ * is available. One font subset for each font face is created.
+ * @x_advance, @y_advance: When @is_scaled is true, @x_advance and @y_advance contain
+ * the x and y advance for the mapped glyph in device space.
+ * When @is_scaled is false, @x_advance and @y_advance contain the x and y advance for
+ * the the mapped glyph from an unhinted 1 point font.
+ * @utf8_is_mapped: If true the utf8 string provided to _cairo_scaled_font_subsets_map_glyph()
+ * is (or already was) the utf8 string mapped to this glyph. If false the glyph is already
+ * mapped to a different utf8 string.
+ * @unicode: the unicode character mapped to this glyph by the font backend.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_t *scaled_font,
+ unsigned long scaled_font_glyph_index,
+ const char * utf8,
+ int utf8_len,
+ cairo_scaled_font_subsets_glyph_t *subset_glyph_ret);
+
+typedef cairo_int_status_t
+(*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset,
+ void *closure);
+
+/**
+ * _cairo_scaled_font_subsets_foreach_scaled:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @font_subset_callback: a function to be called for each font subset
+ * @closure: closure data for the callback function
+ *
+ * Iterate over each unique scaled font subset as created by calls to
+ * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by
+ * unique pairs of (font_id, subset_id) as returned by
+ * _cairo_scaled_font_subsets_map_glyph().
+ *
+ * For each subset, @font_subset_callback will be called and will be
+ * provided with both a #cairo_scaled_font_subset_t object containing
+ * all the glyphs in the subset as well as the value of @closure.
+ *
+ * The #cairo_scaled_font_subset_t object contains the scaled_font,
+ * the font_id, and the subset_id corresponding to all glyphs
+ * belonging to the subset. In addition, it contains an array providing
+ * a mapping between subset glyph indices and the original scaled font
+ * glyph indices.
+ *
+ * The index of the array corresponds to subset_glyph_index values
+ * returned by _cairo_scaled_font_subsets_map_glyph() while the
+ * values of the array correspond to the scaled_font_glyph_index
+ * values passed as input to the same function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_subset_callback_func_t font_subset_callback,
+ void *closure);
+
+/**
+ * _cairo_scaled_font_subsets_foreach_unscaled:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @font_subset_callback: a function to be called for each font subset
+ * @closure: closure data for the callback function
+ *
+ * Iterate over each unique unscaled font subset as created by calls to
+ * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by
+ * unique pairs of (font_id, subset_id) as returned by
+ * _cairo_scaled_font_subsets_map_glyph().
+ *
+ * For each subset, @font_subset_callback will be called and will be
+ * provided with both a #cairo_scaled_font_subset_t object containing
+ * all the glyphs in the subset as well as the value of @closure.
+ *
+ * The #cairo_scaled_font_subset_t object contains the scaled_font,
+ * the font_id, and the subset_id corresponding to all glyphs
+ * belonging to the subset. In addition, it contains an array providing
+ * a mapping between subset glyph indices and the original scaled font
+ * glyph indices.
+ *
+ * The index of the array corresponds to subset_glyph_index values
+ * returned by _cairo_scaled_font_subsets_map_glyph() while the
+ * values of the array correspond to the scaled_font_glyph_index
+ * values passed as input to the same function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_subset_callback_func_t font_subset_callback,
+ void *closure);
+
+/**
+ * _cairo_scaled_font_subsets_foreach_user:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @font_subset_callback: a function to be called for each font subset
+ * @closure: closure data for the callback function
+ *
+ * Iterate over each unique scaled font subset as created by calls to
+ * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by
+ * unique pairs of (font_id, subset_id) as returned by
+ * _cairo_scaled_font_subsets_map_glyph().
+ *
+ * For each subset, @font_subset_callback will be called and will be
+ * provided with both a #cairo_scaled_font_subset_t object containing
+ * all the glyphs in the subset as well as the value of @closure.
+ *
+ * The #cairo_scaled_font_subset_t object contains the scaled_font,
+ * the font_id, and the subset_id corresponding to all glyphs
+ * belonging to the subset. In addition, it contains an array providing
+ * a mapping between subset glyph indices and the original scaled font
+ * glyph indices.
+ *
+ * The index of the array corresponds to subset_glyph_index values
+ * returned by _cairo_scaled_font_subsets_map_glyph() while the
+ * values of the array correspond to the scaled_font_glyph_index
+ * values passed as input to the same function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_subset_callback_func_t font_subset_callback,
+ void *closure);
+
+/**
+ * _cairo_scaled_font_subset_create_glyph_names:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ *
+ * Create an array of strings containing the glyph name for each glyph
+ * in @font_subsets. The array as store in font_subsets->glyph_names.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font backend does not support
+ * mapping the glyph indices to unicode characters. Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset);
+
+typedef struct _cairo_cff_subset {
+ char *family_name_utf8;
+ char *ps_name;
+ double *widths;
+ double x_min, y_min, x_max, y_max;
+ double ascent, descent;
+ char *data;
+ unsigned long data_length;
+} cairo_cff_subset_t;
+
+/**
+ * _cairo_cff_subset_init:
+ * @cff_subset: a #cairo_cff_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a
+ * cff file corresponding to @font_subset and initialize
+ * @cff_subset with information about the subset and the cff
+ * data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * cff file, or an non-zero value indicating an error. Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_cff_subset_fini:
+ * @cff_subset: a #cairo_cff_subset_t
+ *
+ * Free all resources associated with @cff_subset. After this
+ * call, @cff_subset should not be used again without a
+ * subsequent call to _cairo_cff_subset_init() again first.
+ **/
+cairo_private void
+_cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset);
+
+/**
+ * _cairo_cff_scaled_font_is_cid_cff:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Return %TRUE if @scaled_font is a CID CFF font, otherwise return %FALSE.
+ **/
+cairo_private cairo_bool_t
+_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font);
+
+/**
+ * _cairo_cff_fallback_init:
+ * @cff_subset: a #cairo_cff_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a cff
+ * file corresponding to @font_subset and initialize @cff_subset
+ * with information about the subset and the cff data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * cff file, or an non-zero value indicating an error. Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_cff_fallback_fini:
+ * @cff_subset: a #cairo_cff_subset_t
+ *
+ * Free all resources associated with @cff_subset. After this
+ * call, @cff_subset should not be used again without a
+ * subsequent call to _cairo_cff_subset_init() again first.
+ **/
+cairo_private void
+_cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset);
+
+typedef struct _cairo_truetype_subset {
+ char *family_name_utf8;
+ char *ps_name;
+ double *widths;
+ double x_min, y_min, x_max, y_max;
+ double ascent, descent;
+ unsigned char *data;
+ unsigned long data_length;
+ unsigned long *string_offsets;
+ unsigned long num_string_offsets;
+} cairo_truetype_subset_t;
+
+/**
+ * _cairo_truetype_subset_init_ps:
+ * @truetype_subset: a #cairo_truetype_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a
+ * truetype file corresponding to @font_subset and initialize
+ * @truetype_subset with information about the subset and the truetype
+ * data. The generated font will be suitable for embedding in
+ * PostScript.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * truetype file, or an non-zero value indicating an error. Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_truetype_subset_init_pdf:
+ * @truetype_subset: a #cairo_truetype_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a
+ * truetype file corresponding to @font_subset and initialize
+ * @truetype_subset with information about the subset and the truetype
+ * data. The generated font will be suitable for embedding in
+ * PDF.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * truetype file, or an non-zero value indicating an error. Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_truetype_subset_fini:
+ * @truetype_subset: a #cairo_truetype_subset_t
+ *
+ * Free all resources associated with @truetype_subset. After this
+ * call, @truetype_subset should not be used again without a
+ * subsequent call to _cairo_truetype_subset_init() again first.
+ **/
+cairo_private void
+_cairo_truetype_subset_fini (cairo_truetype_subset_t *truetype_subset);
+
+cairo_private const char *
+_cairo_ps_standard_encoding_to_glyphname (int glyph);
+
+cairo_private int
+_cairo_unicode_to_winansi (unsigned long unicode);
+
+cairo_private const char *
+_cairo_winansi_to_glyphname (int glyph);
+
+typedef struct _cairo_type1_subset {
+ char *base_font;
+ double *widths;
+ double x_min, y_min, x_max, y_max;
+ double ascent, descent;
+ char *data;
+ unsigned long header_length;
+ unsigned long data_length;
+ unsigned long trailer_length;
+} cairo_type1_subset_t;
+
+
+/**
+ * _cairo_type1_subset_init:
+ * @type1_subset: a #cairo_type1_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ * @hex_encode: if true the encrypted portion of the font is hex encoded
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a type1
+ * file corresponding to @font_subset and initialize @type1_subset
+ * with information about the subset and the type1 data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1
+ * file, or an non-zero value indicating an error. Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type1_subset_init (cairo_type1_subset_t *type_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *font_subset,
+ cairo_bool_t hex_encode);
+
+/**
+ * _cairo_type1_subset_fini:
+ * @type1_subset: a #cairo_type1_subset_t
+ *
+ * Free all resources associated with @type1_subset. After this call,
+ * @type1_subset should not be used again without a subsequent call to
+ * _cairo_truetype_type1_init() again first.
+ **/
+cairo_private void
+_cairo_type1_subset_fini (cairo_type1_subset_t *subset);
+
+/**
+ * _cairo_type1_scaled_font_is_type1:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Return %TRUE if @scaled_font is a Type 1 font, otherwise return %FALSE.
+ **/
+cairo_private cairo_bool_t
+_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font);
+
+/**
+ * _cairo_type1_fallback_init_binary:
+ * @type1_subset: a #cairo_type1_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a type1
+ * file corresponding to @font_subset and initialize @type1_subset
+ * with information about the subset and the type1 data. The encrypted
+ * part of the font is binary encoded.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1
+ * file, or an non-zero value indicating an error. Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_type1_fallback_init_hex:
+ * @type1_subset: a #cairo_type1_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a type1
+ * file corresponding to @font_subset and initialize @type1_subset
+ * with information about the subset and the type1 data. The encrypted
+ * part of the font is hex encoded.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1
+ * file, or an non-zero value indicating an error. Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_type1_fallback_fini:
+ * @type1_subset: a #cairo_type1_subset_t
+ *
+ * Free all resources associated with @type1_subset. After this call,
+ * @type1_subset should not be used again without a subsequent call to
+ * _cairo_truetype_type1_init() again first.
+ **/
+cairo_private void
+_cairo_type1_fallback_fini (cairo_type1_subset_t *subset);
+
+typedef struct _cairo_type2_charstrings {
+ int *widths;
+ long x_min, y_min, x_max, y_max;
+ long ascent, descent;
+ cairo_array_t charstrings;
+} cairo_type2_charstrings_t;
+
+/**
+ * _cairo_type2_charstrings_init:
+ * @type2_subset: a #cairo_type2_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate type2
+ * charstrings to @font_subset and initialize @type2_subset
+ * with information about the subset.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type2
+ * charstrings, or an non-zero value indicating an error. Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type2_charstrings_init (cairo_type2_charstrings_t *charstrings,
+ cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_type2_charstrings_fini:
+ * @subset: a #cairo_type2_charstrings_t
+ *
+ * Free all resources associated with @type2_charstring. After this call,
+ * @type2_charstring should not be used again without a subsequent call to
+ * _cairo_type2_charstring_init() again first.
+ **/
+cairo_private void
+_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings);
+
+/**
+ * _cairo_truetype_index_to_ucs4:
+ * @scaled_font: the #cairo_scaled_font_t
+ * @index: the glyph index
+ * @ucs4: return value for the unicode value of the glyph
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) assign
+ * the unicode character of the glyph to @ucs4.
+ *
+ * If mapping glyph indices to unicode is supported but the unicode
+ * value of the specified glyph is not available, @ucs4 is set to -1.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if mapping glyph indices to unicode
+ * is not supported. Possible errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font,
+ unsigned long index,
+ uint32_t *ucs4);
+
+/**
+ * _cairo_truetype_read_font_name:
+ * @scaled_font: the #cairo_scaled_font_t
+ * @ps_name: returns the PostScript name of the font
+ * or %NULL if the name could not be found.
+ * @font_name: returns the font name or %NULL if the name could not be found.
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) read the
+ * PostScript and Font names from a TrueType/OpenType font.
+ *
+ * The font name is the full name of the font eg "DejaVu Sans Bold".
+ * The PostScript name is a shortened name with spaces removed
+ * suitable for use as the font name in a PS or PDF file eg
+ * "DejaVuSans-Bold".
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType
+ * or the name table is not present. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font,
+ char **ps_name,
+ char **font_name);
+
+/**
+ * _cairo_truetype_get_style:
+ * @scaled_font: the #cairo_scaled_font_t
+ * @weight: returns the font weight from the OS/2 table
+ * @bold: returns true if font is bold
+ * @italic: returns true if font is italic
+ *
+ * If the font is a truetype/opentype font with an OS/2 table, get the
+ * weight, bold, and italic data from the OS/2 table. The weight
+ * values have the same meaning as the lfWeight field of the Windows
+ * LOGFONT structure. Refer to the TrueType Specification for
+ * definition of the weight values.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType
+ * or the OS/2 table is not present.
+ **/
+cairo_private cairo_int_status_t
+_cairo_truetype_get_style (cairo_scaled_font_t *scaled_font,
+ int *weight,
+ cairo_bool_t *bold,
+ cairo_bool_t *italic);
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
+
+#endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */
diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
new file mode 100755
index 000000000..e78e0c283
--- /dev/null
+++ b/src/cairo-scaled-font-subsets.c
@@ -0,0 +1,1259 @@
+/* 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_bool_t use_latin_subset;
+ cairo_scaled_font_subsets_t *parent;
+ cairo_scaled_font_t *scaled_font;
+ unsigned int font_id;
+
+ int current_subset;
+ int num_glyphs_in_current_subset;
+ int num_glyphs_in_latin_subset;
+ int max_glyphs_per_subset;
+ char latin_char_map[256];
+
+ cairo_hash_table_t *sub_font_glyphs;
+ struct _cairo_sub_font *next;
+} cairo_sub_font_t;
+
+struct _cairo_scaled_font_subsets {
+ cairo_subsets_type_t type;
+ cairo_bool_t use_latin_subset;
+
+ int max_glyphs_per_unscaled_subset_used;
+ cairo_hash_table_t *unscaled_sub_fonts;
+ cairo_sub_font_t *unscaled_sub_fonts_list;
+ cairo_sub_font_t *unscaled_sub_fonts_list_end;
+
+ int max_glyphs_per_scaled_subset_used;
+ cairo_hash_table_t *scaled_sub_fonts;
+ cairo_sub_font_t *scaled_sub_fonts_list;
+ cairo_sub_font_t *scaled_sub_fonts_list_end;
+
+ int num_sub_fonts;
+};
+
+typedef struct _cairo_sub_font_glyph {
+ cairo_hash_entry_t base;
+
+ unsigned int subset_id;
+ unsigned int subset_glyph_index;
+ double x_advance;
+ double y_advance;
+
+ cairo_bool_t is_latin;
+ int latin_character;
+ cairo_bool_t is_mapped;
+ uint32_t unicode;
+ char *utf8;
+ int utf8_len;
+} cairo_sub_font_glyph_t;
+
+typedef struct _cairo_sub_font_collection {
+ unsigned long *glyphs; /* scaled_font_glyph_index */
+ char **utf8;
+ unsigned int glyphs_size;
+ int *to_latin_char;
+ unsigned long *latin_to_subset_glyph_index;
+ unsigned int max_glyph;
+ unsigned int num_glyphs;
+
+ unsigned int subset_id;
+
+ cairo_status_t status;
+ cairo_scaled_font_subset_callback_func_t font_subset_callback;
+ void *font_subset_callback_closure;
+} cairo_sub_font_collection_t;
+
+typedef struct _cairo_string_entry {
+ cairo_hash_entry_t base;
+ char *string;
+} cairo_string_entry_t;
+
+static cairo_status_t
+_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
+ unsigned long scaled_font_glyph_index,
+ const char * utf8,
+ int utf8_len,
+ cairo_scaled_font_subsets_glyph_t *subset_glyph);
+
+static void
+_cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph,
+ unsigned long scaled_font_glyph_index)
+{
+ sub_font_glyph->base.hash = scaled_font_glyph_index;
+}
+
+static cairo_sub_font_glyph_t *
+_cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index,
+ unsigned int subset_id,
+ unsigned int subset_glyph_index,
+ double x_advance,
+ double y_advance,
+ int latin_character,
+ uint32_t unicode,
+ char *utf8,
+ int utf8_len)
+{
+ cairo_sub_font_glyph_t *sub_font_glyph;
+
+ sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t));
+ if (unlikely (sub_font_glyph == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index);
+ sub_font_glyph->subset_id = subset_id;
+ sub_font_glyph->subset_glyph_index = subset_glyph_index;
+ sub_font_glyph->x_advance = x_advance;
+ sub_font_glyph->y_advance = y_advance;
+ sub_font_glyph->is_latin = (latin_character >= 0);
+ sub_font_glyph->latin_character = latin_character;
+ sub_font_glyph->is_mapped = FALSE;
+ sub_font_glyph->unicode = unicode;
+ sub_font_glyph->utf8 = utf8;
+ sub_font_glyph->utf8_len = utf8_len;
+
+ return sub_font_glyph;
+}
+
+static void
+_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph)
+{
+ free (sub_font_glyph->utf8);
+
+ free (sub_font_glyph);
+}
+
+static void
+_cairo_sub_font_glyph_pluck (void *entry, void *closure)
+{
+ cairo_sub_font_glyph_t *sub_font_glyph = entry;
+ cairo_hash_table_t *sub_font_glyphs = closure;
+
+ _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base);
+ _cairo_sub_font_glyph_destroy (sub_font_glyph);
+}
+
+static void
+_cairo_sub_font_glyph_collect (void *entry, void *closure)
+{
+ cairo_sub_font_glyph_t *sub_font_glyph = entry;
+ cairo_sub_font_collection_t *collection = closure;
+ unsigned long scaled_font_glyph_index;
+ unsigned int subset_glyph_index;
+
+ if (sub_font_glyph->subset_id != collection->subset_id)
+ return;
+
+ scaled_font_glyph_index = sub_font_glyph->base.hash;
+ subset_glyph_index = sub_font_glyph->subset_glyph_index;
+
+ /* Ensure we don't exceed the allocated bounds. */
+ assert (subset_glyph_index < collection->glyphs_size);
+
+ collection->glyphs[subset_glyph_index] = scaled_font_glyph_index;
+ collection->utf8[subset_glyph_index] = sub_font_glyph->utf8;
+ collection->to_latin_char[subset_glyph_index] = sub_font_glyph->latin_character;
+ if (sub_font_glyph->is_latin)
+ collection->latin_to_subset_glyph_index[sub_font_glyph->latin_character] = subset_glyph_index;
+
+ if (subset_glyph_index > collection->max_glyph)
+ collection->max_glyph = subset_glyph_index;
+
+ collection->num_glyphs++;
+}
+
+static cairo_bool_t
+_cairo_sub_fonts_equal (const void *key_a, const void *key_b)
+{
+ const cairo_sub_font_t *sub_font_a = key_a;
+ const cairo_sub_font_t *sub_font_b = key_b;
+ cairo_scaled_font_t *a = sub_font_a->scaled_font;
+ cairo_scaled_font_t *b = sub_font_b->scaled_font;
+
+ if (sub_font_a->is_scaled)
+ return a == b;
+ else
+ return a->font_face == b->font_face || a->original_font_face == b->original_font_face;
+}
+
+static void
+_cairo_sub_font_init_key (cairo_sub_font_t *sub_font,
+ cairo_scaled_font_t *scaled_font)
+{
+ if (sub_font->is_scaled)
+ {
+ sub_font->base.hash = (unsigned long) scaled_font;
+ sub_font->scaled_font = scaled_font;
+ }
+ else
+ {
+ sub_font->base.hash = (unsigned long) scaled_font->font_face;
+ sub_font->scaled_font = scaled_font;
+ }
+}
+
+static cairo_status_t
+_cairo_sub_font_create (cairo_scaled_font_subsets_t *parent,
+ cairo_scaled_font_t *scaled_font,
+ unsigned int font_id,
+ int max_glyphs_per_subset,
+ cairo_bool_t is_scaled,
+ cairo_bool_t is_composite,
+ cairo_sub_font_t **sub_font_out)
+{
+ cairo_sub_font_t *sub_font;
+ int i;
+
+ sub_font = 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->use_latin_subset = parent->use_latin_subset;
+
+ /* latin subsets of Type 3 and CID CFF fonts are not supported */
+ if (sub_font->is_user || sub_font->is_scaled ||
+ _cairo_cff_scaled_font_is_cid_cff (scaled_font) )
+ {
+ sub_font->use_latin_subset = FALSE;
+ }
+
+ if (sub_font->use_latin_subset)
+ sub_font->current_subset = 1; /* reserve subset 0 for latin glyphs */
+ else
+ sub_font->current_subset = 0;
+
+ sub_font->num_glyphs_in_current_subset = 0;
+ sub_font->num_glyphs_in_latin_subset = 0;
+ sub_font->max_glyphs_per_subset = max_glyphs_per_subset;
+ for (i = 0; i < 256; i++)
+ sub_font->latin_char_map[i] = FALSE;
+
+ sub_font->sub_font_glyphs = _cairo_hash_table_create (NULL);
+ if (unlikely (sub_font->sub_font_glyphs == NULL)) {
+ free (sub_font);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ sub_font->next = NULL;
+ *sub_font_out = sub_font;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_sub_font_destroy (cairo_sub_font_t *sub_font)
+{
+ _cairo_hash_table_foreach (sub_font->sub_font_glyphs,
+ _cairo_sub_font_glyph_pluck,
+ sub_font->sub_font_glyphs);
+ _cairo_hash_table_destroy (sub_font->sub_font_glyphs);
+ cairo_scaled_font_destroy (sub_font->scaled_font);
+ free (sub_font);
+}
+
+static void
+_cairo_sub_font_pluck (void *entry, void *closure)
+{
+ cairo_sub_font_t *sub_font = entry;
+ cairo_hash_table_t *sub_fonts = closure;
+
+ _cairo_hash_table_remove (sub_fonts, &sub_font->base);
+ _cairo_sub_font_destroy (sub_font);
+}
+
+/* Characters 0x80 to 0x9f in the winansi encoding.
+ * All other characters in the range 0x00 to 0xff map 1:1 to unicode */
+static unsigned int _winansi_0x80_to_0x9f[] = {
+ 0x20ac, 0x0000, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017d, 0x0000,
+ 0x0000, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x0000, 0x017e, 0x0178
+};
+
+int
+_cairo_unicode_to_winansi (unsigned long uni)
+{
+ int i;
+
+ /* exclude the extra "hyphen" at 0xad to avoid duplicate glyphnames */
+ if ((uni >= 0x20 && uni <= 0x7e) ||
+ (uni >= 0xa1 && uni <= 0xff && uni != 0xad) ||
+ uni == 0)
+ return uni;
+
+ for (i = 0; i < 32; i++)
+ if (_winansi_0x80_to_0x9f[i] == uni)
+ return i + 0x80;
+
+ return -1;
+}
+
+static cairo_status_t
+_cairo_sub_font_glyph_lookup_unicode (cairo_scaled_font_t *scaled_font,
+ unsigned long scaled_font_glyph_index,
+ uint32_t *unicode_out,
+ char **utf8_out,
+ int *utf8_len_out)
+{
+ uint32_t unicode;
+ char buf[8];
+ int len;
+ cairo_status_t status;
+
+ /* Do a reverse lookup on the glyph index. unicode is -1 if the
+ * index could not be mapped to a unicode character. */
+ unicode = -1;
+ status = _cairo_truetype_index_to_ucs4 (scaled_font,
+ scaled_font_glyph_index,
+ &unicode);
+ if (_cairo_status_is_error (status))
+ return status;
+
+ if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) {
+ status = scaled_font->backend->index_to_ucs4 (scaled_font,
+ scaled_font_glyph_index,
+ &unicode);
+ if (unlikely (status))
+ return status;
+ }
+
+ *unicode_out = unicode;
+ *utf8_out = NULL;
+ *utf8_len_out = 0;
+ if (unicode != (uint32_t) -1) {
+ len = _cairo_ucs4_to_utf8 (unicode, buf);
+ if (len > 0) {
+ *utf8_out = malloc (len + 1);
+ if (unlikely (*utf8_out == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (*utf8_out, buf, len);
+ (*utf8_out)[len] = 0;
+ *utf8_len_out = len;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
+ const char *utf8,
+ int utf8_len,
+ cairo_bool_t *is_mapped)
+{
+ *is_mapped = FALSE;
+
+ if (utf8_len < 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0')
+ utf8_len--;
+
+ if (utf8 != NULL && utf8_len != 0) {
+ if (sub_font_glyph->utf8 != NULL) {
+ if (utf8_len == sub_font_glyph->utf8_len &&
+ memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0)
+ {
+ /* Requested utf8 mapping matches the existing mapping */
+ *is_mapped = TRUE;
+ }
+ } else {
+ /* No existing mapping. Use the requested mapping */
+ sub_font_glyph->utf8 = malloc (utf8_len + 1);
+ if (unlikely (sub_font_glyph->utf8 == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (sub_font_glyph->utf8, utf8, utf8_len);
+ sub_font_glyph->utf8[utf8_len] = 0;
+ sub_font_glyph->utf8_len = utf8_len;
+ *is_mapped = TRUE;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font,
+ unsigned long scaled_font_glyph_index,
+ const char *utf8,
+ int utf8_len,
+ cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+ cairo_sub_font_glyph_t key, *sub_font_glyph;
+ cairo_int_status_t status;
+
+ _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
+ sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
+ &key.base);
+ if (sub_font_glyph != NULL) {
+ subset_glyph->font_id = sub_font->font_id;
+ subset_glyph->subset_id = sub_font_glyph->subset_id;
+ if (sub_font_glyph->is_latin)
+ subset_glyph->subset_glyph_index = sub_font_glyph->latin_character;
+ else
+ subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
+
+ subset_glyph->is_scaled = sub_font->is_scaled;
+ subset_glyph->is_composite = sub_font->is_composite;
+ subset_glyph->is_latin = sub_font_glyph->is_latin;
+ subset_glyph->x_advance = sub_font_glyph->x_advance;
+ subset_glyph->y_advance = sub_font_glyph->y_advance;
+ status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
+ utf8, utf8_len,
+ &subset_glyph->utf8_is_mapped);
+ subset_glyph->unicode = sub_font_glyph->unicode;
+
+ return status;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_sub_font_add_glyph (cairo_sub_font_t *sub_font,
+ unsigned long scaled_font_glyph_index,
+ cairo_bool_t is_latin,
+ int latin_character,
+ uint32_t unicode,
+ char *utf8,
+ int utf8_len,
+ cairo_sub_font_glyph_t **sub_font_glyph_out)
+{
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_sub_font_glyph_t *sub_font_glyph;
+ int *num_glyphs_in_subset_ptr;
+ double x_advance;
+ double y_advance;
+ cairo_int_status_t status;
+
+ _cairo_scaled_font_freeze_cache (sub_font->scaled_font);
+ status = _cairo_scaled_glyph_lookup (sub_font->scaled_font,
+ scaled_font_glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ if (unlikely (status)) {
+ _cairo_scaled_font_thaw_cache (sub_font->scaled_font);
+ return status;
+ }
+
+ x_advance = scaled_glyph->metrics.x_advance;
+ y_advance = scaled_glyph->metrics.y_advance;
+ _cairo_scaled_font_thaw_cache (sub_font->scaled_font);
+
+ if (!is_latin && sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset)
+ {
+ sub_font->current_subset++;
+ sub_font->num_glyphs_in_current_subset = 0;
+ }
+
+ if (is_latin)
+ num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_latin_subset;
+ else
+ num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_current_subset;
+
+ /* Reserve first glyph in subset for the .notdef glyph except for
+ * Type 3 fonts */
+ if (*num_glyphs_in_subset_ptr == 0 &&
+ scaled_font_glyph_index != 0 &&
+ ! _cairo_font_face_is_user (sub_font->scaled_font->font_face))
+ {
+ status = _cairo_sub_font_add_glyph (sub_font,
+ 0,
+ is_latin,
+ 0,
+ 0,
+ NULL,
+ -1,
+ &sub_font_glyph);
+ if (unlikely (status))
+ return status;
+ }
+
+ sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
+ is_latin ? 0 : sub_font->current_subset,
+ *num_glyphs_in_subset_ptr,
+ x_advance,
+ y_advance,
+ is_latin ? latin_character : -1,
+ unicode,
+ utf8,
+ utf8_len);
+
+ if (unlikely (sub_font_glyph == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base);
+ if (unlikely (status)) {
+ _cairo_sub_font_glyph_destroy (sub_font_glyph);
+ return status;
+ }
+
+ (*num_glyphs_in_subset_ptr)++;
+ if (sub_font->is_scaled) {
+ if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_scaled_subset_used)
+ sub_font->parent->max_glyphs_per_scaled_subset_used = *num_glyphs_in_subset_ptr;
+ } else {
+ if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_unscaled_subset_used)
+ sub_font->parent->max_glyphs_per_unscaled_subset_used = *num_glyphs_in_subset_ptr;
+ }
+
+ *sub_font_glyph_out = sub_font_glyph;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
+ unsigned long scaled_font_glyph_index,
+ const char *text_utf8,
+ int text_utf8_len,
+ cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+ cairo_sub_font_glyph_t key, *sub_font_glyph;
+ cairo_status_t status;
+
+ _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
+ sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
+ &key.base);
+ if (sub_font_glyph == NULL) {
+ uint32_t font_unicode;
+ char *font_utf8;
+ int font_utf8_len;
+ cairo_bool_t is_latin;
+ int latin_character;
+
+ status = _cairo_sub_font_glyph_lookup_unicode (sub_font->scaled_font,
+ scaled_font_glyph_index,
+ &font_unicode,
+ &font_utf8,
+ &font_utf8_len);
+ if (unlikely(status))
+ return status;
+
+ /* If the supplied utf8 is a valid single character, use it
+ * instead of the font lookup */
+ if (text_utf8 != NULL && text_utf8_len > 0) {
+ uint32_t *ucs4;
+ int ucs4_len;
+
+ status = _cairo_utf8_to_ucs4 (text_utf8, text_utf8_len,
+ &ucs4, &ucs4_len);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ if (ucs4_len == 1) {
+ font_unicode = ucs4[0];
+ free (font_utf8);
+ font_utf8 = malloc (text_utf8_len + 1);
+ if (font_utf8 == NULL) {
+ free (ucs4);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ memcpy (font_utf8, text_utf8, text_utf8_len);
+ font_utf8[text_utf8_len] = 0;
+ font_utf8_len = text_utf8_len;
+ }
+ free (ucs4);
+ }
+ }
+
+ /* If glyph is in the winansi encoding and font is not a user
+ * font, put glyph in the latin subset. If glyph is .notdef
+ * the latin subset is preferred but only if the latin subset
+ * already contains at least one glyph. We don't want to
+ * create a separate subset just for the .notdef glyph.
+ */
+ is_latin = FALSE;
+ latin_character = -1;
+ if (sub_font->use_latin_subset &&
+ (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)))
+ {
+ latin_character = _cairo_unicode_to_winansi (font_unicode);
+ if (latin_character > 0 ||
+ (latin_character == 0 && sub_font->num_glyphs_in_latin_subset > 0))
+ {
+ if (!sub_font->latin_char_map[latin_character]) {
+ sub_font->latin_char_map[latin_character] = TRUE;
+ is_latin = TRUE;
+ }
+ }
+ }
+
+ status = _cairo_sub_font_add_glyph (sub_font,
+ scaled_font_glyph_index,
+ is_latin,
+ latin_character,
+ font_unicode,
+ font_utf8,
+ font_utf8_len,
+ &sub_font_glyph);
+ if (unlikely(status))
+ return status;
+ }
+
+ subset_glyph->font_id = sub_font->font_id;
+ subset_glyph->subset_id = sub_font_glyph->subset_id;
+ if (sub_font_glyph->is_latin)
+ subset_glyph->subset_glyph_index = sub_font_glyph->latin_character;
+ else
+ subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
+
+ subset_glyph->is_scaled = sub_font->is_scaled;
+ subset_glyph->is_composite = sub_font->is_composite;
+ subset_glyph->is_latin = sub_font_glyph->is_latin;
+ subset_glyph->x_advance = sub_font_glyph->x_advance;
+ subset_glyph->y_advance = sub_font_glyph->y_advance;
+ status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
+ text_utf8, text_utf8_len,
+ &subset_glyph->utf8_is_mapped);
+ subset_glyph->unicode = sub_font_glyph->unicode;
+
+ return status;
+}
+
+static void
+_cairo_sub_font_collect (void *entry, void *closure)
+{
+ cairo_sub_font_t *sub_font = entry;
+ cairo_sub_font_collection_t *collection = closure;
+ cairo_scaled_font_subset_t subset;
+ int i;
+ unsigned int j;
+
+ if (collection->status)
+ return;
+
+ collection->status = sub_font->scaled_font->status;
+ if (collection->status)
+ return;
+
+ for (i = 0; i <= sub_font->current_subset; i++) {
+ collection->subset_id = i;
+ collection->num_glyphs = 0;
+ collection->max_glyph = 0;
+ memset (collection->latin_to_subset_glyph_index, 0, 256*sizeof(unsigned long));
+
+ _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;
+
+ subset.is_latin = FALSE;
+ if (sub_font->use_latin_subset && i == 0) {
+ subset.is_latin = TRUE;
+ subset.to_latin_char = collection->to_latin_char;
+ subset.latin_to_subset_glyph_index = collection->latin_to_subset_glyph_index;
+ } else {
+ subset.to_latin_char = NULL;
+ subset.latin_to_subset_glyph_index = NULL;
+ }
+
+ collection->status = (collection->font_subset_callback) (&subset,
+ collection->font_subset_callback_closure);
+
+ if (subset.glyph_names != NULL) {
+ for (j = 0; j < collection->num_glyphs; j++)
+ free (subset.glyph_names[j]);
+ free (subset.glyph_names);
+ }
+
+ if (collection->status)
+ break;
+ }
+}
+
+static cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type)
+{
+ cairo_scaled_font_subsets_t *subsets;
+
+ subsets = malloc (sizeof (cairo_scaled_font_subsets_t));
+ if (unlikely (subsets == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ subsets->type = type;
+ subsets->use_latin_subset = FALSE;
+ subsets->max_glyphs_per_unscaled_subset_used = 0;
+ subsets->max_glyphs_per_scaled_subset_used = 0;
+ subsets->num_sub_fonts = 0;
+
+ subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
+ if (! subsets->unscaled_sub_fonts) {
+ free (subsets);
+ return NULL;
+ }
+ subsets->unscaled_sub_fonts_list = NULL;
+ subsets->unscaled_sub_fonts_list_end = NULL;
+
+ subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
+ if (! subsets->scaled_sub_fonts) {
+ _cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
+ free (subsets);
+ return NULL;
+ }
+ subsets->scaled_sub_fonts_list = NULL;
+ subsets->scaled_sub_fonts_list_end = NULL;
+
+ return subsets;
+}
+
+cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_scaled (void)
+{
+ return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED);
+}
+
+cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_simple (void)
+{
+ return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE);
+}
+
+cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_composite (void)
+{
+ return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE);
+}
+
+void
+_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets)
+{
+ _cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts);
+ _cairo_hash_table_destroy (subsets->scaled_sub_fonts);
+
+ _cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts);
+ _cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
+
+ free (subsets);
+}
+
+void
+_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_bool_t use_latin)
+{
+ font_subsets->use_latin_subset = use_latin;
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets,
+ cairo_scaled_font_t *scaled_font,
+ unsigned long scaled_font_glyph_index,
+ const char * utf8,
+ int utf8_len,
+ cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+ cairo_sub_font_t key, *sub_font;
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_font_face_t *font_face;
+ cairo_matrix_t identity;
+ cairo_font_options_t font_options;
+ cairo_scaled_font_t *unscaled_font;
+ cairo_int_status_t status;
+ int max_glyphs;
+ cairo_bool_t type1_font;
+
+ /* Lookup glyph in unscaled subsets */
+ if (subsets->type != CAIRO_SUBSETS_SCALED) {
+ key.is_scaled = FALSE;
+ _cairo_sub_font_init_key (&key, scaled_font);
+ sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
+ &key.base);
+ if (sub_font != NULL) {
+ status = _cairo_sub_font_lookup_glyph (sub_font,
+ scaled_font_glyph_index,
+ utf8, utf8_len,
+ subset_glyph);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+ }
+
+ /* Lookup glyph in scaled subsets */
+ key.is_scaled = TRUE;
+ _cairo_sub_font_init_key (&key, scaled_font);
+ sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
+ &key.base);
+ if (sub_font != NULL) {
+ status = _cairo_sub_font_lookup_glyph (sub_font,
+ scaled_font_glyph_index,
+ utf8, utf8_len,
+ subset_glyph);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ /* Glyph not found. Determine whether the glyph is outline or
+ * bitmap and add to the appropriate subset.
+ *
+ * glyph_index 0 (the .notdef glyph) is a special case. Some fonts
+ * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a
+ * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates
+ * empty glyphs in this case so we can put the glyph in a unscaled
+ * subset. */
+ if (scaled_font_glyph_index == 0 ||
+ _cairo_font_face_is_user (scaled_font->font_face)) {
+ status = CAIRO_STATUS_SUCCESS;
+ } else {
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ scaled_font_glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_PATH,
+ &scaled_glyph);
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ }
+ if (_cairo_int_status_is_error (status))
+ return status;
+
+ if (status == CAIRO_INT_STATUS_SUCCESS &&
+ subsets->type != CAIRO_SUBSETS_SCALED &&
+ ! _cairo_font_face_is_user (scaled_font->font_face))
+ {
+ /* Path available. Add to unscaled subset. */
+ key.is_scaled = FALSE;
+ _cairo_sub_font_init_key (&key, scaled_font);
+ sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
+ &key.base);
+ if (sub_font == NULL) {
+ font_face = cairo_scaled_font_get_font_face (scaled_font);
+ cairo_matrix_init_identity (&identity);
+ _cairo_font_options_init_default (&font_options);
+ cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
+ cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
+ unscaled_font = cairo_scaled_font_create (font_face,
+ &identity,
+ &identity,
+ &font_options);
+ if (unlikely (unscaled_font->status))
+ return unscaled_font->status;
+
+ subset_glyph->is_scaled = FALSE;
+ type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font);
+ if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) {
+ max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT;
+ subset_glyph->is_composite = TRUE;
+ } else {
+ max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
+ subset_glyph->is_composite = FALSE;
+ }
+
+ status = _cairo_sub_font_create (subsets,
+ unscaled_font,
+ subsets->num_sub_fonts,
+ max_glyphs,
+ subset_glyph->is_scaled,
+ subset_glyph->is_composite,
+ &sub_font);
+
+ if (unlikely (status)) {
+ cairo_scaled_font_destroy (unscaled_font);
+ return status;
+ }
+
+ status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts,
+ &sub_font->base);
+
+ if (unlikely (status)) {
+ _cairo_sub_font_destroy (sub_font);
+ return status;
+ }
+ if (!subsets->unscaled_sub_fonts_list)
+ subsets->unscaled_sub_fonts_list = sub_font;
+ else
+ subsets->unscaled_sub_fonts_list_end->next = sub_font;
+ subsets->unscaled_sub_fonts_list_end = sub_font;
+ subsets->num_sub_fonts++;
+ }
+ } else {
+ /* No path available. Add to scaled subset. */
+ key.is_scaled = TRUE;
+ _cairo_sub_font_init_key (&key, scaled_font);
+ sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
+ &key.base);
+ if (sub_font == NULL) {
+ subset_glyph->is_scaled = TRUE;
+ subset_glyph->is_composite = FALSE;
+ if (subsets->type == CAIRO_SUBSETS_SCALED)
+ max_glyphs = INT_MAX;
+ else
+ max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
+
+ status = _cairo_sub_font_create (subsets,
+ cairo_scaled_font_reference (scaled_font),
+ subsets->num_sub_fonts,
+ max_glyphs,
+ subset_glyph->is_scaled,
+ subset_glyph->is_composite,
+ &sub_font);
+ if (unlikely (status)) {
+ cairo_scaled_font_destroy (scaled_font);
+ return status;
+ }
+
+ status = _cairo_hash_table_insert (subsets->scaled_sub_fonts,
+ &sub_font->base);
+ if (unlikely (status)) {
+ _cairo_sub_font_destroy (sub_font);
+ return status;
+ }
+ if (!subsets->scaled_sub_fonts_list)
+ subsets->scaled_sub_fonts_list = sub_font;
+ else
+ subsets->scaled_sub_fonts_list_end->next = sub_font;
+ subsets->scaled_sub_fonts_list_end = sub_font;
+ subsets->num_sub_fonts++;
+ }
+ }
+
+ return _cairo_sub_font_map_glyph (sub_font,
+ scaled_font_glyph_index,
+ utf8, utf8_len,
+ subset_glyph);
+}
+
+static cairo_status_t
+_cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_subset_callback_func_t font_subset_callback,
+ void *closure,
+ cairo_subsets_foreach_type_t type)
+{
+ cairo_sub_font_collection_t collection;
+ cairo_sub_font_t *sub_font;
+ cairo_bool_t is_scaled, is_user;
+
+ is_scaled = FALSE;
+ is_user = FALSE;
+
+ if (type == CAIRO_SUBSETS_FOREACH_USER)
+ is_user = TRUE;
+
+ if (type == CAIRO_SUBSETS_FOREACH_SCALED ||
+ type == CAIRO_SUBSETS_FOREACH_USER)
+ {
+ is_scaled = TRUE;
+ }
+
+ if (is_scaled)
+ collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used;
+ else
+ collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used;
+
+ if (! collection.glyphs_size)
+ return CAIRO_STATUS_SUCCESS;
+
+ collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long));
+ collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *));
+ collection.to_latin_char = _cairo_malloc_ab (collection.glyphs_size, sizeof(int));
+ collection.latin_to_subset_glyph_index = _cairo_malloc_ab (256, sizeof(unsigned long));
+ if (unlikely (collection.glyphs == NULL ||
+ collection.utf8 == NULL ||
+ collection.to_latin_char == NULL ||
+ collection.latin_to_subset_glyph_index == NULL)) {
+ free (collection.glyphs);
+ free (collection.utf8);
+ free (collection.to_latin_char);
+ free (collection.latin_to_subset_glyph_index);
+
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ collection.font_subset_callback = font_subset_callback;
+ collection.font_subset_callback_closure = closure;
+ collection.status = CAIRO_STATUS_SUCCESS;
+
+ if (is_scaled)
+ sub_font = font_subsets->scaled_sub_fonts_list;
+ else
+ sub_font = font_subsets->unscaled_sub_fonts_list;
+
+ while (sub_font) {
+ if (sub_font->is_user == is_user)
+ _cairo_sub_font_collect (sub_font, &collection);
+
+ sub_font = sub_font->next;
+ }
+ free (collection.utf8);
+ free (collection.glyphs);
+ free (collection.to_latin_char);
+ free (collection.latin_to_subset_glyph_index);
+
+ return collection.status;
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_subset_callback_func_t font_subset_callback,
+ void *closure)
+{
+ return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
+ font_subset_callback,
+ closure,
+ CAIRO_SUBSETS_FOREACH_SCALED);
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_subset_callback_func_t font_subset_callback,
+ void *closure)
+{
+ return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
+ font_subset_callback,
+ closure,
+ CAIRO_SUBSETS_FOREACH_UNSCALED);
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets,
+ cairo_scaled_font_subset_callback_func_t font_subset_callback,
+ void *closure)
+{
+ return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
+ font_subset_callback,
+ closure,
+ CAIRO_SUBSETS_FOREACH_USER);
+}
+
+static cairo_bool_t
+_cairo_string_equal (const void *key_a, const void *key_b)
+{
+ const cairo_string_entry_t *a = key_a;
+ const cairo_string_entry_t *b = key_b;
+
+ if (strcmp (a->string, b->string) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+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) {
+ int ch = _cairo_unicode_to_winansi (utf16[0]);
+ if (ch > 0 && _cairo_winansi_to_glyphname (ch))
+ strncpy (buf, _cairo_winansi_to_glyphname (ch), sizeof (buf));
+ else
+ snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]);
+
+ _cairo_string_init_key (&key, buf);
+ entry = _cairo_hash_table_lookup (names, &key.base);
+ if (entry != NULL)
+ snprintf (buf, sizeof (buf), "g%d", i);
+ } else {
+ snprintf (buf, sizeof (buf), "g%d", i);
+ }
+ free (utf16);
+
+ subset->glyph_names[i] = strdup (buf);
+ if (unlikely (subset->glyph_names[i] == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_HASH;
+ }
+
+ status = create_string_entry (subset->glyph_names[i], &entry);
+ if (unlikely (status))
+ goto CLEANUP_HASH;
+
+ status = _cairo_hash_table_insert (names, &entry->base);
+ if (unlikely (status)) {
+ free (entry);
+ goto CLEANUP_HASH;
+ }
+ }
+
+CLEANUP_HASH:
+ _cairo_hash_table_foreach (names, _pluck_entry, names);
+ _cairo_hash_table_destroy (names);
+
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (subset->glyph_names != NULL) {
+ for (i = 0; i < subset->num_glyphs; i++) {
+ free (subset->glyph_names[i]);
+ }
+
+ free (subset->glyph_names);
+ subset->glyph_names = NULL;
+ }
+
+ return status;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
new file mode 100755
index 000000000..ea4435aff
--- /dev/null
+++ b/src/cairo-scaled-font.c
@@ -0,0 +1,3187 @@
+/* -*- 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-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-pattern-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-backend-private.h"
+
+#define TOLERANCE 0.00001
+
+#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.
+ **/
+
+static uint32_t
+_cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font);
+
+/* Global Glyph Cache
+ *
+ * We maintain a global pool of glyphs split between all active fonts. This
+ * allows a heavily used individual font to cache more glyphs than we could
+ * manage if we used per-font glyph caches, but at the same time maintains
+ * fairness across all fonts and provides a cap on the maximum number of
+ * global glyphs.
+ *
+ * The glyphs are allocated in pages, which are capped in the global pool.
+ * Using pages means we can reduce the frequency at which we have to probe the
+ * global pool and ameliorates the memory allocation pressure.
+ */
+
+/* XXX: This number is arbitrary---we've never done any measurement of this. */
+#define MAX_GLYPH_PAGES_CACHED 512
+static cairo_cache_t cairo_scaled_glyph_page_cache;
+
+#define CAIRO_SCALED_GLYPH_PAGE_SIZE 32
+struct _cairo_scaled_glyph_page {
+ cairo_cache_entry_t cache_entry;
+
+ cairo_list_t link;
+
+ unsigned int num_glyphs;
+ cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE];
+};
+
+/*
+ * Notes:
+ *
+ * To store rasterizations of glyphs, we use an image surface and the
+ * device offset to represent the glyph origin.
+ *
+ * A device_transform converts from device space (a conceptual space) to
+ * surface space. For simple cases of translation only, it's called a
+ * device_offset and is public API (cairo_surface_[gs]et_device_offset()).
+ * A possibly better name for those functions could have been
+ * cairo_surface_[gs]et_origin(). So, that's what they do: they set where
+ * the device-space origin (0,0) is in the surface. If the origin is inside
+ * the surface, device_offset values are positive. It may look like this:
+ *
+ * Device space:
+ * (-x,-y) <-- negative numbers
+ * +----------------+
+ * | . |
+ * | . |
+ * |......(0,0) <---|-- device-space origin
+ * | |
+ * | |
+ * +----------------+
+ * (width-x,height-y)
+ *
+ * Surface space:
+ * (0,0) <-- surface-space origin
+ * +---------------+
+ * | . |
+ * | . |
+ * |......(x,y) <--|-- device_offset
+ * | |
+ * | |
+ * +---------------+
+ * (width,height)
+ *
+ * In other words: device_offset is the coordinates of the device-space
+ * origin relative to the top-left of the surface.
+ *
+ * We use device offsets in a couple of places:
+ *
+ * - Public API: To let toolkits like Gtk+ give user a surface that
+ * only represents part of the final destination (say, the expose
+ * area), but has the same device space as the destination. In these
+ * cases device_offset is typically negative. Example:
+ *
+ * application window
+ * +---------------+
+ * | . |
+ * | (x,y). |
+ * |......+---+ |
+ * | | | <--|-- expose area
+ * | +---+ |
+ * +---------------+
+ *
+ * In this case, the user of cairo API can set the device_space on
+ * the expose area to (-x,-y) to move the device space origin to that
+ * of the application window, such that drawing in the expose area
+ * surface and painting it in the application window has the same
+ * effect as drawing in the application window directly. Gtk+ has
+ * been using this feature.
+ *
+ * - Glyph surfaces: In most font rendering systems, glyph surfaces
+ * have an origin at (0,0) and a bounding box that is typically
+ * represented as (x_bearing,y_bearing,width,height). Depending on
+ * which way y progresses in the system, y_bearing may typically be
+ * negative (for systems similar to cairo, with origin at top left),
+ * or be positive (in systems like PDF with origin at bottom left).
+ * No matter which is the case, it is important to note that
+ * (x_bearing,y_bearing) is the coordinates of top-left of the glyph
+ * relative to the glyph origin. That is, for example:
+ *
+ * Scaled-glyph space:
+ *
+ * (x_bearing,y_bearing) <-- negative numbers
+ * +----------------+
+ * | . |
+ * | . |
+ * |......(0,0) <---|-- glyph origin
+ * | |
+ * | |
+ * +----------------+
+ * (width+x_bearing,height+y_bearing)
+ *
+ * Note the similarity of the origin to the device space. That is
+ * exactly how we use the device_offset to represent scaled glyphs:
+ * to use the device-space origin as the glyph origin.
+ *
+ * Now compare the scaled-glyph space to device-space and surface-space
+ * and convince yourself that:
+ *
+ * (x_bearing,y_bearing) = (-x,-y) = - device_offset
+ *
+ * That's right. If you are not convinced yet, contrast the definition
+ * of the two:
+ *
+ * "(x_bearing,y_bearing) is the coordinates of top-left of the
+ * glyph relative to the glyph origin."
+ *
+ * "In other words: device_offset is the coordinates of the
+ * device-space origin relative to the top-left of the surface."
+ *
+ * and note that glyph origin = device-space origin.
+ */
+
+static void
+_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font);
+
+static void
+_cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ while (! cairo_list_is_empty (&scaled_glyph->dev_privates)) {
+ cairo_scaled_glyph_private_t *private =
+ cairo_list_first_entry (&scaled_glyph->dev_privates,
+ cairo_scaled_glyph_private_t,
+ link);
+ private->destroy (private, scaled_glyph, scaled_font);
+ }
+
+ _cairo_image_scaled_glyph_fini (scaled_font, scaled_glyph);
+
+ if (scaled_glyph->surface != NULL)
+ cairo_surface_destroy (&scaled_glyph->surface->base);
+
+ if (scaled_glyph->path != NULL)
+ _cairo_path_fixed_destroy (scaled_glyph->path);
+
+ if (scaled_glyph->recording_surface != NULL) {
+ cairo_surface_finish (scaled_glyph->recording_surface);
+ cairo_surface_destroy (scaled_glyph->recording_surface);
+ }
+}
+
+#define ZOMBIE 0
+static const cairo_scaled_font_t _cairo_scaled_font_nil = {
+ { ZOMBIE }, /* hash_entry */
+ CAIRO_STATUS_NO_MEMORY, /* status */
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ { 0, 0, 0, NULL }, /* user_data */
+ NULL, /* original_font_face */
+ NULL, /* font_face */
+ { 1., 0., 0., 1., 0, 0}, /* font_matrix */
+ { 1., 0., 0., 1., 0, 0}, /* ctm */
+ { CAIRO_ANTIALIAS_DEFAULT, /* options */
+ CAIRO_SUBPIXEL_ORDER_DEFAULT,
+ CAIRO_HINT_STYLE_DEFAULT,
+ CAIRO_HINT_METRICS_DEFAULT} ,
+ FALSE, /* placeholder */
+ FALSE, /* holdover */
+ TRUE, /* finished */
+ { 1., 0., 0., 1., 0, 0}, /* scale */
+ { 1., 0., 0., 1., 0, 0}, /* scale_inverse */
+ 1., /* max_scale */
+ { 0., 0., 0., 0., 0. }, /* extents */
+ { 0., 0., 0., 0., 0. }, /* fs_extents */
+ CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */
+ NULL, /* glyphs */
+ { NULL, NULL }, /* pages */
+ FALSE, /* cache_frozen */
+ FALSE, /* global_cache_frozen */
+ { NULL, NULL }, /* privates */
+ NULL /* backend */
+};
+
+/**
+ * _cairo_scaled_font_set_error:
+ * @scaled_font: a scaled_font
+ * @status: a status value indicating an error
+ *
+ * Atomically sets scaled_font->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS.
+ *
+ * All assignments of an error status to scaled_font->status should happen
+ * through _cairo_scaled_font_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the nil
+ * objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ *
+ * Return value: the error status.
+ **/
+cairo_status_t
+_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
+ cairo_status_t status)
+{
+ if (status == CAIRO_STATUS_SUCCESS)
+ return status;
+
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&scaled_font->status, status);
+
+ return _cairo_error (status);
+}
+
+/**
+ * cairo_scaled_font_get_type:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * This function returns the type of the backend used to create
+ * a scaled font. See #cairo_font_type_t for available types.
+ * However, this function never returns %CAIRO_FONT_TYPE_TOY.
+ *
+ * Return value: The type of @scaled_font.
+ *
+ * Since: 1.2
+ **/
+cairo_font_type_t
+cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font)
+{
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+ return CAIRO_FONT_TYPE_TOY;
+
+ return scaled_font->backend->type;
+}
+
+/**
+ * cairo_scaled_font_status:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Checks whether an error has previously occurred for this
+ * scaled_font.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or another error such as
+ * %CAIRO_STATUS_NO_MEMORY.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
+{
+ return scaled_font->status;
+}
+slim_hidden_def (cairo_scaled_font_status);
+
+/* Here we keep a unique mapping from
+ * font_face/matrix/ctm/font_options => #cairo_scaled_font_t.
+ *
+ * Here are the things that we want to map:
+ *
+ * a) All otherwise referenced #cairo_scaled_font_t's
+ * b) Some number of not otherwise referenced #cairo_scaled_font_t's
+ *
+ * The implementation uses a hash table which covers (a)
+ * completely. Then, for (b) we have an array of otherwise
+ * unreferenced fonts (holdovers) which are expired in
+ * least-recently-used order.
+ *
+ * The cairo_scaled_font_create() code gets to treat this like a regular
+ * hash table. All of the magic for the little holdover cache is in
+ * cairo_scaled_font_reference() and cairo_scaled_font_destroy().
+ */
+
+/* This defines the size of the holdover array ... that is, the number
+ * of scaled fonts we keep around even when not otherwise referenced
+ */
+#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256
+
+typedef struct _cairo_scaled_font_map {
+ cairo_scaled_font_t *mru_scaled_font;
+ cairo_hash_table_t *hash_table;
+ cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS];
+ int num_holdovers;
+} cairo_scaled_font_map_t;
+
+static cairo_scaled_font_map_t *cairo_scaled_font_map;
+
+static int
+_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b);
+
+static cairo_scaled_font_map_t *
+_cairo_scaled_font_map_lock (void)
+{
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+
+ if (cairo_scaled_font_map == NULL) {
+ cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t));
+ if (unlikely (cairo_scaled_font_map == NULL))
+ goto CLEANUP_MUTEX_LOCK;
+
+ cairo_scaled_font_map->mru_scaled_font = NULL;
+ cairo_scaled_font_map->hash_table =
+ _cairo_hash_table_create (_cairo_scaled_font_keys_equal);
+
+ if (unlikely (cairo_scaled_font_map->hash_table == NULL))
+ goto CLEANUP_SCALED_FONT_MAP;
+
+ cairo_scaled_font_map->num_holdovers = 0;
+ }
+
+ return cairo_scaled_font_map;
+
+ CLEANUP_SCALED_FONT_MAP:
+ free (cairo_scaled_font_map);
+ cairo_scaled_font_map = NULL;
+ CLEANUP_MUTEX_LOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+}
+
+static void
+_cairo_scaled_font_map_unlock (void)
+{
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+}
+
+void
+_cairo_scaled_font_map_destroy (void)
+{
+ cairo_scaled_font_map_t *font_map;
+ cairo_scaled_font_t *scaled_font;
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+
+ font_map = cairo_scaled_font_map;
+ if (unlikely (font_map == NULL)) {
+ goto CLEANUP_MUTEX_LOCK;
+ }
+
+ scaled_font = font_map->mru_scaled_font;
+ if (scaled_font != NULL) {
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+ cairo_scaled_font_destroy (scaled_font);
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+ }
+
+ /* remove scaled_fonts starting from the end so that font_map->holdovers
+ * is always in a consistent state when we release the mutex. */
+ while (font_map->num_holdovers) {
+ scaled_font = font_map->holdovers[font_map->num_holdovers-1];
+ assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
+ _cairo_hash_table_remove (font_map->hash_table,
+ &scaled_font->hash_entry);
+
+ font_map->num_holdovers--;
+
+ /* This releases the font_map lock to avoid the possibility of a
+ * recursive deadlock when the scaled font destroy closure gets
+ * called
+ */
+ _cairo_scaled_font_fini (scaled_font);
+
+ free (scaled_font);
+ }
+
+ _cairo_hash_table_destroy (font_map->hash_table);
+
+ free (cairo_scaled_font_map);
+ cairo_scaled_font_map = NULL;
+
+ CLEANUP_MUTEX_LOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+}
+
+static void
+_cairo_scaled_glyph_page_destroy (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_page_t *page)
+{
+ unsigned int n;
+
+ assert (!scaled_font->cache_frozen);
+ assert (!scaled_font->global_cache_frozen);
+
+ for (n = 0; n < page->num_glyphs; n++) {
+ _cairo_hash_table_remove (scaled_font->glyphs,
+ &page->glyphs[n].hash_entry);
+ _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]);
+ }
+
+ cairo_list_del (&page->link);
+ free (page);
+}
+
+static void
+_cairo_scaled_glyph_page_pluck (void *closure)
+{
+ cairo_scaled_glyph_page_t *page = closure;
+ cairo_scaled_font_t *scaled_font;
+
+ assert (! cairo_list_is_empty (&page->link));
+
+ scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
+
+ CAIRO_MUTEX_LOCK (scaled_font->mutex);
+ _cairo_scaled_glyph_page_destroy (scaled_font, page);
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+}
+
+/* If a scaled font wants to unlock the font map while still being
+ * created (needed for user-fonts), we need to take extra care not
+ * ending up with multiple identical scaled fonts being created.
+ *
+ * What we do is, we create a fake identical scaled font, and mark
+ * it as placeholder, lock its mutex, and insert that in the fontmap
+ * hash table. This makes other code trying to create an identical
+ * scaled font to just wait and retry.
+ *
+ * The reason we have to create a fake scaled font instead of just using
+ * scaled_font is for lifecycle management: we need to (or rather,
+ * other code needs to) reference the scaled_font in the hash table.
+ * We can't do that on the input scaled_font as it may be freed by
+ * font backend upon error.
+ */
+
+cairo_status_t
+_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font)
+{
+ cairo_status_t status;
+ cairo_scaled_font_t *placeholder_scaled_font;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex));
+
+ status = scaled_font->status;
+ if (unlikely (status))
+ return status;
+
+ placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t));
+ if (unlikely (placeholder_scaled_font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* full initialization is wasteful, but who cares... */
+ status = _cairo_scaled_font_init (placeholder_scaled_font,
+ scaled_font->font_face,
+ &scaled_font->font_matrix,
+ &scaled_font->ctm,
+ &scaled_font->options,
+ NULL);
+ if (unlikely (status))
+ goto FREE_PLACEHOLDER;
+
+ placeholder_scaled_font->placeholder = TRUE;
+
+ placeholder_scaled_font->hash_entry.hash
+ = _cairo_scaled_font_compute_hash (placeholder_scaled_font);
+ status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table,
+ &placeholder_scaled_font->hash_entry);
+ if (unlikely (status))
+ goto FINI_PLACEHOLDER;
+
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+ CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ FINI_PLACEHOLDER:
+ _cairo_scaled_font_fini_internal (placeholder_scaled_font);
+ FREE_PLACEHOLDER:
+ free (placeholder_scaled_font);
+
+ return _cairo_scaled_font_set_error (scaled_font, status);
+}
+
+void
+_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font)
+{
+ cairo_scaled_font_t *placeholder_scaled_font;
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+
+ /* temporary hash value to match the placeholder */
+ scaled_font->hash_entry.hash
+ = _cairo_scaled_font_compute_hash (scaled_font);
+ placeholder_scaled_font =
+ _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table,
+ &scaled_font->hash_entry);
+ assert (placeholder_scaled_font != NULL);
+ assert (placeholder_scaled_font->placeholder);
+ assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex));
+
+ _cairo_hash_table_remove (cairo_scaled_font_map->hash_table,
+ &placeholder_scaled_font->hash_entry);
+
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+
+ CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
+ cairo_scaled_font_destroy (placeholder_scaled_font);
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+}
+
+static void
+_cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font)
+{
+ /* reference the place holder so it doesn't go away */
+ cairo_scaled_font_reference (placeholder_scaled_font);
+
+ /* now unlock the fontmap mutex so creation has a chance to finish */
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+
+ /* wait on placeholder mutex until we are awaken */
+ CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
+
+ /* ok, creation done. just clean up and back out */
+ CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
+ cairo_scaled_font_destroy (placeholder_scaled_font);
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+}
+
+/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
+ *
+ * Not necessarily better than a lot of other hashes, but should be OK, and
+ * well tested with binary data.
+ */
+
+#define FNV_32_PRIME ((uint32_t)0x01000193)
+#define FNV1_32_INIT ((uint32_t)0x811c9dc5)
+
+static uint32_t
+_hash_matrix_fnv (const cairo_matrix_t *matrix,
+ uint32_t hval)
+{
+ const uint8_t *buffer = (const uint8_t *) matrix;
+ int len = sizeof (cairo_matrix_t);
+ do {
+ hval *= FNV_32_PRIME;
+ hval ^= *buffer++;
+ } while (--len);
+
+ return hval;
+}
+
+static uint32_t
+_hash_mix_bits (uint32_t hash)
+{
+ hash += hash << 12;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
+ return hash;
+}
+
+static uint32_t
+_cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font)
+{
+ uint32_t hash = FNV1_32_INIT;
+
+ /* We do a bytewise hash on the font matrices */
+ hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
+ hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
+ hash = _hash_mix_bits (hash);
+
+ hash ^= (unsigned long) scaled_font->original_font_face;
+ hash ^= cairo_font_options_hash (&scaled_font->options);
+
+ /* final mixing of bits */
+ hash = _hash_mix_bits (hash);
+ assert (hash != ZOMBIE);
+
+ return hash;
+}
+
+static void
+_cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font,
+ cairo_font_face_t *font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options)
+{
+ scaled_font->status = CAIRO_STATUS_SUCCESS;
+ scaled_font->placeholder = FALSE;
+ scaled_font->font_face = font_face;
+ scaled_font->original_font_face = font_face;
+ scaled_font->font_matrix = *font_matrix;
+ scaled_font->ctm = *ctm;
+ /* ignore translation values in the ctm */
+ scaled_font->ctm.x0 = 0.;
+ scaled_font->ctm.y0 = 0.;
+ _cairo_font_options_init_copy (&scaled_font->options, options);
+
+ scaled_font->hash_entry.hash =
+ _cairo_scaled_font_compute_hash (scaled_font);
+}
+
+static cairo_bool_t
+_cairo_scaled_font_keys_equal (const void *abstract_key_a,
+ const void *abstract_key_b)
+{
+ const cairo_scaled_font_t *key_a = abstract_key_a;
+ const cairo_scaled_font_t *key_b = abstract_key_b;
+
+ return key_a->original_font_face == key_b->original_font_face &&
+ memcmp ((unsigned char *)(&key_a->font_matrix.xx),
+ (unsigned char *)(&key_b->font_matrix.xx),
+ sizeof(cairo_matrix_t)) == 0 &&
+ memcmp ((unsigned char *)(&key_a->ctm.xx),
+ (unsigned char *)(&key_b->ctm.xx),
+ sizeof(cairo_matrix_t)) == 0 &&
+ cairo_font_options_equal (&key_a->options, &key_b->options);
+}
+
+static cairo_bool_t
+_cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font,
+ const cairo_font_face_t *font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options)
+{
+ return scaled_font->original_font_face == font_face &&
+ memcmp ((unsigned char *)(&scaled_font->font_matrix.xx),
+ (unsigned char *)(&font_matrix->xx),
+ sizeof(cairo_matrix_t)) == 0 &&
+ memcmp ((unsigned char *)(&scaled_font->ctm.xx),
+ (unsigned char *)(&ctm->xx),
+ sizeof(cairo_matrix_t)) == 0 &&
+ cairo_font_options_equal (&scaled_font->options, options);
+}
+
+/*
+ * Basic #cairo_scaled_font_t object management
+ */
+
+cairo_status_t
+_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font,
+ cairo_font_face_t *font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ const cairo_scaled_font_backend_t *backend)
+{
+ cairo_status_t status;
+
+ status = cairo_font_options_status ((cairo_font_options_t *) options);
+ if (unlikely (status))
+ return status;
+
+ scaled_font->status = CAIRO_STATUS_SUCCESS;
+ scaled_font->placeholder = FALSE;
+ scaled_font->font_face = font_face;
+ scaled_font->original_font_face = font_face;
+ scaled_font->font_matrix = *font_matrix;
+ scaled_font->ctm = *ctm;
+ /* ignore translation values in the ctm */
+ scaled_font->ctm.x0 = 0.;
+ scaled_font->ctm.y0 = 0.;
+ _cairo_font_options_init_copy (&scaled_font->options, options);
+
+ cairo_matrix_multiply (&scaled_font->scale,
+ &scaled_font->font_matrix,
+ &scaled_font->ctm);
+
+ scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy),
+ fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy));
+ scaled_font->scale_inverse = scaled_font->scale;
+ status = cairo_matrix_invert (&scaled_font->scale_inverse);
+ if (unlikely (status)) {
+ /* If the font scale matrix is rank 0, just using an all-zero inverse matrix
+ * makes everything work correctly. This make font size 0 work without
+ * producing an error.
+ *
+ * FIXME: If the scale is rank 1, we still go into error mode. But then
+ * again, that's what we do everywhere in cairo.
+ *
+ * Also, the check for == 0. below may be too harsh...
+ */
+ if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) {
+ cairo_matrix_init (&scaled_font->scale_inverse,
+ 0, 0, 0, 0,
+ -scaled_font->scale.x0,
+ -scaled_font->scale.y0);
+ } else
+ return status;
+ }
+
+ scaled_font->glyphs = _cairo_hash_table_create (NULL);
+ if (unlikely (scaled_font->glyphs == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ cairo_list_init (&scaled_font->glyph_pages);
+ scaled_font->cache_frozen = FALSE;
+ scaled_font->global_cache_frozen = FALSE;
+
+ scaled_font->holdover = FALSE;
+ scaled_font->finished = FALSE;
+
+ CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1);
+
+ _cairo_user_data_array_init (&scaled_font->user_data);
+
+ cairo_font_face_reference (font_face);
+ scaled_font->original_font_face = NULL;
+
+ CAIRO_MUTEX_INIT (scaled_font->mutex);
+
+ cairo_list_init (&scaled_font->dev_privates);
+
+ scaled_font->backend = backend;
+ cairo_list_init (&scaled_font->link);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
+{
+ /* ensure we do not modify an error object */
+ assert (scaled_font->status == CAIRO_STATUS_SUCCESS);
+
+ CAIRO_MUTEX_LOCK (scaled_font->mutex);
+ scaled_font->cache_frozen = TRUE;
+}
+
+void
+_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
+{
+ assert (scaled_font->cache_frozen);
+
+ if (scaled_font->global_cache_frozen) {
+ CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ _cairo_cache_thaw (&cairo_scaled_glyph_page_cache);
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+ scaled_font->global_cache_frozen = FALSE;
+ }
+
+ scaled_font->cache_frozen = FALSE;
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+}
+
+void
+_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
+{
+ CAIRO_MUTEX_LOCK (scaled_font->mutex);
+ assert (! scaled_font->cache_frozen);
+ assert (! scaled_font->global_cache_frozen);
+ CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ while (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
+ cairo_scaled_glyph_page_t *page =
+ cairo_list_first_entry (&scaled_font->glyph_pages,
+ cairo_scaled_glyph_page_t,
+ link);
+
+ cairo_scaled_glyph_page_cache.size -= page->cache_entry.size;
+ _cairo_hash_table_remove (cairo_scaled_glyph_page_cache.hash_table,
+ (cairo_hash_entry_t *) &page->cache_entry);
+
+ _cairo_scaled_glyph_page_destroy (scaled_font, page);
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+}
+
+cairo_status_t
+_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font,
+ cairo_font_extents_t *fs_metrics)
+{
+ cairo_status_t status;
+ double font_scale_x, font_scale_y;
+
+ scaled_font->fs_extents = *fs_metrics;
+
+ status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix,
+ &font_scale_x, &font_scale_y,
+ 1);
+ if (unlikely (status))
+ return status;
+
+ /*
+ * The font responded in unscaled units, scale by the font
+ * matrix scale factors to get to user space
+ */
+
+ scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y;
+ scaled_font->extents.descent = fs_metrics->descent * font_scale_y;
+ scaled_font->extents.height = fs_metrics->height * font_scale_y;
+ scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x;
+ scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font)
+{
+ assert (! scaled_font->cache_frozen);
+ assert (! scaled_font->global_cache_frozen);
+ scaled_font->finished = TRUE;
+
+ _cairo_scaled_font_reset_cache (scaled_font);
+ _cairo_hash_table_destroy (scaled_font->glyphs);
+
+ cairo_font_face_destroy (scaled_font->font_face);
+ cairo_font_face_destroy (scaled_font->original_font_face);
+
+ CAIRO_MUTEX_FINI (scaled_font->mutex);
+
+ while (! cairo_list_is_empty (&scaled_font->dev_privates)) {
+ cairo_scaled_font_private_t *private =
+ cairo_list_first_entry (&scaled_font->dev_privates,
+ cairo_scaled_font_private_t,
+ link);
+ private->destroy (private, scaled_font);
+ }
+
+ if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL)
+ scaled_font->backend->fini (scaled_font);
+
+ _cairo_user_data_array_fini (&scaled_font->user_data);
+}
+
+void
+_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ /* Release the lock to avoid the possibility of a recursive
+ * deadlock when the scaled font destroy closure gets called. */
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+ _cairo_scaled_font_fini_internal (scaled_font);
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+}
+
+void
+_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_font_private_t *private,
+ const void *key,
+ void (*destroy) (cairo_scaled_font_private_t *,
+ cairo_scaled_font_t *))
+{
+ private->key = key;
+ private->destroy = destroy;
+ cairo_list_add (&private->link, &scaled_font->dev_privates);
+}
+
+cairo_scaled_font_private_t *
+_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font,
+ const void *key)
+{
+ cairo_scaled_font_private_t *priv;
+
+ cairo_list_foreach_entry (priv, cairo_scaled_font_private_t,
+ &scaled_font->dev_privates, link)
+ {
+ if (priv->key == key) {
+ if (priv->link.prev != &scaled_font->dev_privates)
+ cairo_list_move (&priv->link, &scaled_font->dev_privates);
+ return priv;
+ }
+ }
+
+ return NULL;
+}
+
+void
+_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_glyph_private_t *private,
+ const void *key,
+ void (*destroy) (cairo_scaled_glyph_private_t *,
+ cairo_scaled_glyph_t *,
+ cairo_scaled_font_t *))
+{
+ private->key = key;
+ private->destroy = destroy;
+ cairo_list_add (&private->link, &scaled_glyph->dev_privates);
+}
+
+cairo_scaled_glyph_private_t *
+_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph,
+ const void *key)
+{
+ cairo_scaled_glyph_private_t *priv;
+
+ cairo_list_foreach_entry (priv, cairo_scaled_glyph_private_t,
+ &scaled_glyph->dev_privates, link)
+ {
+ if (priv->key == key) {
+ if (priv->link.prev != &scaled_glyph->dev_privates)
+ cairo_list_move (&priv->link, &scaled_glyph->dev_privates);
+ return priv;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * cairo_scaled_font_create:
+ * @font_face: a #cairo_font_face_t
+ * @font_matrix: font space to user space transformation matrix for the
+ * font. In the simplest case of a N point font, this matrix is
+ * just a scale by N, but it can also be used to shear the font
+ * or stretch it unequally along the two axes. See
+ * cairo_set_font_matrix().
+ * @ctm: user to device transformation matrix with which the font will
+ * be used.
+ * @options: options to use when getting metrics for the font and
+ * rendering with it.
+ *
+ * Creates a #cairo_scaled_font_t object from a font face and matrices that
+ * describe the size of the font and the environment in which it will
+ * be used.
+ *
+ * Return value: a newly created #cairo_scaled_font_t. Destroy with
+ * cairo_scaled_font_destroy()
+ *
+ * Since: 1.0
+ **/
+cairo_scaled_font_t *
+cairo_scaled_font_create (cairo_font_face_t *font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options)
+{
+ cairo_status_t status;
+ cairo_scaled_font_map_t *font_map;
+ cairo_font_face_t *original_font_face = font_face;
+ cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL;
+ double det;
+
+ status = font_face->status;
+ if (unlikely (status))
+ return _cairo_scaled_font_create_in_error (status);
+
+ det = _cairo_matrix_compute_determinant (font_matrix);
+ if (! ISFINITE (det))
+ return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
+
+ det = _cairo_matrix_compute_determinant (ctm);
+ if (! ISFINITE (det))
+ return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
+
+ status = cairo_font_options_status ((cairo_font_options_t *) options);
+ if (unlikely (status))
+ return _cairo_scaled_font_create_in_error (status);
+
+ /* Note that degenerate ctm or font_matrix *are* allowed.
+ * We want to support a font size of 0. */
+
+ font_map = _cairo_scaled_font_map_lock ();
+ if (unlikely (font_map == NULL))
+ return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ scaled_font = font_map->mru_scaled_font;
+ if (scaled_font != NULL &&
+ _cairo_scaled_font_matches (scaled_font,
+ font_face, font_matrix, ctm, options))
+ {
+ assert (scaled_font->hash_entry.hash != ZOMBIE);
+ assert (! scaled_font->placeholder);
+
+ if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
+ /* We increment the reference count manually here, (rather
+ * than calling into cairo_scaled_font_reference), since we
+ * must modify the reference count while our lock is still
+ * held. */
+ _cairo_reference_count_inc (&scaled_font->ref_count);
+ _cairo_scaled_font_map_unlock ();
+ return scaled_font;
+ }
+
+ /* the font has been put into an error status - abandon the cache */
+ _cairo_hash_table_remove (font_map->hash_table,
+ &scaled_font->hash_entry);
+ scaled_font->hash_entry.hash = ZOMBIE;
+ dead = scaled_font;
+ font_map->mru_scaled_font = NULL;
+ }
+
+ _cairo_scaled_font_init_key (&key, font_face, font_matrix, ctm, options);
+
+ while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table,
+ &key.hash_entry)))
+ {
+ if (! scaled_font->placeholder)
+ break;
+
+ /* If the scaled font is being created (happens for user-font),
+ * just wait until it's done, then retry */
+ _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font);
+ }
+
+ if (scaled_font != NULL) {
+ /* If the original reference count is 0, then this font must have
+ * been found in font_map->holdovers, (which means this caching is
+ * actually working). So now we remove it from the holdovers
+ * array, unless we caught the font in the middle of destruction.
+ */
+ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
+ if (scaled_font->holdover) {
+ int i;
+
+ for (i = 0; i < font_map->num_holdovers; i++) {
+ if (font_map->holdovers[i] == scaled_font) {
+ font_map->num_holdovers--;
+ memmove (&font_map->holdovers[i],
+ &font_map->holdovers[i+1],
+ (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
+ break;
+ }
+ }
+
+ scaled_font->holdover = FALSE;
+ }
+
+ /* reset any error status */
+ scaled_font->status = CAIRO_STATUS_SUCCESS;
+ }
+
+ if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
+ /* We increment the reference count manually here, (rather
+ * than calling into cairo_scaled_font_reference), since we
+ * must modify the reference count while our lock is still
+ * held. */
+
+ old = font_map->mru_scaled_font;
+ font_map->mru_scaled_font = scaled_font;
+ /* increment reference count for the mru cache */
+ _cairo_reference_count_inc (&scaled_font->ref_count);
+ /* and increment for the returned reference */
+ _cairo_reference_count_inc (&scaled_font->ref_count);
+ _cairo_scaled_font_map_unlock ();
+
+ cairo_scaled_font_destroy (old);
+ if (font_face != original_font_face)
+ cairo_font_face_destroy (font_face);
+
+ return scaled_font;
+ }
+
+ /* the font has been put into an error status - abandon the cache */
+ _cairo_hash_table_remove (font_map->hash_table,
+ &scaled_font->hash_entry);
+ scaled_font->hash_entry.hash = ZOMBIE;
+ }
+
+
+ /* Otherwise create it and insert it into the hash table. */
+ if (font_face->backend->get_implementation != NULL) {
+ font_face = font_face->backend->get_implementation (font_face,
+ font_matrix,
+ ctm,
+ options);
+ if (unlikely (font_face->status)) {
+ _cairo_scaled_font_map_unlock ();
+ return _cairo_scaled_font_create_in_error (font_face->status);
+ }
+ }
+
+ status = font_face->backend->scaled_font_create (font_face, font_matrix,
+ ctm, options, &scaled_font);
+ /* Did we leave the backend in an error state? */
+ if (unlikely (status)) {
+ _cairo_scaled_font_map_unlock ();
+ if (font_face != original_font_face)
+ cairo_font_face_destroy (font_face);
+
+ if (dead != NULL)
+ cairo_scaled_font_destroy (dead);
+
+ status = _cairo_font_face_set_error (font_face, status);
+ return _cairo_scaled_font_create_in_error (status);
+ }
+ /* Or did we encounter an error whilst constructing the scaled font? */
+ if (unlikely (scaled_font->status)) {
+ _cairo_scaled_font_map_unlock ();
+ if (font_face != original_font_face)
+ cairo_font_face_destroy (font_face);
+
+ if (dead != NULL)
+ cairo_scaled_font_destroy (dead);
+
+ return scaled_font;
+ }
+
+ /* Our caching above is defeated if the backend switches fonts on us -
+ * e.g. old incarnations of toy-font-face and lazily resolved
+ * ft-font-faces
+ */
+ assert (scaled_font->font_face == font_face);
+ assert (! scaled_font->cache_frozen);
+ assert (! scaled_font->global_cache_frozen);
+
+ scaled_font->original_font_face =
+ cairo_font_face_reference (original_font_face);
+
+ scaled_font->hash_entry.hash = _cairo_scaled_font_compute_hash(scaled_font);
+
+ status = _cairo_hash_table_insert (font_map->hash_table,
+ &scaled_font->hash_entry);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ old = font_map->mru_scaled_font;
+ font_map->mru_scaled_font = scaled_font;
+ _cairo_reference_count_inc (&scaled_font->ref_count);
+ }
+
+ _cairo_scaled_font_map_unlock ();
+
+ cairo_scaled_font_destroy (old);
+ if (font_face != original_font_face)
+ cairo_font_face_destroy (font_face);
+
+ if (dead != NULL)
+ cairo_scaled_font_destroy (dead);
+
+ if (unlikely (status)) {
+ /* We can't call _cairo_scaled_font_destroy here since it expects
+ * that the font has already been successfully inserted into the
+ * hash table. */
+ _cairo_scaled_font_fini_internal (scaled_font);
+ free (scaled_font);
+ return _cairo_scaled_font_create_in_error (status);
+ }
+
+ return scaled_font;
+}
+slim_hidden_def (cairo_scaled_font_create);
+
+static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1];
+
+/* XXX This should disappear in favour of a common pool of error objects. */
+cairo_scaled_font_t *
+_cairo_scaled_font_create_in_error (cairo_status_t status)
+{
+ cairo_scaled_font_t *scaled_font;
+
+ assert (status != CAIRO_STATUS_SUCCESS);
+
+ if (status == CAIRO_STATUS_NO_MEMORY)
+ return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
+ scaled_font = _cairo_scaled_font_nil_objects[status];
+ if (unlikely (scaled_font == NULL)) {
+ scaled_font = malloc (sizeof (cairo_scaled_font_t));
+ if (unlikely (scaled_font == NULL)) {
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
+ }
+
+ *scaled_font = _cairo_scaled_font_nil;
+ scaled_font->status = status;
+ _cairo_scaled_font_nil_objects[status] = scaled_font;
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
+
+ return scaled_font;
+}
+
+void
+_cairo_scaled_font_reset_static_data (void)
+{
+ int status;
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
+ for (status = CAIRO_STATUS_SUCCESS;
+ status <= CAIRO_STATUS_LAST_STATUS;
+ status++)
+ {
+ free (_cairo_scaled_font_nil_objects[status]);
+ _cairo_scaled_font_nil_objects[status] = NULL;
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ if (cairo_scaled_glyph_page_cache.hash_table != NULL) {
+ _cairo_cache_fini (&cairo_scaled_glyph_page_cache);
+ cairo_scaled_glyph_page_cache.hash_table = NULL;
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+}
+
+/**
+ * cairo_scaled_font_reference:
+ * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case
+ * this function does nothing)
+ *
+ * Increases the reference count on @scaled_font by one. This prevents
+ * @scaled_font from being destroyed until a matching call to
+ * cairo_scaled_font_destroy() is made.
+ *
+ * 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
+ *
+ * Since: 1.0
+ **/
+cairo_scaled_font_t *
+cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
+{
+ if (scaled_font == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+ return scaled_font;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
+
+ _cairo_reference_count_inc (&scaled_font->ref_count);
+
+ return scaled_font;
+}
+slim_hidden_def (cairo_scaled_font_reference);
+
+/**
+ * cairo_scaled_font_destroy:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Decreases the reference count on @font by one. If the result
+ * is zero, then @font and all associated resources are freed.
+ * See cairo_scaled_font_reference().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
+{
+ cairo_scaled_font_t *lru = NULL;
+ cairo_scaled_font_map_t *font_map;
+
+ assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex));
+
+ if (scaled_font == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+ return;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count))
+ return;
+
+ assert (! scaled_font->cache_frozen);
+ assert (! scaled_font->global_cache_frozen);
+
+ 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.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font,
+ cairo_font_extents_t *extents)
+{
+ if (scaled_font->status) {
+ extents->ascent = 0.0;
+ extents->descent = 0.0;
+ extents->height = 0.0;
+ extents->max_x_advance = 0.0;
+ extents->max_y_advance = 0.0;
+ return;
+ }
+
+ *extents = scaled_font->extents;
+}
+slim_hidden_def (cairo_scaled_font_extents);
+
+/**
+ * cairo_scaled_font_text_extents:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @utf8: a NUL-terminated string of text, encoded in UTF-8
+ * @extents: a #cairo_text_extents_t which to store the retrieved extents.
+ *
+ * Gets the extents for a string of text. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the text
+ * drawn at the origin (0,0) (as it would be drawn by cairo_show_text()
+ * if the cairo graphics state were set to the same font_face,
+ * font_matrix, ctm, and font_options as @scaled_font). Additionally,
+ * the x_advance and y_advance values indicate the amount by which the
+ * current point would be advanced by cairo_show_text().
+ *
+ * Note that whitespace characters do not directly contribute to the
+ * size of the rectangle (extents.width and extents.height). They do
+ * contribute indirectly by changing the position of non-whitespace
+ * characters. In particular, trailing whitespace characters are
+ * likely to not affect the size of the rectangle, though they will
+ * affect the x_advance and y_advance values.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font,
+ const char *utf8,
+ cairo_text_extents_t *extents)
+{
+ cairo_status_t status;
+ cairo_glyph_t *glyphs = NULL;
+ int num_glyphs;
+
+ if (scaled_font->status)
+ goto ZERO_EXTENTS;
+
+ if (utf8 == NULL)
+ goto ZERO_EXTENTS;
+
+ status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
+ utf8, -1,
+ &glyphs, &num_glyphs,
+ NULL, NULL,
+ NULL);
+ if (unlikely (status)) {
+ status = _cairo_scaled_font_set_error (scaled_font, status);
+ goto ZERO_EXTENTS;
+ }
+
+ cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents);
+ free (glyphs);
+
+ return;
+
+ZERO_EXTENTS:
+ extents->x_bearing = 0.0;
+ extents->y_bearing = 0.0;
+ extents->width = 0.0;
+ extents->height = 0.0;
+ extents->x_advance = 0.0;
+ extents->y_advance = 0.0;
+}
+
+/**
+ * cairo_scaled_font_glyph_extents:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @glyphs: an array of glyph IDs with X and Y offsets.
+ * @num_glyphs: the number of glyphs in the @glyphs array
+ * @extents: a #cairo_text_extents_t which to store the retrieved extents.
+ *
+ * Gets the extents for an array of glyphs. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the
+ * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo
+ * graphics state were set to the same font_face, font_matrix, ctm,
+ * and font_options as @scaled_font). Additionally, the x_advance and
+ * y_advance values indicate the amount by which the current point
+ * would be advanced by cairo_show_glyphs().
+ *
+ * Note that whitespace glyphs do not contribute to the size of the
+ * rectangle (extents.width and extents.height).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents)
+{
+ cairo_status_t status;
+ int i;
+ double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
+ cairo_bool_t visible = FALSE;
+ cairo_scaled_glyph_t *scaled_glyph = NULL;
+
+ extents->x_bearing = 0.0;
+ extents->y_bearing = 0.0;
+ extents->width = 0.0;
+ extents->height = 0.0;
+ extents->x_advance = 0.0;
+ extents->y_advance = 0.0;
+
+ if (unlikely (scaled_font->status))
+ goto ZERO_EXTENTS;
+
+ if (num_glyphs == 0)
+ goto ZERO_EXTENTS;
+
+ if (unlikely (num_glyphs < 0)) {
+ _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT);
+ /* XXX Can't propagate error */
+ goto ZERO_EXTENTS;
+ }
+
+ if (unlikely (glyphs == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NULL_POINTER);
+ /* XXX Can't propagate error */
+ goto ZERO_EXTENTS;
+ }
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ for (i = 0; i < num_glyphs; i++) {
+ double left, top, right, bottom;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status)) {
+ status = _cairo_scaled_font_set_error (scaled_font, status);
+ goto UNLOCK;
+ }
+
+ /* "Ink" extents should skip "invisible" glyphs */
+ if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)
+ continue;
+
+ left = scaled_glyph->metrics.x_bearing + glyphs[i].x;
+ right = left + scaled_glyph->metrics.width;
+ top = scaled_glyph->metrics.y_bearing + glyphs[i].y;
+ bottom = top + scaled_glyph->metrics.height;
+
+ if (!visible) {
+ visible = TRUE;
+ min_x = left;
+ max_x = right;
+ min_y = top;
+ max_y = bottom;
+ } else {
+ if (left < min_x) min_x = left;
+ if (right > max_x) max_x = right;
+ if (top < min_y) min_y = top;
+ if (bottom > max_y) max_y = bottom;
+ }
+ }
+
+ if (visible) {
+ extents->x_bearing = min_x - glyphs[0].x;
+ extents->y_bearing = min_y - glyphs[0].y;
+ extents->width = max_x - min_x;
+ extents->height = max_y - min_y;
+ } else {
+ extents->x_bearing = 0.0;
+ extents->y_bearing = 0.0;
+ extents->width = 0.0;
+ extents->height = 0.0;
+ }
+
+ 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;
+
+ 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_int_status_t status;
+ cairo_glyph_t *orig_glyphs;
+ cairo_text_cluster_t *orig_clusters;
+
+ status = scaled_font->status;
+ if (unlikely (status))
+ return status;
+
+ /* A slew of sanity checks */
+
+ /* glyphs and num_glyphs can't be NULL */
+ if (glyphs == NULL ||
+ num_glyphs == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ goto BAIL;
+ }
+
+ /* Special case for NULL and -1 */
+ if (utf8 == NULL && utf8_len == -1)
+ utf8_len = 0;
+
+ /* No NULLs for non-NULLs! */
+ if ((utf8_len && utf8 == NULL) ||
+ (clusters && num_clusters == NULL) ||
+ (clusters && cluster_flags == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ goto BAIL;
+ }
+
+ /* A -1 for utf8_len means NUL-terminated */
+ if (utf8_len == -1)
+ utf8_len = strlen (utf8);
+
+ /* A NULL *glyphs means no prealloced glyphs array */
+ if (glyphs && *glyphs == NULL)
+ *num_glyphs = 0;
+
+ /* A NULL *clusters means no prealloced clusters array */
+ if (clusters && *clusters == NULL)
+ *num_clusters = 0;
+
+ if (!clusters && num_clusters) {
+ num_clusters = NULL;
+ }
+
+ if (cluster_flags) {
+ *cluster_flags = FALSE;
+ }
+
+ if (!clusters && cluster_flags) {
+ cluster_flags = NULL;
+ }
+
+ /* Apart from that, no negatives */
+ if (utf8_len < 0 ||
+ *num_glyphs < 0 ||
+ (num_clusters && *num_clusters < 0)) {
+ status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
+ goto BAIL;
+ }
+
+ if (utf8_len == 0) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto BAIL;
+ }
+
+ /* validate input so backend does not have to */
+ status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ orig_glyphs = *glyphs;
+ orig_clusters = clusters ? *clusters : NULL;
+
+ if (scaled_font->backend->text_to_glyphs) {
+ status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ /* The checks here are crude; we only should do them in
+ * user-font backend, but they don't hurt here. This stuff
+ * can be hard to get right. */
+
+ if (*num_glyphs < 0) {
+ status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
+ goto DONE;
+ }
+ if (num_glyphs && *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);
+
+/* XXX: Shoot me - cairo has two basic ways of rendring text in API.
+ * The first way is cairo_show_text() and the second way is
+ * cairo_show_glyphs().
+ *
+ * For the first method, each glyph location is advanced by x_advance
+ * and y_advance computed from glyph metrics. For the second method,
+ * app is responsible for supplying the glyph location. This gives us
+ * opportunity to check whether the computed x_advance and _y_advance
+ * is equala/larger than supplied glyphs difference for each adjacent
+ * glyphs. The rational is that if the spacing between glyphs is at
+ * least larger than computed x_advance/y_advance, there should be no
+ * overlapping in text portion of the text images.
+ */
+static inline cairo_bool_t
+_glyph_is_next_to_glyph (cairo_glyph_t *prev,
+ cairo_glyph_t *current,
+ double x_advance,
+ double y_advance)
+{
+ return current->x - prev->x >= x_advance - TOLERANCE &&
+ current->y - prev->y >= y_advance - TOLERANCE;
+}
+
+static inline cairo_bool_t
+_range_contains_glyph (const cairo_box_t *extents,
+ cairo_fixed_t left,
+ cairo_fixed_t top,
+ cairo_fixed_t right,
+ cairo_fixed_t bottom)
+{
+ if (left == right || top == bottom)
+ return FALSE;
+
+ return right > extents->p1.x &&
+ left < extents->p2.x &&
+ bottom > extents->p1.y &&
+ top < extents->p2.y;
+}
+
+static cairo_status_t
+_cairo_scaled_font_single_glyph_device_extents (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyph,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_status_t status;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyph->index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ cairo_bool_t round_xy = _cairo_font_options_get_round_glyph_positions (&scaled_font->options) == CAIRO_ROUND_GLYPH_POS_ON;
+ cairo_box_t box;
+ cairo_fixed_t v;
+
+ if (round_xy)
+ v = _cairo_fixed_from_int (_cairo_lround (glyph->x));
+ else
+ v = _cairo_fixed_from_double (glyph->x);
+ box.p1.x = v + scaled_glyph->bbox.p1.x;
+ box.p2.x = v + scaled_glyph->bbox.p2.x;
+
+ if (round_xy)
+ v = _cairo_fixed_from_int (_cairo_lround (glyph->y));
+ else
+ v = _cairo_fixed_from_double (glyph->y);
+ box.p1.y = v + scaled_glyph->bbox.p1.y;
+ box.p2.y = v + scaled_glyph->bbox.p2.y;
+
+ _cairo_box_round_to_rectangle (&box, extents);
+ }
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return status;
+}
+
+/*
+ * Compute a device-space bounding box for the glyphs.
+ */
+cairo_status_t
+_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_rectangle_int_t *extents,
+ cairo_bool_t *overlap_out)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }};
+ cairo_scaled_glyph_t *glyph_cache[64];
+ cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
+ cairo_scaled_glyph_t *prev_scaled_glyph = NULL;
+ cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options);
+ int i;
+ cairo_bool_t is_next = FALSE;
+
+ if (unlikely (scaled_font->status))
+ return scaled_font->status;
+
+ if (num_glyphs == 1) {
+ if (overlap_out)
+ *overlap_out = FALSE;
+ return _cairo_scaled_font_single_glyph_device_extents (scaled_font,
+ glyphs,
+ extents);
+ }
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+
+ for (i = 0; i < num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_fixed_t x, y, x1, y1, x2, y2;
+ int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
+ {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status))
+ break;
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
+ x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x));
+ else
+ x = _cairo_fixed_from_double (glyphs[i].x);
+ x1 = x + scaled_glyph->bbox.p1.x;
+ x2 = x + scaled_glyph->bbox.p2.x;
+
+ if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
+ y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y));
+ else
+ y = _cairo_fixed_from_double (glyphs[i].y);
+ y1 = y + scaled_glyph->bbox.p1.y;
+ y2 = y + scaled_glyph->bbox.p2.y;
+
+ if (prev_scaled_glyph != NULL && overlap == FALSE) {
+ is_next = _glyph_is_next_to_glyph ((cairo_glyph_t *)&glyphs[i-1],
+ (cairo_glyph_t *)&glyphs[i],
+ prev_scaled_glyph->metrics.x_advance,
+ prev_scaled_glyph->metrics.y_advance);
+
+ if (is_next == 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;
+
+ prev_scaled_glyph = scaled_glyph;
+ }
+
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ if (unlikely (status))
+ return _cairo_scaled_font_set_error (scaled_font, status);
+
+ if (box.p1.x < box.p2.x) {
+ _cairo_box_round_to_rectangle (&box, extents);
+ } else {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ }
+
+ if (overlap_out != NULL)
+ *overlap_out = overlap;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_rectangle_int_t *extents)
+{
+ double x0, x1, y0, y1, pad;
+ int i;
+
+ /* If any of the factors are suspect (i.e. the font is broken), bail */
+ if (scaled_font->fs_extents.max_x_advance == 0 ||
+ scaled_font->fs_extents.height == 0 ||
+ scaled_font->max_scale == 0)
+ {
+ return FALSE;
+ }
+
+ assert (num_glyphs);
+
+ x0 = x1 = glyphs[0].x;
+ y0 = y1 = glyphs[0].y;
+ for (i = 1; i < num_glyphs; i++) {
+ double g;
+
+ g = glyphs[i].x;
+ if (g < x0) x0 = g;
+ if (g > x1) x1 = g;
+
+ g = glyphs[i].y;
+ if (g < y0) y0 = g;
+ if (g > y1) y1 = g;
+ }
+
+ pad = MAX(scaled_font->fs_extents.max_x_advance,
+ scaled_font->fs_extents.height);
+ pad *= scaled_font->max_scale;
+
+ extents->x = floor (x0 - pad);
+ extents->width = ceil (x1 + pad) - extents->x;
+ extents->y = floor (y0 - pad);
+ extents->height = ceil (y1 + pad) - extents->y;
+ return TRUE;
+}
+
+#if 0
+/* XXX win32 */
+cairo_status_t
+_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region)
+{
+ cairo_int_status_t status;
+ cairo_surface_t *mask = NULL;
+ cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */
+ cairo_surface_pattern_t mask_pattern;
+ int i;
+
+ /* These operators aren't interpreted the same way by the backends;
+ * they are implemented in terms of other operators in cairo-gstate.c
+ */
+ assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
+
+ if (scaled_font->status)
+ return scaled_font->status;
+
+ if (!num_glyphs)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (scaled_font->backend->show_glyphs != NULL) {
+ int remaining_glyphs = num_glyphs;
+ status = scaled_font->backend->show_glyphs (scaled_font,
+ op, pattern,
+ surface,
+ source_x, source_y,
+ dest_x, dest_y,
+ width, height,
+ glyphs, num_glyphs,
+ clip_region,
+ &remaining_glyphs);
+ glyphs += num_glyphs - remaining_glyphs;
+ num_glyphs = remaining_glyphs;
+ if (remaining_glyphs == 0)
+ status = CAIRO_INT_STATUS_SUCCESS;
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return _cairo_scaled_font_set_error (scaled_font, status);
+ }
+
+ /* Font display routine either does not exist or failed. */
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ for (i = 0; i < num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ goto CLEANUP_MASK;
+
+ glyph_surface = scaled_glyph->surface;
+
+ /* To start, create the mask using the format from the first
+ * glyph. Later we'll deal with different formats. */
+ if (mask == NULL) {
+ mask_format = glyph_surface->format;
+ mask = cairo_image_surface_create (mask_format, width, height);
+ status = mask->status;
+ if (unlikely (status))
+ goto CLEANUP_MASK;
+ }
+
+ /* If we have glyphs of different formats, we "upgrade" the mask
+ * to the wider of the formats. */
+ if (glyph_surface->format != mask_format &&
+ _cairo_format_bits_per_pixel (mask_format) <
+ _cairo_format_bits_per_pixel (glyph_surface->format) )
+ {
+ cairo_surface_t *new_mask;
+
+ switch (glyph_surface->format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_A8:
+ case CAIRO_FORMAT_A1:
+ mask_format = glyph_surface->format;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ mask_format = CAIRO_FORMAT_ARGB32;
+ break;
+ }
+
+ new_mask = cairo_image_surface_create (mask_format, width, height);
+ status = new_mask->status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (new_mask);
+ goto CLEANUP_MASK;
+ }
+
+ _cairo_pattern_init_for_surface (&mask_pattern, mask);
+ /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is
+ * never any component alpha here.
+ */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ &mask_pattern.base,
+ new_mask,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height,
+ NULL);
+
+ _cairo_pattern_fini (&mask_pattern.base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (new_mask);
+ goto CLEANUP_MASK;
+ }
+
+ cairo_surface_destroy (mask);
+ mask = new_mask;
+ }
+
+ if (glyph_surface->width && glyph_surface->height) {
+ cairo_surface_pattern_t glyph_pattern;
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+
+ _cairo_pattern_init_for_surface (&glyph_pattern,
+ &glyph_surface->base);
+ if (mask_format == CAIRO_FORMAT_ARGB32)
+ glyph_pattern.base.has_component_alpha = TRUE;
+
+ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ &glyph_pattern.base,
+ mask,
+ 0, 0,
+ 0, 0,
+ x - dest_x, y - dest_y,
+ glyph_surface->width,
+ glyph_surface->height,
+ NULL);
+
+ _cairo_pattern_fini (&glyph_pattern.base);
+
+ if (unlikely (status))
+ goto CLEANUP_MASK;
+ }
+ }
+
+ _cairo_pattern_init_for_surface (&mask_pattern, mask);
+ if (mask_format == CAIRO_FORMAT_ARGB32)
+ mask_pattern.base.has_component_alpha = TRUE;
+
+ status = _cairo_surface_composite (op, pattern, &mask_pattern.base,
+ surface,
+ source_x, source_y,
+ 0, 0,
+ dest_x, dest_y,
+ width, height,
+ clip_region);
+
+ _cairo_pattern_fini (&mask_pattern.base);
+
+CLEANUP_MASK:
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ if (mask != NULL)
+ cairo_surface_destroy (mask);
+ return _cairo_scaled_font_set_error (scaled_font, status);
+}
+#endif
+
+/* Add a single-device-unit rectangle to a path. */
+static cairo_status_t
+_add_unit_rectangle_to_path (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y)
+{
+ cairo_status_t status;
+
+ status = _cairo_path_fixed_move_to (path, x, y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_rel_line_to (path,
+ _cairo_fixed_from_int (1),
+ _cairo_fixed_from_int (0));
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_rel_line_to (path,
+ _cairo_fixed_from_int (0),
+ _cairo_fixed_from_int (1));
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_rel_line_to (path,
+ _cairo_fixed_from_int (-1),
+ _cairo_fixed_from_int (0));
+ if (unlikely (status))
+ return status;
+
+ return _cairo_path_fixed_close_path (path);
+}
+
+/**
+ * _trace_mask_to_path:
+ * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8)
+ * @path: An initialized path to hold the result
+ *
+ * Given a mask surface, (an alpha image), fill out the provided path
+ * so that when filled it would result in something that approximates
+ * the mask.
+ *
+ * Note: The current tracing code here is extremely primitive. It
+ * operates only on an A1 surface, (converting an A8 surface to A1 if
+ * necessary), and performs the tracing by drawing a little square
+ * around each pixel that is on in the mask. We do not pretend that
+ * this is a high-quality result. But we are leaving it up to someone
+ * who cares enough about getting a better result to implement
+ * something more sophisticated.
+ **/
+static cairo_status_t
+_trace_mask_to_path (cairo_image_surface_t *mask,
+ cairo_path_fixed_t *path,
+ double tx, double ty)
+{
+ const uint8_t *row;
+ int rows, cols, bytes_per_row;
+ int x, y, bit;
+ double xoff, yoff;
+ cairo_fixed_t x0, y0;
+ cairo_fixed_t px, py;
+ cairo_status_t status;
+
+ mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1);
+ status = mask->base.status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (&mask->base);
+ return status;
+ }
+
+ cairo_surface_get_device_offset (&mask->base, &xoff, &yoff);
+ x0 = _cairo_fixed_from_double (tx - xoff);
+ y0 = _cairo_fixed_from_double (ty - yoff);
+
+ bytes_per_row = (mask->width + 7) / 8;
+ row = mask->data;
+ for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) {
+ const uint8_t *byte_ptr = row;
+ x = 0;
+ py = _cairo_fixed_from_int (y);
+ for (cols = bytes_per_row; cols--; ) {
+ uint8_t byte = *byte_ptr++;
+ if (byte == 0) {
+ x += 8;
+ continue;
+ }
+
+ byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte);
+ for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) {
+ if (byte & bit) {
+ px = _cairo_fixed_from_int (x);
+ status = _add_unit_rectangle_to_path (path,
+ px + x0,
+ py + y0);
+ if (unlikely (status))
+ goto BAIL;
+ }
+ }
+ }
+ }
+
+BAIL:
+ cairo_surface_destroy (&mask->base);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_path_fixed_t *path)
+{
+ cairo_int_status_t status;
+ int i;
+
+ status = scaled_font->status;
+ if (unlikely (status))
+ return status;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ for (i = 0; i < num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_PATH,
+ &scaled_glyph);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ status = _cairo_path_fixed_append (path,
+ scaled_glyph->path,
+ _cairo_fixed_from_double (glyphs[i].x),
+ _cairo_fixed_from_double (glyphs[i].y));
+
+ } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ /* If the font is incapable of providing a path, then we'll
+ * have to trace our own from a surface.
+ */
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _trace_mask_to_path (scaled_glyph->surface, path,
+ glyphs[i].x, glyphs[i].y);
+ }
+
+ if (unlikely (status))
+ goto BAIL;
+ }
+ BAIL:
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ return _cairo_scaled_font_set_error (scaled_font, status);
+}
+
+/**
+ * _cairo_scaled_glyph_set_metrics:
+ * @scaled_glyph: a #cairo_scaled_glyph_t
+ * @scaled_font: a #cairo_scaled_font_t
+ * @fs_metrics: a #cairo_text_extents_t in font space
+ *
+ * _cairo_scaled_glyph_set_metrics() stores user space metrics
+ * for the specified glyph given font space metrics. It is
+ * called by the font backend when initializing a glyph with
+ * %CAIRO_SCALED_GLYPH_INFO_METRICS.
+ **/
+void
+_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_text_extents_t *fs_metrics)
+{
+ cairo_bool_t first = TRUE;
+ double hm, wm;
+ double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0;
+ double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0;
+ double device_x_advance, device_y_advance;
+
+ scaled_glyph->fs_metrics = *fs_metrics;
+
+ for (hm = 0.0; hm <= 1.0; hm += 1.0)
+ for (wm = 0.0; wm <= 1.0; wm += 1.0) {
+ double x, y;
+
+ /* Transform this corner to user space */
+ x = fs_metrics->x_bearing + fs_metrics->width * wm;
+ y = fs_metrics->y_bearing + fs_metrics->height * hm;
+ cairo_matrix_transform_point (&scaled_font->font_matrix,
+ &x, &y);
+ if (first) {
+ min_user_x = max_user_x = x;
+ min_user_y = max_user_y = y;
+ } else {
+ if (x < min_user_x) min_user_x = x;
+ if (x > max_user_x) max_user_x = x;
+ if (y < min_user_y) min_user_y = y;
+ if (y > max_user_y) max_user_y = y;
+ }
+
+ /* Transform this corner to device space from glyph origin */
+ x = fs_metrics->x_bearing + fs_metrics->width * wm;
+ y = fs_metrics->y_bearing + fs_metrics->height * hm;
+ cairo_matrix_transform_distance (&scaled_font->scale,
+ &x, &y);
+
+ if (first) {
+ min_device_x = max_device_x = x;
+ min_device_y = max_device_y = y;
+ } else {
+ if (x < min_device_x) min_device_x = x;
+ if (x > max_device_x) max_device_x = x;
+ if (y < min_device_y) min_device_y = y;
+ if (y > max_device_y) max_device_y = y;
+ }
+ first = FALSE;
+ }
+ scaled_glyph->metrics.x_bearing = min_user_x;
+ scaled_glyph->metrics.y_bearing = min_user_y;
+ scaled_glyph->metrics.width = max_user_x - min_user_x;
+ scaled_glyph->metrics.height = max_user_y - min_user_y;
+
+ scaled_glyph->metrics.x_advance = fs_metrics->x_advance;
+ scaled_glyph->metrics.y_advance = fs_metrics->y_advance;
+ cairo_matrix_transform_distance (&scaled_font->font_matrix,
+ &scaled_glyph->metrics.x_advance,
+ &scaled_glyph->metrics.y_advance);
+
+ device_x_advance = fs_metrics->x_advance;
+ device_y_advance = fs_metrics->y_advance;
+ cairo_matrix_transform_distance (&scaled_font->scale,
+ &device_x_advance,
+ &device_y_advance);
+
+ scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x);
+ scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y);
+ scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x);
+ scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y);
+
+ scaled_glyph->x_advance = _cairo_lround (device_x_advance);
+ scaled_glyph->y_advance = _cairo_lround (device_y_advance);
+
+ scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS;
+}
+
+void
+_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_image_surface_t *surface)
+{
+ if (scaled_glyph->surface != NULL)
+ cairo_surface_destroy (&scaled_glyph->surface->base);
+
+ /* sanity check the backend glyph contents */
+ _cairo_debug_check_image_surface_is_defined (&surface->base);
+ scaled_glyph->surface = surface;
+
+ if (surface != NULL)
+ scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE;
+ else
+ scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE;
+}
+
+void
+_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_path_fixed_t *path)
+{
+ if (scaled_glyph->path != NULL)
+ _cairo_path_fixed_destroy (scaled_glyph->path);
+
+ scaled_glyph->path = path;
+
+ if (path != NULL)
+ scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH;
+ else
+ scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH;
+}
+
+void
+_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_surface_t *recording_surface)
+{
+ if (scaled_glyph->recording_surface != NULL) {
+ cairo_surface_finish (scaled_glyph->recording_surface);
+ cairo_surface_destroy (scaled_glyph->recording_surface);
+ }
+
+ scaled_glyph->recording_surface = recording_surface;
+
+ if (recording_surface != NULL)
+ scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
+ else
+ scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
+}
+
+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;
+
+ assert (scaled_font->cache_frozen);
+
+ /* only the first page in the list may contain available slots */
+ if (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
+ page = cairo_list_last_entry (&scaled_font->glyph_pages,
+ cairo_scaled_glyph_page_t,
+ link);
+ if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) {
+ *scaled_glyph = &page->glyphs[page->num_glyphs++];
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ page = malloc (sizeof (cairo_scaled_glyph_page_t));
+ if (unlikely (page == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ page->cache_entry.hash = (unsigned long) scaled_font;
+ page->cache_entry.size = 1; /* XXX occupancy weighting? */
+ page->num_glyphs = 0;
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ if (scaled_font->global_cache_frozen == FALSE) {
+ if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) {
+ status = _cairo_cache_init (&cairo_scaled_glyph_page_cache,
+ NULL,
+ _cairo_scaled_glyph_page_can_remove,
+ _cairo_scaled_glyph_page_pluck,
+ MAX_GLYPH_PAGES_CACHED);
+ if (unlikely (status)) {
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+ free (page);
+ return status;
+ }
+ }
+
+ _cairo_cache_freeze (&cairo_scaled_glyph_page_cache);
+ scaled_font->global_cache_frozen = TRUE;
+ }
+
+ status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache,
+ &page->cache_entry);
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+ if (unlikely (status)) {
+ free (page);
+ return status;
+ }
+
+ cairo_list_add_tail (&page->link, &scaled_font->glyph_pages);
+
+ *scaled_glyph = &page->glyphs[page->num_glyphs++];
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_scaled_glyph_page_t *page;
+
+ assert (! 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_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ _cairo_cache_remove (&cairo_scaled_glyph_page_cache,
+ &page->cache_entry);
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+ }
+}
+
+/**
+ * _cairo_scaled_glyph_lookup:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @index: the glyph to create
+ * @info: a #cairo_scaled_glyph_info_t marking which portions of
+ * the glyph should be filled in.
+ * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
+ * is returned.
+ *
+ * If the desired info is not available, (for example, when trying to
+ * get INFO_PATH with a bitmapped font), this function will return
+ * %CAIRO_INT_STATUS_UNSUPPORTED.
+ *
+ * Note: This function must be called with the scaled font frozen, and it must
+ * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled
+ * font was not frozen, then there is no guarantee that the glyph would not be
+ * evicted before you tried to access it.) See
+ * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache().
+ *
+ * Returns: a glyph with the requested portions filled in. Glyph
+ * lookup is cached and glyph will be automatically freed along
+ * with the scaled_font so no explicit free is required.
+ * @info can be one or more of:
+ * %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box
+ * %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image
+ * %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space
+ **/
+cairo_int_status_t
+_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
+ unsigned long index,
+ cairo_scaled_glyph_info_t info,
+ cairo_scaled_glyph_t **scaled_glyph_ret)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_scaled_glyph_info_t need_info;
+
+ *scaled_glyph_ret = NULL;
+
+ if (unlikely (scaled_font->status))
+ return scaled_font->status;
+
+ assert (CAIRO_MUTEX_IS_LOCKED(scaled_font->mutex));
+ assert (scaled_font->cache_frozen);
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /*
+ * Check cache for glyph
+ */
+ scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs,
+ (cairo_hash_entry_t *) &index);
+ if (scaled_glyph == NULL) {
+ status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph);
+ if (unlikely (status))
+ goto err;
+
+ memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t));
+ _cairo_scaled_glyph_set_index (scaled_glyph, index);
+ cairo_list_init (&scaled_glyph->dev_privates);
+
+ /* ask backend to initialize metrics and shape fields */
+ status =
+ scaled_font->backend->scaled_glyph_init (scaled_font,
+ scaled_glyph,
+ info | CAIRO_SCALED_GLYPH_INFO_METRICS);
+ if (unlikely (status)) {
+ _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
+ goto err;
+ }
+
+ status = _cairo_hash_table_insert (scaled_font->glyphs,
+ &scaled_glyph->hash_entry);
+ if (unlikely (status)) {
+ _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
+ goto err;
+ }
+ }
+
+ /*
+ * Check and see if the glyph, as provided,
+ * already has the requested data and amend it if not
+ */
+ need_info = info & ~scaled_glyph->has_info;
+ if (need_info) {
+ status = scaled_font->backend->scaled_glyph_init (scaled_font,
+ scaled_glyph,
+ need_info);
+ if (unlikely (status))
+ goto err;
+
+ /* Don't trust the scaled_glyph_init() return value, the font
+ * backend may not even know about some of the info. For example,
+ * no backend other than the user-fonts knows about recording-surface
+ * glyph info. */
+ if (info & ~scaled_glyph->has_info)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ *scaled_glyph_ret = scaled_glyph;
+ return CAIRO_STATUS_SUCCESS;
+
+err:
+ /* It's not an error for the backend to not support the info we want. */
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ status = _cairo_scaled_font_set_error (scaled_font, status);
+ return status;
+}
+
+double
+_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font)
+{
+ return scaled_font->max_scale;
+}
+
+
+/**
+ * cairo_scaled_font_get_font_face:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Gets the font face that this scaled font uses. This might be the
+ * font face passed to cairo_scaled_font_create(), but this does not
+ * hold true for all possible cases.
+ *
+ * Return value: The #cairo_font_face_t with which @scaled_font was
+ * created. This object is owned by cairo. To keep a reference to it,
+ * you must call cairo_scaled_font_reference().
+ *
+ * Since: 1.2
+ **/
+cairo_font_face_t *
+cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font)
+{
+ if (scaled_font->status)
+ return (cairo_font_face_t*) &_cairo_font_face_nil;
+
+ if (scaled_font->original_font_face != NULL)
+ return scaled_font->original_font_face;
+
+ return scaled_font->font_face;
+}
+slim_hidden_def (cairo_scaled_font_get_font_face);
+
+/**
+ * cairo_scaled_font_get_font_matrix:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @font_matrix: return value for the matrix
+ *
+ * Stores the font matrix with which @scaled_font was created into
+ * @matrix.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font,
+ cairo_matrix_t *font_matrix)
+{
+ if (scaled_font->status) {
+ cairo_matrix_init_identity (font_matrix);
+ return;
+ }
+
+ *font_matrix = scaled_font->font_matrix;
+}
+slim_hidden_def (cairo_scaled_font_get_font_matrix);
+
+/**
+ * cairo_scaled_font_get_ctm:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @ctm: return value for the CTM
+ *
+ * Stores the CTM with which @scaled_font was created into @ctm.
+ * Note that the translation offsets (x0, y0) of the CTM are ignored
+ * by cairo_scaled_font_create(). So, the matrix this
+ * function returns always has 0,0 as x0,y0.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font,
+ cairo_matrix_t *ctm)
+{
+ if (scaled_font->status) {
+ cairo_matrix_init_identity (ctm);
+ return;
+ }
+
+ *ctm = scaled_font->ctm;
+}
+slim_hidden_def (cairo_scaled_font_get_ctm);
+
+/**
+ * cairo_scaled_font_get_scale_matrix:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @scale_matrix: return value for the matrix
+ *
+ * Stores the scale matrix of @scaled_font into @matrix.
+ * The scale matrix is product of the font matrix and the ctm
+ * associated with the scaled font, and hence is the matrix mapping from
+ * font space to device space.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font,
+ cairo_matrix_t *scale_matrix)
+{
+ if (scaled_font->status) {
+ cairo_matrix_init_identity (scale_matrix);
+ return;
+ }
+
+ *scale_matrix = scaled_font->scale;
+}
+
+/**
+ * cairo_scaled_font_get_font_options:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @options: return value for the font options
+ *
+ * Stores the font options with which @scaled_font was created into
+ * @options.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font,
+ cairo_font_options_t *options)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ if (scaled_font->status) {
+ _cairo_font_options_init_default (options);
+ return;
+ }
+
+ _cairo_font_options_init_copy (options, &scaled_font->options);
+}
+slim_hidden_def (cairo_scaled_font_get_font_options);
diff --git a/src/cairo-script-private.h b/src/cairo-script-private.h
new file mode 100755
index 000000000..5b506f500
--- /dev/null
+++ b/src/cairo-script-private.h
@@ -0,0 +1,59 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SCRIPT_PRIVATE_H
+#define CAIRO_SCRIPT_PRIVATE_H
+
+#include "cairo.h"
+#include "cairo-script.h"
+
+#include "cairo-compiler-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_device_t *
+_cairo_script_context_create_internal (cairo_output_stream_t *stream);
+
+cairo_private void
+_cairo_script_context_attach_snapshots (cairo_device_t *device,
+ cairo_bool_t enable);
+
+slim_hidden_proto (cairo_script_surface_create);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SCRIPT_PRIVATE_H */
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
new file mode 100755
index 000000000..68c1528f6
--- /dev/null
+++ b/src/cairo-script-surface.c
@@ -0,0 +1,4024 @@
+/* -*- 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.
+ */
+
+/**
+ * SECTION:cairo-script
+ * @Title: Script Surfaces
+ * @Short_Description: Rendering to replayable scripts
+ * @See_Also: #cairo_surface_t
+ *
+ * The script surface provides the ability to render to a native
+ * script that matches the cairo drawing model. The scripts can
+ * be replayed using tools under the util/cairo-script directoriy,
+ * or with cairo-perf-trace.
+ **/
+
+/**
+ * CAIRO_HAS_SCRIPT_SURFACE:
+ *
+ * Defined if the script surface backend is available.
+ * The script surface backend is always built in since 1.12.
+ *
+ * Since: 1.12
+ **/
+
+
+#include "cairoint.h"
+
+#include "cairo-script.h"
+#include "cairo-script-private.h"
+
+#include "cairo-analysis-surface-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-wrapper-private.h"
+
+#if CAIRO_HAS_FT_FONT
+#include "cairo-ft-private.h"
+#endif
+
+#include <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_font cairo_script_font_t;
+
+typedef struct _operand {
+ enum {
+ SURFACE,
+ DEFERRED,
+ } type;
+ cairo_list_t link;
+} operand_t;
+
+
+struct deferred_finish {
+ cairo_list_t link;
+ operand_t operand;
+};
+
+struct _cairo_script_context {
+ cairo_device_t base;
+
+ int active;
+ int attach_snapshots;
+
+ cairo_bool_t owns_stream;
+ cairo_output_stream_t *stream;
+ cairo_script_mode_t mode;
+
+ struct _bitmap {
+ unsigned long min;
+ unsigned long count;
+ unsigned int map[64];
+ struct _bitmap *next;
+ } surface_id, font_id;
+
+ cairo_list_t operands;
+ cairo_list_t deferred;
+
+ cairo_list_t fonts;
+ cairo_list_t defines;
+};
+
+struct _cairo_script_font {
+ cairo_scaled_font_private_t base;
+
+ cairo_bool_t has_sfnt;
+ unsigned long id;
+ unsigned long subset_glyph_index;
+ cairo_list_t link;
+ cairo_scaled_font_t *parent;
+};
+
+struct _cairo_script_implicit_context {
+ cairo_operator_t current_operator;
+ cairo_fill_rule_t current_fill_rule;
+ double current_tolerance;
+ cairo_antialias_t current_antialias;
+ cairo_stroke_style_t current_style;
+ cairo_pattern_union_t current_source;
+ cairo_matrix_t current_ctm;
+ cairo_matrix_t current_stroke_matrix;
+ cairo_matrix_t current_font_matrix;
+ cairo_font_options_t current_font_options;
+ cairo_scaled_font_t *current_scaled_font;
+ cairo_path_fixed_t current_path;
+ cairo_bool_t has_clip;
+};
+
+struct _cairo_script_surface {
+ cairo_surface_t base;
+
+ cairo_surface_wrapper_t wrapper;
+
+ cairo_surface_clipper_t clipper;
+
+ operand_t operand;
+ cairo_bool_t emitted;
+ cairo_bool_t defined;
+ cairo_bool_t active;
+
+ double width, height;
+
+ /* implicit flattened context */
+ cairo_script_implicit_context_t cr;
+};
+
+static const cairo_surface_backend_t _cairo_script_surface_backend;
+
+static cairo_script_surface_t *
+_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
+ cairo_content_t content,
+ cairo_rectangle_t *extents,
+ cairo_surface_t *passthrough);
+
+static void
+_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
+ cairo_scaled_font_t *scaled_font);
+
+static void
+_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr);
+
+static void
+_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr);
+
+static void
+_bitmap_release_id (struct _bitmap *b, unsigned long token)
+{
+ struct _bitmap **prev = NULL;
+
+ do {
+ if (token < b->min + sizeof (b->map) * CHAR_BIT) {
+ unsigned int bit, elem;
+
+ token -= b->min;
+ elem = token / (sizeof (b->map[0]) * CHAR_BIT);
+ bit = token % (sizeof (b->map[0]) * CHAR_BIT);
+ b->map[elem] &= ~(1 << bit);
+ if (! --b->count && prev) {
+ *prev = b->next;
+ free (b);
+ }
+ return;
+ }
+ prev = &b->next;
+ b = b->next;
+ } while (b != NULL);
+}
+
+static cairo_status_t
+_bitmap_next_id (struct _bitmap *b,
+ unsigned long *id)
+{
+ struct _bitmap *bb, **prev = NULL;
+ unsigned long min = 0;
+
+ do {
+ if (b->min != min)
+ break;
+
+ if (b->count < sizeof (b->map) * CHAR_BIT) {
+ unsigned int n, m, bit;
+ for (n = 0; n < ARRAY_LENGTH (b->map); n++) {
+ if (b->map[n] == (unsigned int) -1)
+ continue;
+
+ for (m=0, bit=1; 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);
+
+ if (prev == NULL) {
+ free (bb);
+ return CAIRO_STATUS_NULL_POINTER;
+ }
+
+ *prev = bb;
+ bb->next = b;
+ bb->min = min;
+ bb->count = 1;
+ bb->map[0] = 0x1;
+ memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0]));
+ *id = min;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_bitmap_fini (struct _bitmap *b)
+{
+ while (b != NULL) {
+ struct _bitmap *next = b->next;
+ free (b);
+ b = next;
+ }
+}
+
+static const char *
+_direction_to_string (cairo_bool_t backward)
+{
+ static const char *names[] = {
+ "FORWARD",
+ "BACKWARD"
+ };
+ assert (backward < ARRAY_LENGTH (names));
+ return names[backward];
+}
+
+static const char *
+_operator_to_string (cairo_operator_t op)
+{
+ static const char *names[] = {
+ "CLEAR", /* CAIRO_OPERATOR_CLEAR */
+
+ "SOURCE", /* CAIRO_OPERATOR_SOURCE */
+ "OVER", /* CAIRO_OPERATOR_OVER */
+ "IN", /* CAIRO_OPERATOR_IN */
+ "OUT", /* CAIRO_OPERATOR_OUT */
+ "ATOP", /* CAIRO_OPERATOR_ATOP */
+
+ "DEST", /* CAIRO_OPERATOR_DEST */
+ "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
+ "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
+ "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
+ "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
+
+ "XOR", /* CAIRO_OPERATOR_XOR */
+ "ADD", /* CAIRO_OPERATOR_ADD */
+ "SATURATE", /* CAIRO_OPERATOR_SATURATE */
+
+ "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
+ "SCREEN", /* CAIRO_OPERATOR_SCREEN */
+ "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
+ "DARKEN", /* CAIRO_OPERATOR_DARKEN */
+ "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
+ "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
+ "BURN", /* CAIRO_OPERATOR_COLOR_BURN */
+ "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
+ "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
+ "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
+ "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
+ "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
+ "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
+ "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
+ "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
+ };
+ assert (op < ARRAY_LENGTH (names));
+ return names[op];
+}
+
+static const char *
+_extend_to_string (cairo_extend_t extend)
+{
+ static const char *names[] = {
+ "EXTEND_NONE", /* CAIRO_EXTEND_NONE */
+ "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */
+ "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */
+ "EXTEND_PAD" /* CAIRO_EXTEND_PAD */
+ };
+ assert (extend < ARRAY_LENGTH (names));
+ return names[extend];
+}
+
+static const char *
+_filter_to_string (cairo_filter_t filter)
+{
+ static const char *names[] = {
+ "FILTER_FAST", /* CAIRO_FILTER_FAST */
+ "FILTER_GOOD", /* CAIRO_FILTER_GOOD */
+ "FILTER_BEST", /* CAIRO_FILTER_BEST */
+ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
+ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
+ "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
+ };
+ assert (filter < ARRAY_LENGTH (names));
+ return names[filter];
+}
+
+static const char *
+_fill_rule_to_string (cairo_fill_rule_t rule)
+{
+ static const char *names[] = {
+ "WINDING", /* CAIRO_FILL_RULE_WINDING */
+ "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
+ };
+ assert (rule < ARRAY_LENGTH (names));
+ return names[rule];
+}
+
+static const char *
+_antialias_to_string (cairo_antialias_t antialias)
+{
+ static const char *names[] = {
+ "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
+ "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */
+ "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */
+ "ANTIALIAS_SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */
+ "ANTIALIAS_FAST", /* CAIRO_ANTIALIAS_FAST */
+ "ANTIALIAS_GOOD", /* CAIRO_ANTIALIAS_GOOD */
+ "ANTIALIAS_BEST" /* CAIRO_ANTIALIAS_BEST */
+ };
+ assert (antialias < ARRAY_LENGTH (names));
+ return names[antialias];
+}
+
+static const char *
+_line_cap_to_string (cairo_line_cap_t line_cap)
+{
+ static const char *names[] = {
+ "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
+ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
+ "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
+ };
+ assert (line_cap < ARRAY_LENGTH (names));
+ return names[line_cap];
+}
+
+static const char *
+_line_join_to_string (cairo_line_join_t line_join)
+{
+ static const char *names[] = {
+ "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
+ "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
+ "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
+ };
+ assert (line_join < ARRAY_LENGTH (names));
+ return names[line_join];
+}
+
+static inline cairo_script_context_t *
+to_context (cairo_script_surface_t *surface)
+{
+ return (cairo_script_context_t *) surface->base.device;
+}
+
+static cairo_bool_t
+target_is_active (cairo_script_surface_t *surface)
+{
+ return cairo_list_is_first (&surface->operand.link,
+ &to_context (surface)->operands);
+}
+
+static void
+target_push (cairo_script_surface_t *surface)
+{
+ cairo_list_move (&surface->operand.link, &to_context (surface)->operands);
+}
+
+static int
+target_depth (cairo_script_surface_t *surface)
+{
+ cairo_list_t *link;
+ int depth = 0;
+
+ cairo_list_foreach (link, &to_context (surface)->operands) {
+ if (link == &surface->operand.link)
+ break;
+ depth++;
+ }
+
+ return depth;
+}
+
+static void
+_get_target (cairo_script_surface_t *surface)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+
+ if (target_is_active (surface)) {
+ _cairo_output_stream_puts (ctx->stream, "dup ");
+ return;
+ }
+
+ if (surface->defined) {
+ _cairo_output_stream_printf (ctx->stream, "s%u ",
+ surface->base.unique_id);
+ } else {
+ int depth = target_depth (surface);
+
+ assert (! cairo_list_is_empty (&surface->operand.link));
+ assert (! target_is_active (surface));
+
+ if (ctx->active) {
+ _cairo_output_stream_printf (ctx->stream, "%d index ", depth);
+ _cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
+ } else {
+ if (depth == 1) {
+ _cairo_output_stream_puts (ctx->stream, "exch ");
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "%d -1 roll ", depth);
+ }
+ target_push (surface);
+ _cairo_output_stream_puts (ctx->stream, "dup ");
+ }
+ }
+}
+
+static const char *
+_content_to_string (cairo_content_t content)
+{
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA: return "ALPHA";
+ case CAIRO_CONTENT_COLOR: return "COLOR";
+ default:
+ case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
+ }
+}
+
+static cairo_status_t
+_emit_surface (cairo_script_surface_t *surface)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "<< /content //%s",
+ _content_to_string (surface->base.content));
+ if (surface->width != -1 && surface->height != -1) {
+ _cairo_output_stream_printf (ctx->stream,
+ " /width %f /height %f",
+ surface->width,
+ surface->height);
+ }
+
+ if (surface->base.x_fallback_resolution !=
+ CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT ||
+ surface->base.y_fallback_resolution !=
+ CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT)
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ " /fallback-resolution [%f %f]",
+ surface->base.x_fallback_resolution,
+ surface->base.y_fallback_resolution);
+ }
+
+ if (surface->base.device_transform.x0 != 0. ||
+ surface->base.device_transform.y0 != 0.)
+ {
+ /* XXX device offset is encoded into the pattern matrices etc. */
+ if (0) {
+ _cairo_output_stream_printf (ctx->stream,
+ " /device-offset [%f %f]",
+ surface->base.device_transform.x0,
+ surface->base.device_transform.y0);
+ }
+ }
+
+ _cairo_output_stream_puts (ctx->stream, " >> surface context\n");
+ surface->emitted = TRUE;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_context (cairo_script_surface_t *surface)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+
+ if (target_is_active (surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ while (! cairo_list_is_empty (&ctx->operands)) {
+ operand_t *op;
+ cairo_script_surface_t *old;
+
+ op = cairo_list_first_entry (&ctx->operands,
+ operand_t,
+ link);
+ if (op->type == DEFERRED)
+ break;
+
+ old = cairo_container_of (op, cairo_script_surface_t, operand);
+ if (old == surface)
+ break;
+ if (old->active)
+ break;
+
+ if (! old->defined) {
+ assert (old->emitted);
+ _cairo_output_stream_printf (ctx->stream,
+ "/target get /s%u exch def pop\n",
+ old->base.unique_id);
+ old->defined = TRUE;
+ } else {
+ _cairo_output_stream_puts (ctx->stream, "pop\n");
+ }
+
+ cairo_list_del (&old->operand.link);
+ }
+
+ if (target_is_active (surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! surface->emitted) {
+ cairo_status_t status;
+
+ status = _emit_surface (surface);
+ if (unlikely (status))
+ return status;
+ } else if (cairo_list_is_empty (&surface->operand.link)) {
+ assert (surface->defined);
+ _cairo_output_stream_printf (ctx->stream,
+ "s%u context\n",
+ surface->base.unique_id);
+ _cairo_script_implicit_context_reset (&surface->cr);
+ _cairo_surface_clipper_reset (&surface->clipper);
+ } else {
+ int depth = target_depth (surface);
+ if (depth == 1) {
+ _cairo_output_stream_puts (ctx->stream, "exch\n");
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "%d -1 roll\n",
+ depth);
+ }
+ }
+ target_push (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_operator (cairo_script_surface_t *surface,
+ cairo_operator_t op)
+{
+ assert (target_is_active (surface));
+
+ if (surface->cr.current_operator == op)
+ return CAIRO_STATUS_SUCCESS;
+
+ surface->cr.current_operator = op;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "//%s set-operator\n",
+ _operator_to_string (op));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_fill_rule (cairo_script_surface_t *surface,
+ cairo_fill_rule_t fill_rule)
+{
+ assert (target_is_active (surface));
+
+ if (surface->cr.current_fill_rule == fill_rule)
+ return CAIRO_STATUS_SUCCESS;
+
+ surface->cr.current_fill_rule = fill_rule;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "//%s set-fill-rule\n",
+ _fill_rule_to_string (fill_rule));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_tolerance (cairo_script_surface_t *surface,
+ double tolerance,
+ cairo_bool_t force)
+{
+ assert (target_is_active (surface));
+
+ if ((! force ||
+ fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) &&
+ surface->cr.current_tolerance == tolerance)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ surface->cr.current_tolerance = tolerance;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "%f set-tolerance\n",
+ tolerance);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_antialias (cairo_script_surface_t *surface,
+ cairo_antialias_t antialias)
+{
+ assert (target_is_active (surface));
+
+ if (surface->cr.current_antialias == antialias)
+ return CAIRO_STATUS_SUCCESS;
+
+ surface->cr.current_antialias = antialias;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "//%s set-antialias\n",
+ _antialias_to_string (antialias));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_line_width (cairo_script_surface_t *surface,
+ double line_width,
+ cairo_bool_t force)
+{
+ assert (target_is_active (surface));
+
+ if ((! force ||
+ fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) &&
+ surface->cr.current_style.line_width == line_width)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ surface->cr.current_style.line_width = line_width;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "%f set-line-width\n",
+ line_width);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_line_cap (cairo_script_surface_t *surface,
+ cairo_line_cap_t line_cap)
+{
+ assert (target_is_active (surface));
+
+ if (surface->cr.current_style.line_cap == line_cap)
+ return CAIRO_STATUS_SUCCESS;
+
+ surface->cr.current_style.line_cap = line_cap;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "//%s set-line-cap\n",
+ _line_cap_to_string (line_cap));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_line_join (cairo_script_surface_t *surface,
+ cairo_line_join_t line_join)
+{
+ assert (target_is_active (surface));
+
+ if (surface->cr.current_style.line_join == line_join)
+ return CAIRO_STATUS_SUCCESS;
+
+ surface->cr.current_style.line_join = line_join;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "//%s set-line-join\n",
+ _line_join_to_string (line_join));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_miter_limit (cairo_script_surface_t *surface,
+ double miter_limit,
+ cairo_bool_t force)
+{
+ assert (target_is_active (surface));
+
+ if ((! force ||
+ fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) &&
+ surface->cr.current_style.miter_limit == miter_limit)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ surface->cr.current_style.miter_limit = miter_limit;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "%f set-miter-limit\n",
+ miter_limit);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_dashes_equal (const double *a, const double *b, int num_dashes)
+{
+ while (num_dashes--) {
+ if (fabs (*a - *b) > 1e-5)
+ return FALSE;
+ a++, b++;
+ }
+
+ return TRUE;
+}
+
+static cairo_status_t
+_emit_dash (cairo_script_surface_t *surface,
+ const double *dash,
+ unsigned int num_dashes,
+ double offset,
+ cairo_bool_t force)
+{
+ unsigned int n;
+
+ assert (target_is_active (surface));
+
+ if (force &&
+ num_dashes == 0 &&
+ surface->cr.current_style.num_dashes == 0)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (! force &&
+ (surface->cr.current_style.num_dashes == num_dashes &&
+ (num_dashes == 0 ||
+ (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 &&
+ _dashes_equal (surface->cr.current_style.dash, dash, num_dashes)))))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+
+ if (num_dashes) {
+ surface->cr.current_style.dash = _cairo_realloc_ab
+ (surface->cr.current_style.dash, num_dashes, sizeof (double));
+ if (unlikely (surface->cr.current_style.dash == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (surface->cr.current_style.dash, dash,
+ sizeof (double) * num_dashes);
+ } else {
+ free (surface->cr.current_style.dash);
+ surface->cr.current_style.dash = NULL;
+ }
+
+ surface->cr.current_style.num_dashes = num_dashes;
+ surface->cr.current_style.dash_offset = offset;
+
+ _cairo_output_stream_puts (to_context (surface)->stream, "[");
+ for (n = 0; n < num_dashes; n++) {
+ _cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]);
+ if (n < num_dashes-1)
+ _cairo_output_stream_puts (to_context (surface)->stream, " ");
+ }
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "] %f set-dash\n",
+ offset);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_stroke_style (cairo_script_surface_t *surface,
+ const cairo_stroke_style_t *style,
+ cairo_bool_t force)
+{
+ cairo_status_t status;
+
+ assert (target_is_active (surface));
+
+ status = _emit_line_width (surface, style->line_width, force);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_line_cap (surface, style->line_cap);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_line_join (surface, style->line_join);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_miter_limit (surface, style->miter_limit, force);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_dash (surface,
+ style->dash, style->num_dashes, style->dash_offset,
+ force);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const char *
+_format_to_string (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32: return "ARGB32";
+ case CAIRO_FORMAT_RGB30: return "RGB30";
+ case CAIRO_FORMAT_RGB24: return "RGB24";
+ case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
+ case CAIRO_FORMAT_A8: return "A8";
+ case CAIRO_FORMAT_A1: return "A1";
+ case CAIRO_FORMAT_INVALID: return "INVALID";
+ }
+ ASSERT_NOT_REACHED;
+ return "INVALID";
+}
+
+static cairo_status_t
+_emit_solid_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+ cairo_script_context_t *ctx = to_context (surface);
+
+ if (! CAIRO_COLOR_IS_OPAQUE (&solid->color))
+ {
+ if (! (surface->base.content & CAIRO_CONTENT_COLOR) ||
+ ((solid->color.red_short == 0 || solid->color.red_short == 0xffff) &&
+ (solid->color.green_short == 0 || solid->color.green_short == 0xffff) &&
+ (solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) ))
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ "%f a",
+ solid->color.alpha);
+ }
+ else
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ "%f %f %f %f rgba",
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+ }
+ }
+ else
+ {
+ if (solid->color.red_short == solid->color.green_short &&
+ solid->color.red_short == solid->color.blue_short)
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ "%f g",
+ solid->color.red);
+ }
+ else
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ "%f %f %f rgb",
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue);
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient,
+ cairo_output_stream_t *output)
+{
+ unsigned int n;
+
+ for (n = 0; n < gradient->n_stops; n++) {
+ _cairo_output_stream_printf (output,
+ "\n %f %f %f %f %f add-color-stop",
+ gradient->stops[n].offset,
+ gradient->stops[n].color.red,
+ gradient->stops[n].color.green,
+ gradient->stops[n].color.blue,
+ gradient->stops[n].color.alpha);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_linear_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_linear_pattern_t *linear;
+
+ linear = (cairo_linear_pattern_t *) pattern;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "%f %f %f %f linear",
+ linear->pd1.x, linear->pd1.y,
+ linear->pd2.x, linear->pd2.y);
+ return _emit_gradient_color_stops (&linear->base, ctx->stream);
+}
+
+static cairo_status_t
+_emit_radial_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_radial_pattern_t *radial;
+
+ radial = (cairo_radial_pattern_t *) pattern;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "%f %f %f %f %f %f radial",
+ radial->cd1.center.x,
+ radial->cd1.center.y,
+ radial->cd1.radius,
+ radial->cd2.center.x,
+ radial->cd2.center.y,
+ radial->cd2.radius);
+ return _emit_gradient_color_stops (&radial->base, ctx->stream);
+}
+
+static cairo_status_t
+_emit_mesh_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_pattern_t *mesh;
+ cairo_status_t status;
+ unsigned int i, n;
+
+ mesh = (cairo_pattern_t *) pattern;
+ status = cairo_mesh_pattern_get_patch_count (mesh, &n);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (ctx->stream, "mesh");
+ for (i = 0; i < n; i++) {
+ cairo_path_t *path;
+ cairo_path_data_t *data;
+ int j;
+
+ _cairo_output_stream_printf (ctx->stream, "\n begin-patch");
+
+ path = cairo_mesh_pattern_get_path (mesh, i);
+ if (unlikely (path->status)) {
+ status = path->status;
+ cairo_path_destroy (path);
+ return status;
+ }
+
+ for (j = 0; j < path->num_data; j+=data[0].header.length) {
+ data = &path->data[j];
+ switch (data->header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %f %f m",
+ data[1].point.x, data[1].point.y);
+ break;
+ case CAIRO_PATH_LINE_TO:
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %f %f l",
+ data[1].point.x, data[1].point.y);
+ break;
+ case CAIRO_PATH_CURVE_TO:
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %f %f %f %f %f %f c",
+ data[1].point.x, data[1].point.y,
+ data[2].point.x, data[2].point.y,
+ data[3].point.x, data[3].point.y);
+ break;
+ case CAIRO_PATH_CLOSE_PATH:
+ break;
+ }
+ }
+ cairo_path_destroy (path);
+
+ for (j = 0; j < 4; j++) {
+ double x, y;
+
+ status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y);
+ if (unlikely (status))
+ return status;
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %d %f %f set-control-point",
+ j, x, y);
+ }
+
+ for (j = 0; j < 4; j++) {
+ double r, g, b, a;
+
+ status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %d %f %f %f %f set-corner-color",
+ j, r, g, b, a);
+ }
+
+ _cairo_output_stream_printf (ctx->stream, "\n end-patch");
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct script_snapshot {
+ cairo_surface_t base;
+};
+
+static cairo_status_t
+script_snapshot_finish (void *abstract_surface)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t script_snapshot_backend = {
+ CAIRO_SURFACE_TYPE_SCRIPT,
+ script_snapshot_finish,
+};
+
+static void
+detach_snapshot (cairo_surface_t *abstract_surface)
+{
+ cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface;
+ cairo_script_context_t *ctx = to_context (surface);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "/s%d undef\n",
+ surface->base.unique_id);
+}
+
+static void
+attach_snapshot (cairo_script_context_t *ctx,
+ cairo_surface_t *source)
+{
+ struct script_snapshot *surface;
+
+ if (! ctx->attach_snapshots)
+ return;
+
+ surface = malloc (sizeof (*surface));
+ if (unlikely (surface == NULL))
+ return;
+
+ _cairo_surface_init (&surface->base,
+ &script_snapshot_backend,
+ &ctx->base,
+ source->content);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "dup /s%d exch def ",
+ surface->base.unique_id);
+
+ _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot);
+ cairo_surface_destroy (&surface->base);
+}
+
+static cairo_status_t
+_emit_recording_surface_pattern (cairo_script_surface_t *surface,
+ cairo_recording_surface_t *source)
+{
+ cairo_script_implicit_context_t old_cr;
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_surface_t *similar;
+ cairo_surface_t *snapshot;
+ cairo_rectangle_t r, *extents;
+ cairo_status_t status;
+
+ snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend);
+ if (snapshot) {
+ _cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ extents = NULL;
+ if (_cairo_recording_surface_get_bounds (&source->base, &r))
+ extents = &r;
+
+ similar = _cairo_script_surface_create_internal (ctx,
+ source->base.content,
+ extents,
+ NULL);
+ if (unlikely (similar->base.status)) {
+ status = similar->base.status;
+ cairo_surface_destroy (&similar->base);
+ return status;
+ }
+
+ similar->base.is_clear = TRUE;
+
+ _cairo_output_stream_printf (ctx->stream, "//%s ",
+ _content_to_string (source->base.content));
+ if (extents) {
+ _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]",
+ extents->x, extents->y,
+ extents->width, extents->height);
+ } else
+ _cairo_output_stream_puts (ctx->stream, "[]");
+ _cairo_output_stream_puts (ctx->stream, " record\n");
+
+ attach_snapshot (ctx, &source->base);
+
+ _cairo_output_stream_puts (ctx->stream, "dup context\n");
+
+ target_push (similar);
+ similar->emitted = TRUE;
+
+
+ old_cr = surface->cr;
+ _cairo_script_implicit_context_init (&surface->cr);
+ status = _cairo_recording_surface_replay (&source->base, &similar->base);
+ surface->cr = old_cr;
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&similar->base);
+ return status;
+ }
+
+ cairo_list_del (&similar->operand.link);
+ assert (target_is_active (surface));
+
+ _cairo_output_stream_puts (ctx->stream, "pop ");
+ cairo_surface_destroy (&similar->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_script_surface_pattern (cairo_script_surface_t *surface,
+ cairo_script_surface_t *source)
+{
+ _get_target (source);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_write_image_surface (cairo_output_stream_t *output,
+ const cairo_image_surface_t *image)
+{
+ int 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_RGB30:
+ case CAIRO_FORMAT_ARGB32:
+ for (row = image->height; row--; ) {
+ uint32_t *src = (uint32_t *) data;
+ uint32_t *dst = (uint32_t *) rowdata;
+ int col;
+ for (col = 0; col < width; col++)
+ dst[col] = bswap_32 (src[col]);
+ _cairo_output_stream_write (output, rowdata, 4*width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ if (rowdata != row_stack)
+ free (rowdata);
+#endif
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_png_surface (cairo_script_surface_t *surface,
+ cairo_image_surface_t *image)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_output_stream_t *base85_stream;
+ cairo_status_t status;
+ const uint8_t *mime_data;
+ unsigned long mime_data_length;
+
+ cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG,
+ &mime_data, &mime_data_length);
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "<< "
+ "/width %d "
+ "/height %d "
+ "/format //%s "
+ "/mime-type (image/png) "
+ "/source <~",
+ image->width, image->height,
+ _format_to_string (image->format));
+
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ _cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
+ status = _cairo_output_stream_destroy (base85_stream);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (ctx->stream, "~> >> image ");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_image_surface (cairo_script_surface_t *surface,
+ cairo_image_surface_t *image)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_output_stream_t *base85_stream;
+ cairo_output_stream_t *zlib_stream;
+ cairo_int_status_t status, status2;
+ cairo_surface_t *snapshot;
+ const uint8_t *mime_data;
+ unsigned long mime_data_length;
+
+ snapshot = _cairo_surface_has_snapshot (&image->base,
+ &script_snapshot_backend);
+ if (snapshot) {
+ _cairo_output_stream_printf (ctx->stream, "s%u ", snapshot->unique_id);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ status = _emit_png_surface (surface, image);
+ if (_cairo_int_status_is_error (status)) {
+ return status;
+ } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_image_surface_t *clone;
+ uint32_t len;
+
+ if (image->format == CAIRO_FORMAT_INVALID) {
+ clone = _cairo_image_surface_coerce (image);
+ } else {
+ clone = (cairo_image_surface_t *)
+ cairo_surface_reference (&image->base);
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ "<< "
+ "/width %d "
+ "/height %d "
+ "/format //%s "
+ "/source ",
+ clone->width, clone->height,
+ _format_to_string (clone->format));
+
+ switch (clone->format) {
+ case CAIRO_FORMAT_A1:
+ len = (clone->width + 7)/8;
+ break;
+ case CAIRO_FORMAT_A8:
+ len = clone->width;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ len = clone->width * 2;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ len = clone->width * 3;
+ break;
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_ARGB32:
+ len = clone->width * 4;
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ len = 0;
+ break;
+ }
+ len *= clone->height;
+
+ if (len > 24) {
+ _cairo_output_stream_puts (ctx->stream, "<|");
+
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+
+ len = to_be32 (len);
+ _cairo_output_stream_write (base85_stream, &len, sizeof (len));
+
+ zlib_stream = _cairo_deflate_stream_create (base85_stream);
+ status = _write_image_surface (zlib_stream, clone);
+
+ status2 = _cairo_output_stream_destroy (zlib_stream);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = status2;
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = status2;
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+ } else {
+ _cairo_output_stream_puts (ctx->stream, "<~");
+
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ status = _write_image_surface (base85_stream, clone);
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = status2;
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+ }
+ _cairo_output_stream_puts (ctx->stream, "~> >> image ");
+
+ cairo_surface_destroy (&clone->base);
+ }
+
+ cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG,
+ &mime_data, &mime_data_length);
+ if (mime_data != NULL) {
+ _cairo_output_stream_printf (ctx->stream,
+ "\n (%s) <~",
+ CAIRO_MIME_TYPE_JPEG);
+
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ _cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
+ status = _cairo_output_stream_destroy (base85_stream);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
+ }
+
+ cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2,
+ &mime_data, &mime_data_length);
+ if (mime_data != NULL) {
+ _cairo_output_stream_printf (ctx->stream,
+ "\n (%s) <~",
+ CAIRO_MIME_TYPE_JP2);
+
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ _cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
+ status = _cairo_output_stream_destroy (base85_stream);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_image_surface_pattern (cairo_script_surface_t *surface,
+ cairo_surface_t *source)
+{
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ void *extra;
+
+ status = _cairo_surface_acquire_source_image (source, &image, &extra);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _emit_image_surface (surface, image);
+ _cairo_surface_release_source_image (source, image, extra);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_emit_subsurface_pattern (cairo_script_surface_t *surface,
+ cairo_surface_subsurface_t *sub)
+{
+ cairo_surface_t *source = sub->target;
+ cairo_int_status_t status;
+
+ switch ((int) source->backend->type) {
+ case CAIRO_SURFACE_TYPE_RECORDING:
+ status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
+ break;
+ case CAIRO_SURFACE_TYPE_SCRIPT:
+ status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
+ break;
+ default:
+ status = _emit_image_surface_pattern (surface, source);
+ break;
+ }
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "%d %d %d %d subsurface ",
+ sub->extents.x,
+ sub->extents.y,
+ sub->extents.width,
+ sub->extents.height);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_surface_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_surface_pattern_t *surface_pattern;
+ cairo_surface_t *source, *snapshot, *free_me = NULL;
+ cairo_surface_t *take_snapshot = NULL;
+ cairo_int_status_t status;
+
+ surface_pattern = (cairo_surface_pattern_t *) pattern;
+ source = surface_pattern->surface;
+
+ if (_cairo_surface_is_snapshot (source)) {
+ snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
+ if (snapshot) {
+ _cairo_output_stream_printf (ctx->stream,
+ "s%d pattern ",
+ snapshot->unique_id);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (_cairo_surface_snapshot_is_reused (source))
+ take_snapshot = source;
+
+ free_me = source = _cairo_surface_snapshot_get_target (source);
+ }
+
+ switch ((int) source->backend->type) {
+ case CAIRO_SURFACE_TYPE_RECORDING:
+ status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
+ break;
+ case CAIRO_SURFACE_TYPE_SCRIPT:
+ status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
+ break;
+ case CAIRO_SURFACE_TYPE_SUBSURFACE:
+ status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source);
+ break;
+ default:
+ status = _emit_image_surface_pattern (surface, source);
+ break;
+ }
+ cairo_surface_destroy (free_me);
+ if (unlikely (status))
+ return status;
+
+ if (take_snapshot)
+ attach_snapshot (ctx, take_snapshot);
+
+ _cairo_output_stream_puts (ctx->stream, "pattern");
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_raster_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *source;
+ cairo_int_status_t status;
+
+ source = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
+ if (unlikely (source == NULL)) {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (unlikely (source->status))
+ return source->status;
+
+ status = _emit_image_surface_pattern (surface, source);
+ _cairo_raster_source_pattern_release (pattern, source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (to_context(surface)->stream, "pattern");
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_int_status_t status;
+ cairo_bool_t is_default_extend;
+ cairo_bool_t need_newline = TRUE;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ /* solid colors do not need filter/extend/matrix */
+ return _emit_solid_pattern (surface, pattern);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ status = _emit_linear_pattern (surface, pattern);
+ is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _emit_radial_pattern (surface, pattern);
+ is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
+ break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ status = _emit_mesh_pattern (surface, pattern);
+ is_default_extend = TRUE;
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ status = _emit_surface_pattern (surface, pattern);
+ is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ status = _emit_raster_pattern (surface, pattern);
+ is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_matrix_is_identity (&pattern->matrix)) {
+ if (need_newline) {
+ _cairo_output_stream_puts (ctx->stream, "\n ");
+ need_newline = FALSE;
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ " [%f %f %f %f %f %f] set-matrix\n ",
+ pattern->matrix.xx, pattern->matrix.yx,
+ pattern->matrix.xy, pattern->matrix.yy,
+ pattern->matrix.x0, pattern->matrix.y0);
+ }
+
+ /* XXX need to discriminate the user explicitly setting the default */
+ if (pattern->filter != CAIRO_FILTER_DEFAULT) {
+ if (need_newline) {
+ _cairo_output_stream_puts (ctx->stream, "\n ");
+ need_newline = FALSE;
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ " //%s set-filter\n ",
+ _filter_to_string (pattern->filter));
+ }
+ if (! is_default_extend ){
+ if (need_newline) {
+ _cairo_output_stream_puts (ctx->stream, "\n ");
+ need_newline = FALSE;
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ " //%s set-extend\n ",
+ _extend_to_string (pattern->extend));
+ }
+
+ if (need_newline)
+ _cairo_output_stream_puts (ctx->stream, "\n ");
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_identity (cairo_script_surface_t *surface,
+ cairo_bool_t *matrix_updated)
+{
+ assert (target_is_active (surface));
+
+ if (_cairo_matrix_is_identity (&surface->cr.current_ctm))
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ _cairo_output_stream_puts (to_context (surface)->stream,
+ "identity set-matrix\n");
+
+ *matrix_updated = TRUE;
+ cairo_matrix_init_identity (&surface->cr.current_ctm);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_source (cairo_script_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source)
+{
+ cairo_bool_t matrix_updated = FALSE;
+ cairo_int_status_t status;
+
+ assert (target_is_active (surface));
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ /* the source is ignored, so don't change it */
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (_cairo_pattern_equal (&surface->cr.current_source.base, source))
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ _cairo_pattern_fini (&surface->cr.current_source.base);
+ status = _cairo_pattern_init_copy (&surface->cr.current_source.base,
+ source);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_identity (surface, &matrix_updated);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_pattern (surface, source);
+ if (unlikely (status))
+ return status;
+
+ assert (target_is_active (surface));
+ _cairo_output_stream_puts (to_context (surface)->stream,
+ " set-source\n");
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ _cairo_output_stream_printf (closure,
+ " %f %f m",
+ _cairo_fixed_to_double (point->x),
+ _cairo_fixed_to_double (point->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ _cairo_output_stream_printf (closure,
+ " %f %f l",
+ _cairo_fixed_to_double (point->x),
+ _cairo_fixed_to_double (point->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_curve_to (void *closure,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ const cairo_point_t *p3)
+{
+ _cairo_output_stream_printf (closure,
+ " %f %f %f %f %f %f c",
+ _cairo_fixed_to_double (p1->x),
+ _cairo_fixed_to_double (p1->y),
+ _cairo_fixed_to_double (p2->x),
+ _cairo_fixed_to_double (p2->y),
+ _cairo_fixed_to_double (p3->x),
+ _cairo_fixed_to_double (p3->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_close (void *closure)
+{
+ _cairo_output_stream_printf (closure,
+ " h");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_path_boxes (cairo_script_surface_t *surface,
+ const cairo_path_fixed_t *path)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_path_fixed_iter_t iter;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ struct _cairo_boxes_chunk *chunk;
+ cairo_boxes_t boxes;
+ cairo_box_t box;
+ int i;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_path_fixed_iter_init (&iter, path);
+ while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+ if (box.p1.y == box.p2.y || box.p1.x == box.p2.x)
+ continue;
+
+ status = _cairo_boxes_add (&boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&boxes);
+ return status;
+ }
+ }
+
+ if (! _cairo_path_fixed_iter_at_end (&iter)) {
+ _cairo_boxes_fini (&boxes);
+ return FALSE;
+ }
+
+ for (chunk = &boxes.chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ const cairo_box_t *b = &chunk->base[i];
+ double x1 = _cairo_fixed_to_double (b->p1.x);
+ double y1 = _cairo_fixed_to_double (b->p1.y);
+ double x2 = _cairo_fixed_to_double (b->p2.x);
+ double y2 = _cairo_fixed_to_double (b->p2.y);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %f %f %f %f rectangle",
+ x1, y1, x2 - x1, y2 - y1);
+ }
+ }
+
+ _cairo_boxes_fini (&boxes);
+ return status;
+}
+
+static cairo_status_t
+_emit_path (cairo_script_surface_t *surface,
+ const cairo_path_fixed_t *path,
+ cairo_bool_t is_fill)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_box_t box;
+ cairo_int_status_t status;
+
+ assert (target_is_active (surface));
+ assert (_cairo_matrix_is_identity (&surface->cr.current_ctm));
+
+ if (_cairo_path_fixed_equal (&surface->cr.current_path, path))
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_path_fixed_fini (&surface->cr.current_path);
+
+ _cairo_output_stream_puts (ctx->stream, "n");
+
+ if (path == NULL) {
+ _cairo_path_fixed_init (&surface->cr.current_path);
+ _cairo_output_stream_puts (ctx->stream, "\n");
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
+ if (unlikely (status))
+ return status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_is_rectangle (path, &box)) {
+ double x1 = _cairo_fixed_to_double (box.p1.x);
+ double y1 = _cairo_fixed_to_double (box.p1.y);
+ double x2 = _cairo_fixed_to_double (box.p2.x);
+ double y2 = _cairo_fixed_to_double (box.p2.y);
+
+ assert (x1 > -9999);
+
+ _cairo_output_stream_printf (ctx->stream,
+ " %f %f %f %f rectangle",
+ x1, y1, x2 - x1, y2 - y1);
+ status = CAIRO_INT_STATUS_SUCCESS;
+ } else if (is_fill && _cairo_path_fixed_fill_is_rectilinear (path)) {
+ status = _emit_path_boxes (surface, path);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_path_fixed_interpret (path,
+ _path_move_to,
+ _path_line_to,
+ _path_curve_to,
+ _path_close,
+ ctx->stream);
+ }
+
+ _cairo_output_stream_puts (ctx->stream, "\n");
+
+ return status;
+}
+static cairo_bool_t
+_scaling_matrix_equal (const cairo_matrix_t *a,
+ const cairo_matrix_t *b)
+{
+ return fabs (a->xx - b->xx) < 1e-5 &&
+ fabs (a->xy - b->xy) < 1e-5 &&
+ fabs (a->yx - b->yx) < 1e-5 &&
+ fabs (a->yy - b->yy) < 1e-5;
+}
+
+static cairo_status_t
+_emit_scaling_matrix (cairo_script_surface_t *surface,
+ const cairo_matrix_t *ctm,
+ cairo_bool_t *matrix_updated)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_bool_t was_identity;
+ assert (target_is_active (surface));
+
+ if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm))
+ return CAIRO_STATUS_SUCCESS;
+
+ was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm);
+
+ *matrix_updated = TRUE;
+ surface->cr.current_ctm = *ctm;
+ surface->cr.current_ctm.x0 = 0.;
+ surface->cr.current_ctm.y0 = 0.;
+
+ if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) {
+ _cairo_output_stream_puts (ctx->stream,
+ "identity set-matrix\n");
+ } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) {
+ _cairo_output_stream_printf (ctx->stream,
+ "%f %f scale\n",
+ ctm->xx, ctm->yy);
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "[%f %f %f %f 0 0] set-matrix\n",
+ ctm->xx, ctm->yx,
+ ctm->xy, ctm->yy);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_font_matrix (cairo_script_surface_t *surface,
+ const cairo_matrix_t *font_matrix)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ assert (target_is_active (surface));
+
+ if (memcmp (&surface->cr.current_font_matrix,
+ font_matrix,
+ sizeof (cairo_matrix_t)) == 0)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ surface->cr.current_font_matrix = *font_matrix;
+
+ if (_cairo_matrix_is_identity (font_matrix)) {
+ _cairo_output_stream_puts (ctx->stream,
+ "identity set-font-matrix\n");
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "[%f %f %f %f %f %f] set-font-matrix\n",
+ font_matrix->xx, font_matrix->yx,
+ font_matrix->xy, font_matrix->yy,
+ font_matrix->x0, font_matrix->y0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_script_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_script_surface_t *surface, *other = abstract_surface;
+ cairo_surface_t *passthrough = NULL;
+ cairo_script_context_t *ctx;
+ cairo_rectangle_t extents;
+ cairo_status_t status;
+
+ ctx = to_context (other);
+
+ status = cairo_device_acquire (&ctx->base);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (! other->emitted) {
+ status = _emit_surface (other);
+ if (unlikely (status)) {
+ cairo_device_release (&ctx->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ target_push (other);
+ }
+
+ if (_cairo_surface_wrapper_is_active (&other->wrapper)) {
+ passthrough =
+ _cairo_surface_wrapper_create_similar (&other->wrapper,
+ content, width, height);
+ if (unlikely (passthrough->status)) {
+ cairo_device_release (&ctx->base);
+ return passthrough;
+ }
+ }
+
+ extents.x = extents.y = 0;
+ extents.width = width;
+ extents.height = height;
+ surface = _cairo_script_surface_create_internal (ctx, content,
+ &extents, passthrough);
+ cairo_surface_destroy (passthrough);
+
+ if (unlikely (surface->base.status)) {
+ cairo_device_release (&ctx->base);
+ return &surface->base;
+ }
+
+ _get_target (other);
+ _cairo_output_stream_printf (ctx->stream,
+ "%u %u //%s similar dup /s%u exch def context\n",
+ width, height,
+ _content_to_string (content),
+ surface->base.unique_id);
+
+ surface->emitted = TRUE;
+ surface->defined = TRUE;
+ surface->base.is_clear = TRUE;
+ target_push (surface);
+
+ cairo_device_release (&ctx->base);
+ return &surface->base;
+}
+
+static cairo_status_t
+_device_flush (void *abstract_device)
+{
+ cairo_script_context_t *ctx = abstract_device;
+
+ return _cairo_output_stream_flush (ctx->stream);
+}
+
+static void
+_device_destroy (void *abstract_device)
+{
+ cairo_script_context_t *ctx = abstract_device;
+ cairo_status_t status;
+
+ while (! cairo_list_is_empty (&ctx->fonts)) {
+ cairo_script_font_t *font;
+
+ font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link);
+ cairo_list_del (&font->base.link);
+ cairo_list_del (&font->link);
+ free (font);
+ }
+
+ _bitmap_fini (ctx->surface_id.next);
+ _bitmap_fini (ctx->font_id.next);
+
+ if (ctx->owns_stream)
+ status = _cairo_output_stream_destroy (ctx->stream);
+
+ free (ctx);
+}
+
+static cairo_surface_t *
+_cairo_script_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+
+ if (extents) {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+static cairo_status_t
+_cairo_script_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+ return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper,
+ image_out,
+ image_extra);
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_cairo_script_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+
+ assert (_cairo_surface_wrapper_is_active (&surface->wrapper));
+ _cairo_surface_wrapper_release_source_image (&surface->wrapper,
+ image,
+ image_extra);
+}
+
+static cairo_status_t
+_cairo_script_surface_finish (void *abstract_surface)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
+
+ _cairo_surface_wrapper_fini (&surface->wrapper);
+
+ free (surface->cr.current_style.dash);
+ surface->cr.current_style.dash = NULL;
+
+ _cairo_pattern_fini (&surface->cr.current_source.base);
+ _cairo_path_fixed_fini (&surface->cr.current_path);
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ status = cairo_device_acquire (&ctx->base);
+ if (unlikely (status))
+ return status;
+
+ if (surface->emitted) {
+ assert (! surface->active);
+
+ if (! cairo_list_is_empty (&surface->operand.link)) {
+ if (! ctx->active) {
+ if (target_is_active (surface)) {
+ _cairo_output_stream_printf (ctx->stream,
+ "pop\n");
+ } else {
+ int depth = target_depth (surface);
+ if (depth == 1) {
+ _cairo_output_stream_printf (ctx->stream,
+ "exch pop\n");
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "%d -1 roll pop\n",
+ depth);
+ }
+ }
+ cairo_list_del (&surface->operand.link);
+ } else {
+ struct deferred_finish *link = malloc (sizeof (*link));
+ if (link == NULL) {
+ status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+ cairo_list_del (&surface->operand.link);
+ } else {
+ link->operand.type = DEFERRED;
+ cairo_list_swap (&link->operand.link,
+ &surface->operand.link);
+ cairo_list_add (&link->link, &ctx->deferred);
+ }
+ }
+ }
+
+ if (surface->defined) {
+ _cairo_output_stream_printf (ctx->stream,
+ "/s%u undef\n",
+ surface->base.unique_id);
+ }
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_output_stream_flush (to_context (surface)->stream);
+
+ cairo_device_release (&ctx->base);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_copy_page (void *abstract_surface)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n");
+
+BAIL:
+ cairo_device_release (surface->base.device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_show_page (void *abstract_surface)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n");
+
+BAIL:
+ cairo_device_release (surface->base.device);
+ return status;
+}
+
+static cairo_status_t
+_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_script_surface_t *surface = cairo_container_of (clipper,
+ cairo_script_surface_t,
+ clipper);
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_bool_t matrix_updated = FALSE;
+ cairo_status_t status;
+ cairo_box_t box;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ return status;
+
+ if (path == NULL) {
+ if (surface->cr.has_clip) {
+ _cairo_output_stream_puts (ctx->stream, "reset-clip\n");
+ surface->cr.has_clip = FALSE;
+ }
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* skip the trivial clip covering the surface extents */
+ if (surface->width >= 0 && surface->height >= 0 &&
+ _cairo_path_fixed_is_box (path, &box))
+ {
+ if (box.p1.x <= 0 && box.p1.y <= 0 &&
+ box.p2.x >= _cairo_fixed_from_double (surface->width) &&
+ box.p2.y >= _cairo_fixed_from_double (surface->height))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ status = _emit_identity (surface, &matrix_updated);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_fill_rule (surface, fill_rule);
+ if (unlikely (status))
+ return status;
+
+ if (path->has_curve_to) {
+ status = _emit_tolerance (surface, tolerance, matrix_updated);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (! _cairo_path_fixed_fill_maybe_region (path)) {
+ status = _emit_antialias (surface, antialias);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _emit_path (surface, path, TRUE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (ctx->stream, "clip+\n");
+ surface->cr.has_clip = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+active (cairo_script_surface_t *surface)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (surface->active++ == 0)
+ to_context (surface)->active++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+inactive (cairo_script_surface_t *surface)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_list_t sorted;
+
+ assert (surface->active > 0);
+ if (--surface->active)
+ goto DONE;
+
+ assert (ctx->active > 0);
+ if (--ctx->active)
+ goto DONE;
+
+ cairo_list_init (&sorted);
+ while (! cairo_list_is_empty (&ctx->deferred)) {
+ struct deferred_finish *df;
+ cairo_list_t *operand;
+ int depth;
+
+ df = cairo_list_first_entry (&ctx->deferred,
+ struct deferred_finish,
+ link);
+
+ depth = 0;
+ cairo_list_foreach (operand, &ctx->operands) {
+ if (operand == &df->operand.link)
+ break;
+ depth++;
+ }
+
+ df->operand.type = depth;
+
+ if (cairo_list_is_empty (&sorted)) {
+ cairo_list_move (&df->link, &sorted);
+ } else {
+ struct deferred_finish *pos;
+
+ cairo_list_foreach_entry (pos, struct deferred_finish,
+ &sorted,
+ link)
+ {
+ if (df->operand.type < pos->operand.type)
+ break;
+ }
+ cairo_list_move_tail (&df->link, &pos->link);
+ }
+ }
+
+ while (! cairo_list_is_empty (&sorted)) {
+ struct deferred_finish *df;
+ cairo_list_t *operand;
+ int depth;
+
+ df = cairo_list_first_entry (&sorted,
+ struct deferred_finish,
+ link);
+
+ depth = 0;
+ cairo_list_foreach (operand, &ctx->operands) {
+ if (operand == &df->operand.link)
+ break;
+ depth++;
+ }
+
+ if (depth == 0) {
+ _cairo_output_stream_printf (ctx->stream,
+ "pop\n");
+ } else if (depth == 1) {
+ _cairo_output_stream_printf (ctx->stream,
+ "exch pop\n");
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "%d -1 roll pop\n",
+ depth);
+ }
+
+ cairo_list_del (&df->operand.link);
+ cairo_list_del (&df->link);
+ free (df);
+ }
+
+DONE:
+ cairo_device_release (surface->base.device);
+}
+
+static cairo_int_status_t
+_cairo_script_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = active (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_source (surface, op, source);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_operator (surface, op);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_output_stream_puts (to_context (surface)->stream,
+ "paint\n");
+
+ inactive (surface);
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+ return _cairo_surface_wrapper_paint (&surface->wrapper,
+ op, source, clip);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ inactive (surface);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = active (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_source (surface, op, source);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_operator (surface, op);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (_cairo_pattern_equal (source, mask)) {
+ _cairo_output_stream_puts (to_context (surface)->stream, "/source get");
+ } else {
+ status = _emit_pattern (surface, mask);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ assert (surface->cr.current_operator == op);
+
+ _cairo_output_stream_puts (to_context (surface)->stream,
+ " mask\n");
+
+ inactive (surface);
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+ return _cairo_surface_wrapper_mask (&surface->wrapper,
+ op, source, mask, clip);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ inactive (surface);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_bool_t matrix_updated = FALSE;
+ cairo_status_t status;
+
+ status = active (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_identity (surface, &matrix_updated);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_path (surface, path, FALSE);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_source (surface, op, source);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_scaling_matrix (surface, ctm, &matrix_updated);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_operator (surface, op);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (_scaling_matrix_equal (&surface->cr.current_ctm,
+ &surface->cr.current_stroke_matrix))
+ {
+ matrix_updated = FALSE;
+ }
+ else
+ {
+ matrix_updated = TRUE;
+ surface->cr.current_stroke_matrix = surface->cr.current_ctm;
+ }
+
+ status = _emit_stroke_style (surface, style, matrix_updated);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_tolerance (surface, tolerance, matrix_updated);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_antialias (surface, antialias);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n");
+
+ inactive (surface);
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+ return _cairo_surface_wrapper_stroke (&surface->wrapper,
+ op, source, path,
+ style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ inactive (surface);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_bool_t matrix_updated = FALSE;
+ cairo_status_t status;
+ cairo_box_t box;
+
+ status = active (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_identity (surface, &matrix_updated);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_source (surface, op, source);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (! _cairo_path_fixed_is_box (path, &box)) {
+ status = _emit_fill_rule (surface, fill_rule);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ if (path->has_curve_to) {
+ status = _emit_tolerance (surface, tolerance, matrix_updated);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ if (! _cairo_path_fixed_fill_maybe_region (path)) {
+ status = _emit_antialias (surface, antialias);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ status = _emit_path (surface, path, TRUE);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_operator (surface, op);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n");
+
+ inactive (surface);
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+ return _cairo_surface_wrapper_fill (&surface->wrapper,
+ op, source, path,
+ fill_rule,
+ tolerance,
+ antialias,
+ clip);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ inactive (surface);
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_script_surface_snapshot (void *abstract_surface)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper))
+ return _cairo_surface_wrapper_snapshot (&surface->wrapper);
+
+ return NULL;
+}
+
+static cairo_bool_t
+_cairo_script_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ return TRUE;
+}
+
+static const char *
+_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order)
+{
+ static const char *names[] = {
+ "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */
+ "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */
+ "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */
+ "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */
+ "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */
+ };
+ return names[subpixel_order];
+}
+static const char *
+_hint_style_to_string (cairo_hint_style_t hint_style)
+{
+ static const char *names[] = {
+ "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */
+ "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */
+ "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */
+ "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */
+ "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */
+ };
+ return names[hint_style];
+}
+static const char *
+_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics)
+{
+ static const char *names[] = {
+ "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */
+ "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */
+ "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */
+ };
+ return names[hint_metrics];
+}
+
+static cairo_status_t
+_emit_font_options (cairo_script_surface_t *surface,
+ cairo_font_options_t *font_options)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+
+ if (cairo_font_options_equal (&surface->cr.current_font_options,
+ font_options))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_output_stream_printf (ctx->stream, "<<");
+
+ if (font_options->antialias != surface->cr.current_font_options.antialias) {
+ _cairo_output_stream_printf (ctx->stream,
+ " /antialias //%s",
+ _antialias_to_string (font_options->antialias));
+ }
+
+ if (font_options->subpixel_order !=
+ surface->cr.current_font_options.subpixel_order)
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ " /subpixel-order //%s",
+ _subpixel_order_to_string (font_options->subpixel_order));
+ }
+
+ if (font_options->hint_style !=
+ surface->cr.current_font_options.hint_style)
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ " /hint-style //%s",
+ _hint_style_to_string (font_options->hint_style));
+ }
+
+ if (font_options->hint_metrics !=
+ surface->cr.current_font_options.hint_metrics)
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ " /hint-metrics //%s",
+ _hint_metrics_to_string (font_options->hint_metrics));
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ " >> set-font-options\n");
+
+ surface->cr.current_font_options = *font_options;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_script_font_t *priv = (cairo_script_font_t *)abstract_private;
+ cairo_script_context_t *ctx = (cairo_script_context_t *)abstract_private->key;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&ctx->base);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_output_stream_printf (ctx->stream,
+ "/f%lu undef /sf%lu undef\n",
+ priv->id,
+ priv->id);
+
+ _bitmap_release_id (&ctx->font_id, priv->id);
+ cairo_device_release (&ctx->base);
+ }
+
+ cairo_list_del (&priv->link);
+ cairo_list_del (&priv->base.link);
+ free (priv);
+}
+
+static cairo_script_font_t *
+_cairo_script_font_get (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
+{
+ return (cairo_script_font_t *) _cairo_scaled_font_find_private (font, ctx);
+}
+
+static long unsigned
+_cairo_script_font_id (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
+{
+ return _cairo_script_font_get (ctx, font)->id;
+}
+
+static cairo_status_t
+_emit_type42_font (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ const cairo_scaled_font_backend_t *backend;
+ cairo_output_stream_t *base85_stream;
+ cairo_output_stream_t *zlib_stream;
+ cairo_status_t status, status2;
+ unsigned long size;
+ unsigned int load_flags;
+ uint32_t len;
+ uint8_t *buf;
+
+ backend = scaled_font->backend;
+ if (backend->load_truetype_table == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
+ if (unlikely (status))
+ return status;
+
+ buf = malloc (size);
+ if (unlikely (buf == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
+ if (unlikely (status)) {
+ free (buf);
+ return status;
+ }
+
+#if CAIRO_HAS_FT_FONT
+ load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font);
+#else
+ load_flags = 0;
+#endif
+ _cairo_output_stream_printf (ctx->stream,
+ "<< "
+ "/type 42 "
+ "/index 0 "
+ "/flags %d "
+ "/source <|",
+ load_flags);
+
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ len = to_be32 (size);
+ _cairo_output_stream_write (base85_stream, &len, sizeof (len));
+
+ zlib_stream = _cairo_deflate_stream_create (base85_stream);
+
+ _cairo_output_stream_write (zlib_stream, buf, size);
+ free (buf);
+
+ status2 = _cairo_output_stream_destroy (zlib_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "~> >> font dup /f%lu exch def set-font-face",
+ _cairo_script_font_id (ctx, scaled_font));
+
+ return status;
+}
+
+static cairo_status_t
+_emit_scaled_font_init (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_script_font_t **font_out)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_font_t *font_private;
+ cairo_int_status_t status;
+
+ font_private = malloc (sizeof (cairo_script_font_t));
+ if (unlikely (font_private == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_scaled_font_attach_private (scaled_font, &font_private->base, ctx,
+ _cairo_script_scaled_font_fini);
+
+ font_private->parent = scaled_font;
+ font_private->subset_glyph_index = 0;
+ font_private->has_sfnt = TRUE;
+
+ cairo_list_add (&font_private->link, &ctx->fonts);
+
+ status = _bitmap_next_id (&ctx->font_id,
+ &font_private->id);
+ if (unlikely (status)) {
+ free (font_private);
+ return status;
+ }
+
+ status = _emit_context (surface);
+ if (unlikely (status)) {
+ free (font_private);
+ return status;
+ }
+
+ status = _emit_type42_font (surface, scaled_font);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ *font_out = font_private;
+ return status;
+ }
+
+ font_private->has_sfnt = FALSE;
+ _cairo_output_stream_printf (ctx->stream,
+ "dict\n"
+ " /type 3 set\n"
+ " /metrics [%f %f %f %f %f] set\n"
+ " /glyphs array set\n"
+ " font dup /f%lu exch def set-font-face",
+ scaled_font->fs_extents.ascent,
+ scaled_font->fs_extents.descent,
+ scaled_font->fs_extents.height,
+ scaled_font->fs_extents.max_x_advance,
+ scaled_font->fs_extents.max_y_advance,
+ font_private->id);
+
+ *font_out = font_private;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_font (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_matrix_t matrix;
+ cairo_font_options_t options;
+ cairo_bool_t matrix_updated = FALSE;
+ cairo_status_t status;
+ cairo_script_font_t *font_private;
+
+ cairo_scaled_font_get_ctm (scaled_font, &matrix);
+ status = _emit_scaling_matrix (surface, &matrix, &matrix_updated);
+ if (unlikely (status))
+ return status;
+
+ if (! matrix_updated && surface->cr.current_scaled_font == scaled_font)
+ return CAIRO_STATUS_SUCCESS;
+
+ surface->cr.current_scaled_font = scaled_font;
+
+ font_private = _cairo_script_font_get (ctx, scaled_font);
+ if (font_private == NULL) {
+ cairo_scaled_font_get_font_matrix (scaled_font, &matrix);
+ status = _emit_font_matrix (surface, &matrix);
+ if (unlikely (status))
+ return status;
+
+ cairo_scaled_font_get_font_options (scaled_font, &options);
+ status = _emit_font_options (surface, &options);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_scaled_font_init (surface, scaled_font, &font_private);
+ if (unlikely (status))
+ return status;
+
+ assert (target_is_active (surface));
+ _cairo_output_stream_printf (ctx->stream,
+ " /scaled-font get /sf%lu exch def\n",
+ font_private->id);
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "sf%lu set-scaled-font\n",
+ font_private->id);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_glyph_vector (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_script_font_t *font_private,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_implicit_context_t old_cr;
+ cairo_status_t status;
+ unsigned long index;
+
+ index = ++font_private->subset_glyph_index;
+ scaled_glyph->dev_private_key = ctx;
+ scaled_glyph->dev_private = (void *) index;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "%lu <<\n"
+ " /metrics [%f %f %f %f %f %f]\n"
+ " /render {\n",
+ index,
+ scaled_glyph->fs_metrics.x_bearing,
+ scaled_glyph->fs_metrics.y_bearing,
+ scaled_glyph->fs_metrics.width,
+ scaled_glyph->fs_metrics.height,
+ scaled_glyph->fs_metrics.x_advance,
+ scaled_glyph->fs_metrics.y_advance);
+
+ if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) {
+ _cairo_output_stream_printf (ctx->stream,
+ "[%f %f %f %f %f %f] transform\n",
+ scaled_font->scale_inverse.xx,
+ scaled_font->scale_inverse.yx,
+ scaled_font->scale_inverse.xy,
+ scaled_font->scale_inverse.yy,
+ scaled_font->scale_inverse.x0,
+ scaled_font->scale_inverse.y0);
+ }
+
+ old_cr = surface->cr;
+ _cairo_script_implicit_context_init (&surface->cr);
+ status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
+ &surface->base);
+ surface->cr = old_cr;
+
+ _cairo_output_stream_puts (ctx->stream, "} >> set\n");
+
+ return status;
+}
+
+static cairo_status_t
+_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_script_font_t *font_private,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_status_t status;
+ unsigned long index;
+
+ index = ++font_private->subset_glyph_index;
+ scaled_glyph->dev_private_key = ctx;
+ scaled_glyph->dev_private = (void *) index;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "%lu <<\n"
+ " /metrics [%f %f %f %f %f %f]\n"
+ " /render {\n"
+ "%f %f translate\n",
+ index,
+ scaled_glyph->fs_metrics.x_bearing,
+ scaled_glyph->fs_metrics.y_bearing,
+ scaled_glyph->fs_metrics.width,
+ scaled_glyph->fs_metrics.height,
+ scaled_glyph->fs_metrics.x_advance,
+ scaled_glyph->fs_metrics.y_advance,
+ scaled_glyph->fs_metrics.x_bearing,
+ scaled_glyph->fs_metrics.y_bearing);
+
+ status = _emit_image_surface (surface, scaled_glyph->surface);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (ctx->stream, "pattern ");
+
+ if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) {
+ _cairo_output_stream_printf (ctx->stream,
+ "\n [%f %f %f %f %f %f] set-matrix\n",
+ scaled_font->font_matrix.xx,
+ scaled_font->font_matrix.yx,
+ scaled_font->font_matrix.xy,
+ scaled_font->font_matrix.yy,
+ scaled_font->font_matrix.x0,
+ scaled_font->font_matrix.y0);
+ }
+ _cairo_output_stream_puts (ctx->stream,
+ "mask\n} >> set\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_glyph_prologue (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+
+ _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n",
+ _cairo_script_font_id (ctx, scaled_font));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_glyphs (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ unsigned int num_glyphs)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_font_t *font_private;
+ cairo_status_t status;
+ unsigned int n;
+ cairo_bool_t have_glyph_prologue = FALSE;
+
+ if (num_glyphs == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ font_private = _cairo_script_font_get (ctx, scaled_font);
+ if (font_private == NULL)
+ return (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+
+ if (font_private->has_sfnt)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ for (n = 0; n < num_glyphs; n++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status))
+ break;
+
+ if (scaled_glyph->dev_private_key == ctx)
+ continue;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
+ &scaled_glyph);
+ if (_cairo_status_is_error (status))
+ break;
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ if (! have_glyph_prologue) {
+ status = _emit_scaled_glyph_prologue (surface, scaled_font);
+ if (unlikely (status))
+ break;
+
+ have_glyph_prologue = TRUE;
+ }
+
+ status = _emit_scaled_glyph_vector (surface,
+ scaled_font, font_private,
+ scaled_glyph);
+ if (unlikely (status))
+ break;
+
+ continue;
+ }
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (_cairo_status_is_error (status))
+ break;
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ if (! have_glyph_prologue) {
+ status = _emit_scaled_glyph_prologue (surface, scaled_font);
+ if (unlikely (status))
+ break;
+
+ have_glyph_prologue = TRUE;
+ }
+
+ status = _emit_scaled_glyph_bitmap (surface,
+ scaled_font,
+ font_private,
+ scaled_glyph);
+ if (unlikely (status))
+ break;
+
+ continue;
+ }
+ }
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ if (have_glyph_prologue) {
+ _cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n");
+ }
+
+ return status;
+}
+
+static void
+to_octal (int value, char *buf, size_t size)
+{
+ do {
+ buf[--size] = '0' + (value & 7);
+ value >>= 3;
+ } while (size);
+}
+
+static void
+_emit_string_literal (cairo_script_surface_t *surface,
+ const char *utf8, int len)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ char c;
+ const char *end;
+
+ _cairo_output_stream_puts (ctx->stream, "(");
+
+ if (utf8 == NULL) {
+ end = utf8;
+ } else {
+ if (len < 0)
+ len = strlen (utf8);
+ end = utf8 + len;
+ }
+
+ while (utf8 < end) {
+ switch ((c = *utf8++)) {
+ case '\n':
+ c = 'n';
+ goto ESCAPED_CHAR;
+ case '\r':
+ c = 'r';
+ goto ESCAPED_CHAR;
+ case '\t':
+ c = 't';
+ goto ESCAPED_CHAR;
+ case '\b':
+ c = 'b';
+ goto ESCAPED_CHAR;
+ case '\f':
+ c = 'f';
+ goto ESCAPED_CHAR;
+ case '\\':
+ case '(':
+ case ')':
+ESCAPED_CHAR:
+ _cairo_output_stream_printf (ctx->stream, "\\%c", c);
+ break;
+ default:
+ if (isprint (c) || isspace (c)) {
+ _cairo_output_stream_printf (ctx->stream, "%c", c);
+ } else {
+ char buf[4] = { '\\' };
+
+ to_octal (c, buf+1, 3);
+ _cairo_output_stream_write (ctx->stream, buf, 4);
+ }
+ break;
+ }
+ }
+ _cairo_output_stream_puts (ctx->stream, ")");
+}
+
+static cairo_int_status_t
+_cairo_script_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t backward,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_font_t *font_private;
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_matrix_t matrix;
+ cairo_status_t status;
+ double x, y, ix, iy;
+ int n;
+ cairo_output_stream_t *base85_stream = NULL;
+
+ status = active (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_source (surface, op, source);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_scaled_font (surface, scaled_font);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_operator (surface, op);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs);
+ if (unlikely (status))
+ goto BAIL;
+
+ /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */
+ /* [cx cy [glyphs]] show_glyphs */
+
+ if (utf8 != NULL && clusters != NULL) {
+ _emit_string_literal (surface, utf8, utf8_len);
+ _cairo_output_stream_puts (ctx->stream, " ");
+ }
+
+ matrix = surface->cr.current_ctm;
+ status = cairo_matrix_invert (&matrix);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ ix = x = glyphs[0].x;
+ iy = y = glyphs[0].y;
+ cairo_matrix_transform_point (&matrix, &ix, &iy);
+ ix -= scaled_font->font_matrix.x0;
+ iy -= scaled_font->font_matrix.y0;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ font_private = _cairo_script_font_get (ctx, scaled_font);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "[%f %f ",
+ ix, iy);
+
+ for (n = 0; n < num_glyphs; n++) {
+ if (font_private->has_sfnt) {
+ if (glyphs[n].index > 256)
+ break;
+ } else {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status)) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ goto BAIL;
+ }
+
+ if ((long unsigned) scaled_glyph->dev_private > 256)
+ break;
+ }
+ }
+
+ if (n == num_glyphs) {
+ _cairo_output_stream_puts (ctx->stream, "<~");
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ } else
+ _cairo_output_stream_puts (ctx->stream, "[");
+
+ for (n = 0; n < num_glyphs; n++) {
+ double dx, dy;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status)) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ goto BAIL;
+ }
+
+ if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) {
+ if (fabs (glyphs[n].y - y) < 1e-5) {
+ if (base85_stream != NULL) {
+ status = _cairo_output_stream_destroy (base85_stream);
+ if (unlikely (status)) {
+ base85_stream = NULL;
+ break;
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ "~> %f <~", glyphs[n].x - x);
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ " ] %f [ ", glyphs[n].x - x);
+ }
+
+ x = glyphs[n].x;
+ } else {
+ ix = x = glyphs[n].x;
+ iy = y = glyphs[n].y;
+ cairo_matrix_transform_point (&matrix, &ix, &iy);
+ ix -= scaled_font->font_matrix.x0;
+ iy -= scaled_font->font_matrix.y0;
+ if (base85_stream != NULL) {
+ status = _cairo_output_stream_destroy (base85_stream);
+ if (unlikely (status)) {
+ base85_stream = NULL;
+ break;
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ "~> %f %f <~",
+ ix, iy);
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ " ] %f %f [ ",
+ ix, iy);
+ }
+ }
+ }
+ if (base85_stream != NULL) {
+ uint8_t c;
+
+ if (font_private->has_sfnt)
+ c = glyphs[n].index;
+ else
+ c = (uint8_t) (long unsigned) scaled_glyph->dev_private;
+
+ _cairo_output_stream_write (base85_stream, &c, 1);
+ } else {
+ if (font_private->has_sfnt)
+ _cairo_output_stream_printf (ctx->stream, " %lu",
+ glyphs[n].index);
+ else
+ _cairo_output_stream_printf (ctx->stream, " %lu",
+ (long unsigned) scaled_glyph->dev_private);
+ }
+
+ dx = scaled_glyph->metrics.x_advance;
+ dy = scaled_glyph->metrics.y_advance;
+ cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy);
+ x += dx;
+ y += dy;
+ }
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ if (base85_stream != NULL) {
+ cairo_status_t status2;
+
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ _cairo_output_stream_printf (ctx->stream, "~>");
+ } else {
+ _cairo_output_stream_puts (ctx->stream, " ]");
+ }
+ if (unlikely (status))
+ return status;
+
+ if (utf8 != NULL && clusters != NULL) {
+ for (n = 0; n < num_clusters; n++) {
+ if (clusters[n].num_bytes > UCHAR_MAX ||
+ clusters[n].num_glyphs > UCHAR_MAX)
+ {
+ break;
+ }
+ }
+
+ if (n < num_clusters) {
+ _cairo_output_stream_puts (ctx->stream, "] [ ");
+ for (n = 0; n < num_clusters; n++) {
+ _cairo_output_stream_printf (ctx->stream,
+ "%d %d ",
+ clusters[n].num_bytes,
+ clusters[n].num_glyphs);
+ }
+ _cairo_output_stream_puts (ctx->stream, "]");
+ }
+ else
+ {
+ _cairo_output_stream_puts (ctx->stream, "] <~");
+ base85_stream = _cairo_base85_stream_create (ctx->stream);
+ for (n = 0; n < num_clusters; n++) {
+ uint8_t c[2];
+ c[0] = clusters[n].num_bytes;
+ c[1] = clusters[n].num_glyphs;
+ _cairo_output_stream_write (base85_stream, c, 2);
+ }
+ status = _cairo_output_stream_destroy (base85_stream);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_output_stream_puts (ctx->stream, "~>");
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ " //%s show-text-glyphs\n",
+ _direction_to_string (backward));
+ } else {
+ _cairo_output_stream_puts (ctx->stream,
+ "] show-glyphs\n");
+ }
+
+ inactive (surface);
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
+ return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper,
+ op, source,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ backward,
+ scaled_font,
+ clip);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ inactive (surface);
+ return status;
+}
+
+static cairo_bool_t
+_cairo_script_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+
+ if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+ return _cairo_surface_wrapper_get_extents (&surface->wrapper,
+ rectangle);
+ }
+
+ if (surface->width < 0 || surface->height < 0)
+ return FALSE;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static const cairo_surface_backend_t
+_cairo_script_surface_backend = {
+ CAIRO_SURFACE_TYPE_SCRIPT,
+ _cairo_script_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_script_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_script_surface_source,
+ _cairo_script_surface_acquire_source_image,
+ _cairo_script_surface_release_source_image,
+ _cairo_script_surface_snapshot,
+
+ _cairo_script_surface_copy_page,
+ _cairo_script_surface_show_page,
+
+ _cairo_script_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_script_surface_paint,
+ _cairo_script_surface_mask,
+ _cairo_script_surface_stroke,
+ _cairo_script_surface_fill,
+ NULL, /* fill/stroke */
+ NULL, /* glyphs */
+ _cairo_script_surface_has_show_text_glyphs,
+ _cairo_script_surface_show_text_glyphs
+};
+
+static void
+_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
+{
+ cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT;
+ cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
+ cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
+ cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT;
+ _cairo_stroke_style_init (&cr->current_style);
+ _cairo_pattern_init_solid (&cr->current_source.solid,
+ CAIRO_COLOR_BLACK);
+ _cairo_path_fixed_init (&cr->current_path);
+ cairo_matrix_init_identity (&cr->current_ctm);
+ cairo_matrix_init_identity (&cr->current_stroke_matrix);
+ cairo_matrix_init_identity (&cr->current_font_matrix);
+ _cairo_font_options_init_default (&cr->current_font_options);
+ cr->current_scaled_font = NULL;
+ cr->has_clip = FALSE;
+}
+
+static void
+_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
+{
+ free (cr->current_style.dash);
+ cr->current_style.dash = NULL;
+
+ _cairo_pattern_fini (&cr->current_source.base);
+ _cairo_path_fixed_fini (&cr->current_path);
+
+ _cairo_script_implicit_context_init (cr);
+}
+
+static cairo_script_surface_t *
+_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
+ cairo_content_t content,
+ cairo_rectangle_t *extents,
+ cairo_surface_t *passthrough)
+{
+ cairo_script_surface_t *surface;
+
+ if (unlikely (ctx == NULL))
+ return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+
+ surface = 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 = surface->height = -1;
+ if (extents) {
+ surface->width = extents->width;
+ surface->height = extents->height;
+ cairo_surface_set_device_offset (&surface->base,
+ -extents->x, -extents->y);
+ }
+
+ surface->emitted = FALSE;
+ surface->defined = FALSE;
+ surface->active = FALSE;
+ surface->operand.type = SURFACE;
+ cairo_list_init (&surface->operand.link);
+
+ _cairo_script_implicit_context_init (&surface->cr);
+
+ return surface;
+}
+
+static const cairo_device_backend_t _cairo_script_device_backend = {
+ CAIRO_DEVICE_TYPE_SCRIPT,
+
+ NULL, NULL, /* lock, unlock */
+
+ _device_flush, /* flush */
+ NULL, /* finish */
+ _device_destroy
+};
+
+cairo_device_t *
+_cairo_script_context_create_internal (cairo_output_stream_t *stream)
+{
+ cairo_script_context_t *ctx;
+
+ ctx = malloc (sizeof (cairo_script_context_t));
+ if (unlikely (ctx == NULL))
+ return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (ctx, 0, sizeof (cairo_script_context_t));
+
+ _cairo_device_init (&ctx->base, &_cairo_script_device_backend);
+
+ cairo_list_init (&ctx->operands);
+ cairo_list_init (&ctx->deferred);
+ ctx->stream = stream;
+ ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
+
+ cairo_list_init (&ctx->fonts);
+ cairo_list_init (&ctx->defines);
+
+ ctx->attach_snapshots = TRUE;
+
+ return &ctx->base;
+}
+
+void
+_cairo_script_context_attach_snapshots (cairo_device_t *device,
+ cairo_bool_t enable)
+{
+ cairo_script_context_t *ctx;
+
+ ctx = (cairo_script_context_t *) device;
+ ctx->attach_snapshots = enable;
+}
+
+static cairo_device_t *
+_cairo_script_context_create (cairo_output_stream_t *stream)
+{
+ cairo_script_context_t *ctx;
+
+ ctx = (cairo_script_context_t *)
+ _cairo_script_context_create_internal (stream);
+ if (unlikely (ctx->base.status))
+ return &ctx->base;
+
+ ctx->owns_stream = TRUE;
+ _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
+ return &ctx->base;
+}
+
+/**
+ * cairo_script_create:
+ * @filename: the name (path) of the file to write the script to
+ *
+ * Creates a output device for emitting the script, used when
+ * creating the individual surfaces.
+ *
+ * Return value: a pointer to the newly created device. The caller
+ * owns the surface and should call cairo_device_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" device if an error such as out of memory
+ * occurs. You can use cairo_device_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_device_t *
+cairo_script_create (const char *filename)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ stream = _cairo_output_stream_create_for_filename (filename);
+ if ((status = _cairo_output_stream_get_status (stream))) {
+ _cairo_output_stream_destroy (stream);
+ return _cairo_device_create_in_error (status);
+ }
+
+ return _cairo_script_context_create (stream);
+}
+
+/**
+ * cairo_script_create_for_stream:
+ * @write_func: callback function passed the bytes written to the script
+ * @closure: user data to be passed to the callback
+ *
+ * Creates a output device for emitting the script, used when
+ * creating the individual surfaces.
+ *
+ * Return value: a pointer to the newly created device. The caller
+ * owns the surface and should call cairo_device_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" device if an error such as out of memory
+ * occurs. You can use cairo_device_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_device_t *
+cairo_script_create_for_stream (cairo_write_func_t write_func,
+ void *closure)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ stream = _cairo_output_stream_create (write_func, NULL, closure);
+ if ((status = _cairo_output_stream_get_status (stream))) {
+ _cairo_output_stream_destroy (stream);
+ return _cairo_device_create_in_error (status);
+ }
+
+ return _cairo_script_context_create (stream);
+}
+
+/**
+ * cairo_script_write_comment:
+ * @script: the script (output device)
+ * @comment: the string to emit
+ * @len:the length of the sting to write, or -1 to use strlen()
+ *
+ * Emit a string verbatim into the script.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_script_write_comment (cairo_device_t *script,
+ const char *comment,
+ int len)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) script;
+
+ if (len < 0)
+ len = strlen (comment);
+
+ _cairo_output_stream_puts (context->stream, "% ");
+ _cairo_output_stream_write (context->stream, comment, len);
+ _cairo_output_stream_puts (context->stream, "\n");
+}
+
+/**
+ * cairo_script_set_mode:
+ * @script: The script (output device)
+ * @mode: the new mode
+ *
+ * Change the output mode of the script
+ *
+ * Since: 1.12
+ **/
+void
+cairo_script_set_mode (cairo_device_t *script,
+ cairo_script_mode_t mode)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) script;
+
+ context->mode = mode;
+}
+
+/**
+ * cairo_script_get_mode:
+ * @script: The script (output device) to query
+ *
+ * Queries the script for its current output mode.
+ *
+ * Return value: the current output mode of the script
+ *
+ * Since: 1.12
+ **/
+cairo_script_mode_t
+cairo_script_get_mode (cairo_device_t *script)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) script;
+
+ return context->mode;
+}
+
+/**
+ * cairo_script_surface_create:
+ * @script: the script (output device)
+ * @content: the content of the surface
+ * @width: width in pixels
+ * @height: height in pixels
+ *
+ * Create a new surface that will emit its rendering through @script
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_script_surface_create (cairo_device_t *script,
+ cairo_content_t content,
+ double width,
+ double height)
+{
+ cairo_rectangle_t *extents, r;
+
+ if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (script->status))
+ return _cairo_surface_create_in_error (script->status);
+
+ extents = NULL;
+ if (width > 0 && height > 0) {
+ r.x = r.y = 0;
+ r.width = width;
+ r.height = height;
+ extents = &r;
+ }
+ return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
+ content, extents,
+ NULL)->base;
+}
+slim_hidden_def (cairo_script_surface_create);
+
+/**
+ * cairo_script_surface_create_for_target:
+ * @script: the script (output device)
+ * @target: a target surface to wrap
+ *
+ * Create a pxoy surface that will render to @target and record
+ * the operations to @device.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_device_t *script,
+ cairo_surface_t *target)
+{
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_t rect, *r;
+
+ if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (script->status))
+ return _cairo_surface_create_in_error (script->status);
+
+ if (unlikely (target->status))
+ return _cairo_surface_create_in_error (target->status);
+
+ r = NULL;
+ if (_cairo_surface_get_extents (target, &extents)) {
+ rect.x = rect.y = 0;
+ rect.width = extents.width;
+ rect.height = extents.height;
+ r= &rect;
+ }
+ return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
+ target->content, r,
+ target)->base;
+}
+
+/**
+ * cairo_script_from_recording_surface:
+ * @script: the script (output device)
+ * @recording_surface: the recording surface to replay
+ *
+ * Converts the record operations in @recording_surface into a script.
+ *
+ * Return value: #CAIRO_STATUS_SUCCESS on successful completion or an error code.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_script_from_recording_surface (cairo_device_t *script,
+ cairo_surface_t *recording_surface)
+{
+ cairo_rectangle_t r, *extents;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (script->status))
+ return _cairo_error (script->status);
+
+ if (unlikely (recording_surface->status))
+ return recording_surface->status;
+
+ if (unlikely (! _cairo_surface_is_recording (recording_surface)))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ extents = NULL;
+ if (_cairo_recording_surface_get_bounds (recording_surface, &r))
+ extents = &r;
+
+ surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
+ recording_surface->content,
+ extents,
+ NULL)->base;
+ if (unlikely (surface->status)) {
+ status = surface->status;
+ cairo_surface_destroy (surface);
+ return status;
+ }
+
+ status = _cairo_recording_surface_replay (recording_surface, surface);
+ cairo_surface_destroy (surface);
+
+ return status;
+}
diff --git a/src/cairo-script.h b/src/cairo-script.h
new file mode 100755
index 000000000..b5a8cf32d
--- /dev/null
+++ b/src/cairo-script.h
@@ -0,0 +1,98 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SCRIPT_H
+#define CAIRO_SCRIPT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+/**
+ * cairo_script_mode_t:
+ * @CAIRO_SCRIPT_MODE_ASCII: the output will be in readable text (default). (Since 1.12)
+ * @CAIRO_SCRIPT_MODE_BINARY: the output will use byte codes. (Since 1.12)
+ *
+ * A set of script output variants.
+ *
+ * Since: 1.12
+ **/
+typedef enum {
+ CAIRO_SCRIPT_MODE_ASCII,
+ CAIRO_SCRIPT_MODE_BINARY
+} cairo_script_mode_t;
+
+cairo_public cairo_device_t *
+cairo_script_create (const char *filename);
+
+cairo_public cairo_device_t *
+cairo_script_create_for_stream (cairo_write_func_t write_func,
+ void *closure);
+
+cairo_public void
+cairo_script_write_comment (cairo_device_t *script,
+ const char *comment,
+ int len);
+
+cairo_public void
+cairo_script_set_mode (cairo_device_t *script,
+ cairo_script_mode_t mode);
+
+cairo_public cairo_script_mode_t
+cairo_script_get_mode (cairo_device_t *script);
+
+cairo_public cairo_surface_t *
+cairo_script_surface_create (cairo_device_t *script,
+ cairo_content_t content,
+ double width,
+ double height);
+
+cairo_public cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_device_t *script,
+ cairo_surface_t *target);
+
+cairo_public cairo_status_t
+cairo_script_from_recording_surface (cairo_device_t *script,
+ cairo_surface_t *recording_surface);
+
+CAIRO_END_DECLS
+
+#else /*CAIRO_HAS_SCRIPT_SURFACE*/
+# error Cairo was not compiled with support for the CairoScript backend
+#endif /*CAIRO_HAS_SCRIPT_SURFACE*/
+
+#endif /*CAIRO_SCRIPT_H*/
diff --git a/src/cairo-shape-mask-compositor.c b/src/cairo-shape-mask-compositor.c
new file mode 100755
index 000000000..c2425b08c
--- /dev/null
+++ b/src/cairo-shape-mask-compositor.c
@@ -0,0 +1,337 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-surface-offset-private.h"
+
+static cairo_int_status_t
+_cairo_shape_mask_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_surface_t *mask;
+ cairo_surface_pattern_t pattern;
+ cairo_int_status_t status;
+ cairo_clip_t *clip;
+
+ if (! extents->is_bounded)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ mask = _cairo_surface_create_similar_scratch (extents->surface,
+ CAIRO_CONTENT_ALPHA,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ clip = extents->clip;
+ if (! _cairo_clip_is_region (clip))
+ clip = _cairo_clip_copy_region (clip);
+
+ if (! mask->is_clear) {
+ status = _cairo_surface_offset_paint (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ clip);
+ if (unlikely (status))
+ goto error;
+ }
+
+ status = _cairo_surface_offset_stroke (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (unlikely (status))
+ goto error;
+
+ if (clip != extents->clip) {
+ status = _cairo_clip_combine_with_surface (extents->clip, mask,
+ extents->bounded.x,
+ extents->bounded.y);
+ if (unlikely (status))
+ goto error;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, mask);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ pattern.base.extend = CAIRO_EXTEND_NONE;
+ if (extents->op == CAIRO_OPERATOR_SOURCE) {
+ status = _cairo_surface_mask (extents->surface,
+ CAIRO_OPERATOR_DEST_OUT,
+ &_cairo_pattern_white.base,
+ &pattern.base,
+ clip);
+ if ((status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _cairo_surface_mask (extents->surface,
+ CAIRO_OPERATOR_ADD,
+ &extents->source_pattern.base,
+ &pattern.base,
+ clip);
+ }
+ } else {
+ status = _cairo_surface_mask (extents->surface,
+ extents->op,
+ &extents->source_pattern.base,
+ &pattern.base,
+ clip);
+ }
+ _cairo_pattern_fini (&pattern.base);
+
+error:
+ cairo_surface_destroy (mask);
+ if (clip != extents->clip)
+ _cairo_clip_destroy (clip);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_shape_mask_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_surface_t *mask;
+ cairo_surface_pattern_t pattern;
+ cairo_int_status_t status;
+ cairo_clip_t *clip;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (! extents->is_bounded)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ mask = _cairo_surface_create_similar_scratch (extents->surface,
+ CAIRO_CONTENT_ALPHA,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ clip = extents->clip;
+ if (! _cairo_clip_is_region (clip))
+ clip = _cairo_clip_copy_region (clip);
+
+ if (! mask->is_clear) {
+ status = _cairo_surface_offset_paint (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ clip);
+ if (unlikely (status))
+ goto error;
+ }
+
+ status = _cairo_surface_offset_fill (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ path, fill_rule, tolerance, antialias,
+ clip);
+ if (unlikely (status))
+ goto error;
+
+ if (clip != extents->clip) {
+ status = _cairo_clip_combine_with_surface (extents->clip, mask,
+ extents->bounded.x,
+ extents->bounded.y);
+ if (unlikely (status))
+ goto error;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, mask);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ pattern.base.extend = CAIRO_EXTEND_NONE;
+ if (extents->op == CAIRO_OPERATOR_SOURCE) {
+ status = _cairo_surface_mask (extents->surface,
+ CAIRO_OPERATOR_DEST_OUT,
+ &_cairo_pattern_white.base,
+ &pattern.base,
+ clip);
+ if ((status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _cairo_surface_mask (extents->surface,
+ CAIRO_OPERATOR_ADD,
+ &extents->source_pattern.base,
+ &pattern.base,
+ clip);
+ }
+ } else {
+ status = _cairo_surface_mask (extents->surface,
+ extents->op,
+ &extents->source_pattern.base,
+ &pattern.base,
+ clip);
+ }
+ _cairo_pattern_fini (&pattern.base);
+
+error:
+ if (clip != extents->clip)
+ _cairo_clip_destroy (clip);
+ cairo_surface_destroy (mask);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_shape_mask_compositor_glyphs (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_surface_t *mask;
+ cairo_surface_pattern_t pattern;
+ cairo_int_status_t status;
+ cairo_clip_t *clip;
+
+ if (! extents->is_bounded)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ mask = _cairo_surface_create_similar_scratch (extents->surface,
+ CAIRO_CONTENT_ALPHA,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ clip = extents->clip;
+ if (! _cairo_clip_is_region (clip))
+ clip = _cairo_clip_copy_region (clip);
+
+ if (! mask->is_clear) {
+ status = _cairo_surface_offset_paint (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ clip);
+ if (unlikely (status))
+ goto error;
+ }
+
+ status = _cairo_surface_offset_glyphs (mask,
+ extents->bounded.x,
+ extents->bounded.y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ scaled_font, glyphs, num_glyphs,
+ clip);
+ if (unlikely (status))
+ goto error;
+
+ if (clip != extents->clip) {
+ status = _cairo_clip_combine_with_surface (extents->clip, mask,
+ extents->bounded.x,
+ extents->bounded.y);
+ if (unlikely (status))
+ goto error;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, mask);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ pattern.base.extend = CAIRO_EXTEND_NONE;
+ if (extents->op == CAIRO_OPERATOR_SOURCE) {
+ status = _cairo_surface_mask (extents->surface,
+ CAIRO_OPERATOR_DEST_OUT,
+ &_cairo_pattern_white.base,
+ &pattern.base,
+ clip);
+ if ((status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _cairo_surface_mask (extents->surface,
+ CAIRO_OPERATOR_ADD,
+ &extents->source_pattern.base,
+ &pattern.base,
+ clip);
+ }
+ } else {
+ status = _cairo_surface_mask (extents->surface,
+ extents->op,
+ &extents->source_pattern.base,
+ &pattern.base,
+ clip);
+ }
+ _cairo_pattern_fini (&pattern.base);
+
+error:
+ if (clip != extents->clip)
+ _cairo_clip_destroy (clip);
+ cairo_surface_destroy (mask);
+ return status;
+}
+
+void
+_cairo_shape_mask_compositor_init (cairo_compositor_t *compositor,
+ const cairo_compositor_t *delegate)
+{
+ compositor->delegate = delegate;
+
+ compositor->paint = NULL;
+ compositor->mask = NULL;
+ compositor->fill = _cairo_shape_mask_compositor_fill;
+ compositor->stroke = _cairo_shape_mask_compositor_stroke;
+ compositor->glyphs = _cairo_shape_mask_compositor_glyphs;
+}
diff --git a/src/cairo-skia-surface.cpp b/src/cairo-skia-surface.cpp
new file mode 100755
index 000000000..bf6b14a5e
--- /dev/null
+++ b/src/cairo-skia-surface.cpp
@@ -0,0 +1,1175 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* 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 Corporation.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-skia.h"
+
+#include "cairo-surface-clipper-private.h"
+#include "cairo-image-surface-inline.h"
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+
+#include <SkColorShader.h>
+
+#include <SkGradientShader.h>
+#include <SkDashPathEffect.h>
+
+#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED)
+# define CAIRO_FIXED_TO_SK_SCALAR(x) (x)
+#elif defined(SK_SCALAR_IS_FIXED)
+/* This can be done better, but this will do for now */
+# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x))
+#else
+# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x))
+#endif
+
+#ifndef CAIRO_INT_STATUS_SUCCESS
+# define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS)
+#endif
+
+#define CAIRO_MAYBE_UNSUPPORTED CAIRO_INT_STATUS_UNSUPPORTED
+//#define CAIRO_MAYBE_UNSUPPORTED _skia_unsupported ()
+
+static cairo_int_status_t _skia_unsupported () {
+ printf ("unsupported!\n");
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+typedef struct cairo_skia_surface {
+ cairo_surface_t base;
+
+ SkBitmap *bitmap;
+ SkCanvas *canvas;
+
+ cairo_surface_clipper_t clipper;
+
+ cairo_image_surface_t *_image_surface; /* wrapper around bitmap */
+} cairo_skia_surface_t;
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+ bool opaque,
+ unsigned char *data,
+ int width,
+ int height,
+ int stride);
+
+/*
+ * conversion methods
+ */
+
+/*
+ * format conversion
+ */
+static inline bool
+format_to_sk_config (cairo_format_t format,
+ SkBitmap::Config& config,
+ bool& opaque)
+{
+ opaque = false;
+
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ config = SkBitmap::kARGB_8888_Config;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ config = SkBitmap::kARGB_8888_Config;
+ opaque = true;
+ break;
+ case CAIRO_FORMAT_A8:
+ config = SkBitmap::kA8_Config;
+ break;
+ case CAIRO_FORMAT_A1:
+ config = SkBitmap::kA1_Config;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static inline cairo_format_t
+sk_config_to_format (SkBitmap::Config config,
+ bool opaque)
+{
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ if (opaque)
+ return CAIRO_FORMAT_RGB24;
+ return CAIRO_FORMAT_ARGB32;
+
+ case SkBitmap::kA8_Config:
+ return CAIRO_FORMAT_A8;
+
+ case SkBitmap::kA1_Config:
+ return CAIRO_FORMAT_A1;
+
+ case SkBitmap::kNo_Config:
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kRLE_Index8_Config:
+ case SkBitmap::kRGB_565_Config:
+ case SkBitmap::kARGB_4444_Config:
+ case SkBitmap::kConfigCount:
+ default:
+ return (cairo_format_t) -1;
+ }
+}
+
+/*
+ * image surface wrapping
+ */
+static inline bool
+surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap)
+{
+ cairo_image_surface_t *img = (cairo_image_surface_t *) surface;
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (unlikely (! format_to_sk_config (img->format, config, opaque)))
+ return false;
+
+ bitmap.reset ();
+ bitmap.setConfig (config, img->width, img->height, img->stride);
+ bitmap.setIsOpaque (opaque);
+ bitmap.setPixels (img->data);
+
+ return true;
+}
+
+/*
+ * operator conversion
+ */
+
+static inline SkXfermode::Mode
+operator_to_sk (cairo_operator_t op)
+{
+ static const SkXfermode::Mode modeMap[] = {
+ SkXfermode::kClear_Mode,
+
+ SkXfermode::kSrc_Mode,
+ SkXfermode::kSrcOver_Mode,
+ SkXfermode::kSrcIn_Mode,
+ SkXfermode::kSrcOut_Mode,
+ SkXfermode::kSrcATop_Mode,
+
+ SkXfermode::kDst_Mode,
+ SkXfermode::kDstOver_Mode,
+ SkXfermode::kDstIn_Mode,
+ SkXfermode::kDstOut_Mode,
+ SkXfermode::kDstATop_Mode,
+
+ SkXfermode::kXor_Mode,
+ SkXfermode::kPlus_Mode, // XXX Add?
+ SkXfermode::kPlus_Mode, // XXX SATURATE
+
+ SkXfermode::kPlus_Mode,
+ SkXfermode::kMultiply_Mode,
+ SkXfermode::kScreen_Mode,
+ SkXfermode::kOverlay_Mode,
+ SkXfermode::kDarken_Mode,
+ SkXfermode::kLighten_Mode,
+ SkXfermode::kColorDodge_Mode,
+ SkXfermode::kColorBurn_Mode,
+ SkXfermode::kHardLight_Mode,
+ SkXfermode::kSoftLight_Mode,
+ SkXfermode::kDifference_Mode,
+ SkXfermode::kExclusion_Mode,
+
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION,
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR,
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY
+ };
+
+ return modeMap[op];
+}
+
+/*
+ * tiling mode conversion
+ */
+static SkShader::TileMode
+extend_to_sk (cairo_extend_t extend)
+{
+ static const SkShader::TileMode modeMap[] = {
+ SkShader::kClamp_TileMode, // NONE behaves like PAD, because noone wants NONE
+ SkShader::kRepeat_TileMode,
+ SkShader::kMirror_TileMode,
+ SkShader::kClamp_TileMode
+ };
+
+ return modeMap[extend];
+}
+
+/*
+ * color conversion
+ */
+static inline SkColor
+color_to_sk (const cairo_color_t& c)
+{
+ /* Need unpremultiplied 1-byte values */
+ return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+ (U8CPU) (c.red * 255),
+ (U8CPU) (c.green * 255),
+ (U8CPU) (c.blue * 255));
+}
+
+/*
+ * matrix conversion
+ */
+static inline SkMatrix
+matrix_to_sk (const cairo_matrix_t& mat)
+{
+ SkMatrix skm;
+
+ skm.reset ();
+ skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx));
+ skm.set (SkMatrix::kMSkewX, SkFloatToScalar (mat.xy));
+ skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0));
+ skm.set (SkMatrix::kMSkewY, SkFloatToScalar (mat.yx));
+ skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy));
+ skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0));
+
+ /*
+ skm[6] = SkFloatToScalar (0.0);
+ skm[7] = SkFloatToScalar (0.0);
+ skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself. It wants Sk_Fract1 (2.30), not Sk_Scalar1
+ */
+
+ return skm;
+}
+
+static inline SkMatrix
+matrix_inverse_to_sk (const cairo_matrix_t& mat)
+{
+ cairo_matrix_t inv = mat;
+ cairo_status_t status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ return matrix_to_sk (inv);
+}
+
+/*
+ * pattern conversion
+ */
+static inline cairo_surface_t *
+surface_from_pattern (const cairo_pattern_t *pattern)
+{
+ return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface;
+}
+
+static SkShader*
+pattern_to_sk_shader (cairo_skia_surface_t *dst, const cairo_pattern_t *pattern,
+ cairo_image_surface_t **image, void **image_extra)
+{
+ SkShader *shader = NULL;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+ return new SkColorShader (color_to_sk (solid->color));
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_t *surface = surface_from_pattern (pattern);
+
+ if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+
+ shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+ extend_to_sk (pattern->extend),
+ extend_to_sk (pattern->extend));
+ } else {
+ SkBitmap bitmap;
+
+ if (! _cairo_surface_is_image (surface)) {
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (surface,
+ image, image_extra);
+ if (status)
+ return NULL;
+
+ surface = &(*image)->base;
+ }
+
+
+ if (unlikely (! surface_to_sk_bitmap (surface, bitmap)))
+ return NULL;
+
+ shader = SkShader::CreateBitmapShader (bitmap,
+ extend_to_sk (pattern->extend),
+ extend_to_sk (pattern->extend));
+ }
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR
+ /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */)
+ {
+ cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+ SkColor colors_stack[10];
+ SkScalar pos_stack[10];
+ SkColor *colors = colors_stack;
+ SkScalar *pos = pos_stack;
+
+ if (gradient->n_stops > 10) {
+ colors = new SkColor[gradient->n_stops];
+ pos = new SkScalar[gradient->n_stops];
+ }
+
+ for (unsigned int i = 0; i < gradient->n_stops; i++) {
+ pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
+ colors[i] = color_to_sk (gradient->stops[i].color);
+ }
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ SkPoint points[2];
+
+ points[0].set (SkFloatToScalar (linear->pd1.x),
+ SkFloatToScalar (linear->pd1.y));
+ points[1].set (SkFloatToScalar (linear->pd2.x),
+ SkFloatToScalar (linear->pd2.y));
+ shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
+ extend_to_sk (pattern->extend));
+ } else {
+ // XXX todo -- implement real radial shaders in Skia
+ }
+
+ if (gradient->n_stops > 10) {
+ delete [] colors;
+ delete [] pos;
+ }
+ }
+
+ if (shader && ! _cairo_matrix_is_identity (&pattern->matrix))
+ shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix));
+
+ return shader;
+}
+
+static inline bool
+pattern_filter_to_sk (const cairo_pattern_t *pattern)
+{
+ switch (pattern->filter) {
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return true;
+ default:
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ return false;
+ }
+}
+
+static inline bool
+pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color)
+{
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return false;
+
+ color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color);
+ return true;
+}
+
+/*
+ * path conversion
+ */
+
+struct cpc {
+ SkPath skPath;
+ cairo_matrix_t *matrix;
+};
+
+static cairo_status_t
+cpc_move_to (void *closure, const cairo_point_t *point)
+{
+ struct cpc *cpc = static_cast <struct cpc *> (closure);
+ if (cpc->matrix) {
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+ cairo_matrix_transform_point (cpc->matrix, &x, &y);
+ cpc->skPath.moveTo (SkFloatToScalar (x), SkFloatToScalar (y));
+ } else {
+ cpc->skPath.moveTo (CAIRO_FIXED_TO_SK_SCALAR (point->x),
+ CAIRO_FIXED_TO_SK_SCALAR (point->y));
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cpc_line_to (void *closure, const cairo_point_t *point)
+{
+ struct cpc *cpc = static_cast <struct cpc *> (closure);
+ if (cpc->matrix) {
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+ cairo_matrix_transform_point (cpc->matrix, &x, &y);
+ cpc->skPath.lineTo (SkFloatToScalar (x), SkFloatToScalar (y));
+ } else {
+ cpc->skPath.lineTo (CAIRO_FIXED_TO_SK_SCALAR (point->x),
+ CAIRO_FIXED_TO_SK_SCALAR (point->y));
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cpc_curve_to (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ struct cpc *cpc = static_cast <struct cpc *> (closure);
+ if (cpc->matrix) {
+ 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);
+ cairo_matrix_transform_point (cpc->matrix, &x0, &y0);
+ cairo_matrix_transform_point (cpc->matrix, &x1, &y1);
+ cairo_matrix_transform_point (cpc->matrix, &x2, &y2);
+
+ cpc->skPath.cubicTo (SkFloatToScalar (x0),
+ SkFloatToScalar (y0),
+ SkFloatToScalar (x1),
+ SkFloatToScalar (y1),
+ SkFloatToScalar (x2),
+ SkFloatToScalar (y2));
+ } else {
+ cpc->skPath.cubicTo (CAIRO_FIXED_TO_SK_SCALAR (p0->x),
+ CAIRO_FIXED_TO_SK_SCALAR (p0->y),
+ CAIRO_FIXED_TO_SK_SCALAR (p1->x),
+ CAIRO_FIXED_TO_SK_SCALAR (p1->y),
+ CAIRO_FIXED_TO_SK_SCALAR (p2->x),
+ CAIRO_FIXED_TO_SK_SCALAR (p2->y));
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cpc_close_path (void *closure)
+{
+ struct cpc *cpc = static_cast <struct cpc *> (closure);
+ cpc->skPath.close ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline SkPath
+path_to_sk (cairo_path_fixed_t *path,
+ cairo_matrix_t *mat = NULL)
+{
+ struct cpc data;
+ cairo_status_t status;
+
+ if (mat && _cairo_matrix_is_identity (mat))
+ mat = NULL;
+ data.matrix = mat;
+
+ status = _cairo_path_fixed_interpret (path,
+ cpc_move_to,
+ cpc_line_to,
+ cpc_curve_to,
+ cpc_close_path,
+ &data);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ return data.skPath;
+}
+
+static inline SkPath
+path_to_sk (cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_matrix_t *mat = NULL)
+{
+ SkPath skPath = path_to_sk (path, mat);
+
+ if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD)
+ skPath.setFillType (SkPath::kEvenOdd_FillType);
+ else
+ skPath.setFillType (SkPath::kWinding_FillType);
+
+ return skPath;
+}
+
+/*
+ * cairo surface methods
+ */
+
+static cairo_surface_t *
+_cairo_skia_surface_create_similar (void *asurface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (! format_to_sk_config (_cairo_format_from_content (content),
+ config, opaque))
+ {
+ _skia_unsupported ();
+ return NULL;
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque,
+ NULL,
+ width, height,
+ 0)->base;
+}
+
+static cairo_status_t
+_cairo_skia_surface_finish (void *asurface)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+ cairo_surface_destroy (&surface->_image_surface->base);
+
+ delete surface->canvas;
+ delete surface->bitmap;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_image_surface_t *
+_get_image_surface (cairo_skia_surface_t *surface)
+{
+ if (! surface->_image_surface) {
+ SkBitmap *bitmap = surface->bitmap;
+ surface->_image_surface = (cairo_image_surface_t *)
+ cairo_image_surface_create_for_data ((unsigned char *) bitmap->getPixels (),
+ sk_config_to_format (bitmap->config (),
+ bitmap->isOpaque ()),
+ bitmap->width (),
+ bitmap->height (),
+ bitmap->rowBytes ());
+ }
+
+ return surface->_image_surface;
+}
+
+static cairo_status_t
+_cairo_skia_surface_acquire_source_image (void *asurface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ cairo_image_surface_t *image = _get_image_surface (surface);
+
+ if (unlikely (image->base.status))
+ return image->base.status;
+
+ surface->bitmap->lockPixels ();
+
+ *image_out = image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_source_image (void *asurface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->unlockPixels ();
+}
+
+static cairo_status_t
+_cairo_skia_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_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ cairo_image_surface_t *image = _get_image_surface (surface);
+
+ if (unlikely (image->base.status))
+ return image->base.status;
+
+ image_rect->x = 0;
+ image_rect->y = 0;
+ image_rect->width = image->width;
+ image_rect->height = image->height;
+
+ surface->bitmap->lockPixels ();
+
+ *image_out = image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_dest_image (void *asurface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->notifyPixelsChanged ();
+ surface->bitmap->unlockPixels ();
+}
+
+#if 0
+static cairo_status_t
+_cairo_skia_surface_clone_similar (void *asurface,
+ 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)
+{
+ if (src->type == CAIRO_SURFACE_TYPE_SKIA || _cairo_surface_is_image (src)) {
+ *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;
+}
+#endif
+
+static cairo_status_t
+_cairo_skia_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_skia_surface_t *surface = cairo_container_of (clipper,
+ cairo_skia_surface_t,
+ clipper);
+
+ if (path == NULL) {
+ /* XXX TODO: teach Skia how to reset the clip path */
+ surface->canvas->restore ();
+ surface->canvas->save ();
+ } else {
+ surface->canvas->clipPath (path_to_sk (path, fill_rule));
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_skia_surface_get_extents (void *asurface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ extents->x = 0;
+ extents->y = 0;
+ extents->width = surface->bitmap->width ();
+ extents->height = surface->bitmap->height ();
+
+ return TRUE;
+}
+
+/*
+ * Core drawing operations
+ */
+
+static SkBitmap *
+pattern_to_sk_bitmap (cairo_skia_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ SkMatrix *matrix,
+ cairo_image_surface_t **image,
+ void **image_extra)
+{
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return NULL;
+
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ return NULL;
+
+ cairo_surface_t *surface = surface_from_pattern (pattern);
+ SkBitmap *bitmap;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+ bitmap = new SkBitmap (*((cairo_skia_surface_t *) surface)->bitmap);
+ } else {
+ if (surface->type != CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (surface,
+ image, image_extra);
+ if (unlikely (status))
+ return NULL;
+
+ surface = &(*image)->base;
+ }
+
+ bitmap = new SkBitmap;
+ if (unlikely (! surface_to_sk_bitmap (surface, *bitmap)))
+ return NULL;
+ }
+
+ *matrix = matrix_inverse_to_sk (pattern->matrix);
+ return bitmap;
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_paint (void *asurface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ cairo_image_surface_t *image = NULL;
+ cairo_status_t status;
+ void *image_extra;
+ SkColor color;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return (cairo_int_status_t) status;
+
+ if (pattern_to_sk_color (source, color)) {
+ surface->canvas->drawColor (color, operator_to_sk (op));
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ SkMatrix bitmapMatrix;
+ SkBitmap *bitmap = pattern_to_sk_bitmap (surface, source, &bitmapMatrix,
+ &image, &image_extra);
+ SkShader *shader = NULL;
+ if (!bitmap)
+ shader = pattern_to_sk_shader (surface, source, &image, &image_extra);
+
+ if (!bitmap && !shader)
+ return CAIRO_MAYBE_UNSUPPORTED;
+
+ SkPaint paint;
+ paint.setFilterBitmap (pattern_filter_to_sk (source));
+ paint.setXfermodeMode (operator_to_sk (op));
+
+ if (shader) {
+ paint.setShader (shader);
+ surface->canvas->drawPaint (paint);
+ } else {
+ surface->canvas->drawBitmapMatrix (*bitmap, bitmapMatrix, &paint);
+ }
+
+ if (bitmap)
+ delete bitmap;
+ if (shader)
+ shader->unref ();
+
+ if (image != NULL) {
+ _cairo_surface_release_source_image (&surface->base,
+ image, image_extra);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_stroke (void *asurface,
+ 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_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ cairo_image_surface_t *image = NULL;
+ cairo_status_t status;
+ void *image_extra;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return (cairo_int_status_t) status;
+
+ SkPaint paint;
+ paint.setStyle (SkPaint::kStroke_Style);
+
+ SkColor color;
+ if (pattern_to_sk_color (source, color)) {
+ paint.setColor (color);
+ } else {
+ SkShader *shader = pattern_to_sk_shader (surface,
+ source, &image, &image_extra);
+ if (shader == NULL)
+ return CAIRO_MAYBE_UNSUPPORTED;
+
+ paint.setShader (shader);
+ shader->unref ();
+
+ paint.setFilterBitmap (pattern_filter_to_sk (source));
+ }
+
+ paint.setXfermodeMode (operator_to_sk (op));
+ paint.setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+
+ /* Convert the various stroke rendering bits */
+ paint.setStrokeWidth (SkFloatToScalar (style->line_width));
+ paint.setStrokeMiter (SkFloatToScalar (style->miter_limit));
+
+ static const SkPaint::Cap capMap[] = {
+ SkPaint::kButt_Cap,
+ SkPaint::kRound_Cap,
+ SkPaint::kSquare_Cap
+ };
+ paint.setStrokeCap (capMap[style->line_cap]);
+
+ static const SkPaint::Join joinMap[] = {
+ SkPaint::kMiter_Join,
+ SkPaint::kRound_Join,
+ SkPaint::kBevel_Join
+ };
+ paint.setStrokeJoin (joinMap[style->line_join]);
+
+ /* If we have a dash pattern, we need to
+ * create a SkDashPathEffect and set it on the Paint.
+ */
+ if (style->dash != NULL) {
+ SkScalar intervals_static[20];
+ SkScalar *intervals = intervals_static;
+
+ int loop = 0;
+ unsigned int dash_count = style->num_dashes;
+ if ((dash_count & 1) != 0) {
+ loop = 1;
+ dash_count <<= 1;
+ }
+
+ if (dash_count > 20)
+ intervals = new SkScalar[dash_count];
+
+ unsigned int i = 0;
+ do {
+ for (unsigned int j = 0; i < style->num_dashes; j++)
+ intervals[i++] = SkFloatToScalar (style->dash[j]);
+ } while (loop--);
+
+ SkDashPathEffect *dash = new SkDashPathEffect (intervals,
+ dash_count,
+ SkFloatToScalar (style->dash_offset));
+
+ paint.setPathEffect (dash);
+ dash->unref ();
+ }
+
+ surface->canvas->save (SkCanvas::kMatrix_SaveFlag);
+ surface->canvas->concat (matrix_to_sk (*ctm));
+ surface->canvas->drawPath (path_to_sk (path, ctm_inverse), paint);
+ surface->canvas->restore ();
+
+ if (image != NULL) {
+ _cairo_surface_release_source_image (&surface->base,
+ image, image_extra);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_fill (void *asurface,
+ 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_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ cairo_image_surface_t *image = NULL;
+ cairo_status_t status;
+ void *image_extra;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return (cairo_int_status_t) status;
+
+
+ SkPaint paint;
+ paint.setStyle (SkPaint::kFill_Style);
+
+ SkColor color;
+ if (pattern_to_sk_color (source, color)) {
+ paint.setColor (color);
+ } else {
+ SkShader *shader = pattern_to_sk_shader (surface,
+ source, &image, &image_extra);
+ if (shader == NULL)
+ return CAIRO_MAYBE_UNSUPPORTED;
+
+ paint.setShader (shader);
+ shader->unref ();
+
+ paint.setFilterBitmap (pattern_filter_to_sk (source));
+ }
+
+ paint.setXfermodeMode (operator_to_sk (op));
+ paint.setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+
+ surface->canvas->drawPath (path_to_sk (path, fill_rule), paint);
+
+ if (image != NULL) {
+ _cairo_surface_release_source_image (&surface->base,
+ image, image_extra);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static const struct _cairo_surface_backend
+cairo_skia_surface_backend = {
+ CAIRO_SURFACE_TYPE_SKIA,
+ _cairo_skia_surface_create_similar,
+ _cairo_skia_surface_finish,
+ _cairo_skia_surface_acquire_source_image,
+ _cairo_skia_surface_release_source_image,
+ _cairo_skia_surface_acquire_dest_image,
+ _cairo_skia_surface_release_dest_image,
+
+ NULL, // _cairo_skia_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_skia_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_skia_surface_paint,
+ NULL, /* mask? */
+ _cairo_skia_surface_stroke,
+ _cairo_skia_surface_fill,
+ NULL, /* show_glyphs */
+
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* reset */
+};
+
+/*
+ * Surface constructors
+ */
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+ bool opaque,
+ unsigned char *data,
+ int width,
+ int height,
+ int stride)
+{
+ cairo_skia_surface_t *surface;
+ cairo_format_t format;
+
+ surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t));
+ if (surface == NULL)
+ return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (surface, 0, sizeof (cairo_skia_surface_t));
+
+ format = sk_config_to_format (config, opaque);
+ assert (format != -1);
+
+ _cairo_surface_init (&surface->base,
+ &cairo_skia_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_format (format));
+
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_skia_surface_clipper_intersect_clip_path);
+
+ surface->bitmap = new SkBitmap;
+ if (data == NULL)
+ stride = cairo_format_stride_for_width (format, width);
+ surface->bitmap->setConfig (config, width, height, stride);
+ surface->bitmap->setIsOpaque (opaque);
+ if (data != NULL)
+ surface->bitmap->setPixels (data);
+ else
+ surface->bitmap->allocPixels ();
+
+ surface->canvas = new SkCanvas (*surface->bitmap);
+ //surface->canvas->translate (SkIntToScalar (0), SkIntToScalar (height));
+ //surface->canvas->scale (SkIntToScalar (1), SkIntToScalar (-1));
+ surface->canvas->save ();
+
+ return surface;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+ int width,
+ int height)
+{
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (! CAIRO_FORMAT_VALID (format) ||
+ ! format_to_sk_config (format, config, opaque))
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque,
+ NULL,
+ width, height, 0)->base;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride)
+{
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (! CAIRO_FORMAT_VALID (format) ||
+ ! format_to_sk_config (format, config, opaque))
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque,
+ data,
+ width, height, stride)->base;
+}
+
+unsigned char *
+cairo_skia_surface_get_data (cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+ return NULL;
+
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ return (unsigned char *) esurf->bitmap->getPixels ();
+}
+
+cairo_format_t
+cairo_skia_surface_get_format (cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+ return (cairo_format_t) -1;
+
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ return sk_config_to_format (esurf->bitmap->config (),
+ esurf->bitmap->isOpaque ());
+}
+
+int
+cairo_skia_surface_get_width (cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+ return 0;
+
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ return esurf->bitmap->width ();
+}
+
+int
+cairo_skia_surface_get_height (cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+ return 0;
+
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ return esurf->bitmap->height ();
+}
+
+int
+cairo_skia_surface_get_stride (cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+ return 0;
+
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ return esurf->bitmap->rowBytes ();
+}
+
+cairo_surface_t *
+cairo_skia_surface_get_image (cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+ return NULL;
+
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ return &_get_image_surface (esurf)->base;
+}
+
+/*
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+*/
diff --git a/src/cairo-skia.h b/src/cairo-skia.h
new file mode 100755
index 000000000..99b928656
--- /dev/null
+++ b/src/cairo-skia.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_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_END_DECLS
+
+#else
+
+# error Cairo was not compiled with support for the Skia backend
+
+#endif
+
+#endif
diff --git a/src/cairo-slope-private.h b/src/cairo-slope-private.h
new file mode 100755
index 000000000..6a58c9f45
--- /dev/null
+++ b/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/src/cairo-slope.c b/src/cairo-slope.c
new file mode 100755
index 000000000..cc5f30cb0
--- /dev/null
+++ b/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/src/cairo-spans-compositor-private.h b/src/cairo-spans-compositor-private.h
new file mode 100755
index 000000000..0babebd26
--- /dev/null
+++ b/src/cairo-spans-compositor-private.h
@@ -0,0 +1,111 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SPANS_COMPOSITOR_PRIVATE_H
+#define CAIRO_SPANS_COMPOSITOR_PRIVATE_H
+
+#include "cairo-compositor-private.h"
+#include "cairo-types-private.h"
+#include "cairo-spans-private.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_abstract_span_renderer {
+ cairo_span_renderer_t base;
+ char data[4096];
+} cairo_abstract_span_renderer_t;
+
+struct cairo_spans_compositor {
+ cairo_compositor_t base;
+
+ unsigned int flags;
+#define CAIRO_SPANS_COMPOSITOR_HAS_LERP 0x1
+
+ /* pixel-aligned fast paths */
+ cairo_int_status_t (*fill_boxes) (void *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes);
+
+ cairo_int_status_t (*draw_image_boxes) (void *surface,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy);
+
+ cairo_int_status_t (*copy_boxes) (void *surface,
+ cairo_surface_t *src,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents,
+ int dx, int dy);
+
+ cairo_surface_t * (*pattern_to_surface) (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y);
+
+ cairo_int_status_t (*composite_boxes) (void *surface,
+ cairo_operator_t op,
+ cairo_surface_t *source,
+ cairo_surface_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents);
+
+ /* general shape masks using a span renderer */
+ cairo_int_status_t (*renderer_init) (cairo_abstract_span_renderer_t *renderer,
+ const cairo_composite_rectangles_t *extents,
+ cairo_antialias_t antialias,
+ cairo_bool_t needs_clip);
+
+ void (*renderer_fini) (cairo_abstract_span_renderer_t *renderer,
+ cairo_int_status_t status);
+};
+
+cairo_private void
+_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor,
+ const cairo_compositor_t *delegate);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SPANS_COMPOSITOR_PRIVATE_H */
diff --git a/src/cairo-spans-compositor.c b/src/cairo-spans-compositor.c
new file mode 100755
index 000000000..8580da383
--- /dev/null
+++ b/src/cairo-spans-compositor.c
@@ -0,0 +1,1187 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-region-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-observer-private.h"
+
+typedef struct {
+ cairo_polygon_t *polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+} composite_spans_info_t;
+
+static cairo_int_status_t
+composite_polygon (const cairo_spans_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias);
+
+static cairo_int_status_t
+composite_boxes (const cairo_spans_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes);
+
+static cairo_int_status_t
+clip_and_composite_polygon (const cairo_spans_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias);
+static cairo_surface_t *
+get_clip_surface (const cairo_spans_compositor_t *compositor,
+ cairo_surface_t *dst,
+ const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_composite_rectangles_t composite;
+ cairo_surface_t *surface;
+ cairo_box_t box;
+ cairo_polygon_t polygon;
+ const cairo_clip_path_t *clip_path;
+ cairo_antialias_t antialias;
+ cairo_fill_rule_t fill_rule;
+ cairo_int_status_t status;
+
+ assert (clip->path);
+
+ surface = _cairo_surface_create_similar_solid (dst,
+ CAIRO_CONTENT_ALPHA,
+ extents->width,
+ extents->height,
+ CAIRO_COLOR_TRANSPARENT);
+
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_polygon_init (&polygon, &box, 1);
+
+ clip_path = clip->path;
+ status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+ clip_path->tolerance,
+ &polygon);
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ polygon.num_limits = 0;
+
+ antialias = clip_path->antialias;
+ fill_rule = clip_path->fill_rule;
+
+ if (clip->boxes) {
+ cairo_polygon_t intersect;
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init_for_array (&tmp, clip->boxes, clip->num_boxes);
+ status= _cairo_polygon_init_boxes (&intersect, &tmp);
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ status = _cairo_polygon_intersect (&polygon, fill_rule,
+ &intersect, CAIRO_FILL_RULE_WINDING);
+ _cairo_polygon_fini (&intersect);
+
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ fill_rule = CAIRO_FILL_RULE_WINDING;
+ }
+
+ polygon.limits = NULL;
+ polygon.num_limits = 0;
+
+ clip_path = clip_path->prev;
+ while (clip_path) {
+ if (clip_path->antialias == antialias) {
+ cairo_polygon_t next;
+
+ _cairo_polygon_init (&next, NULL, 0);
+ status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+ clip_path->tolerance,
+ &next);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = _cairo_polygon_intersect (&polygon, fill_rule,
+ &next, clip_path->fill_rule);
+ _cairo_polygon_fini (&next);
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ fill_rule = CAIRO_FILL_RULE_WINDING;
+ }
+
+ clip_path = clip_path->prev;
+ }
+
+ _cairo_polygon_translate (&polygon, -extents->x, -extents->y);
+ status = _cairo_composite_rectangles_init_for_polygon (&composite, surface,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ &polygon,
+ NULL);
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ status = composite_polygon (compositor, &composite,
+ &polygon, fill_rule, antialias);
+ _cairo_composite_rectangles_fini (&composite);
+ _cairo_polygon_fini (&polygon);
+ if (unlikely (status))
+ goto error;
+
+ _cairo_polygon_init (&polygon, &box, 1);
+
+ clip_path = clip->path;
+ antialias = clip_path->antialias == CAIRO_ANTIALIAS_DEFAULT ? CAIRO_ANTIALIAS_NONE : CAIRO_ANTIALIAS_DEFAULT;
+ clip_path = clip_path->prev;
+ while (clip_path) {
+ if (clip_path->antialias == antialias) {
+ if (polygon.num_edges == 0) {
+ status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+ clip_path->tolerance,
+ &polygon);
+
+ fill_rule = clip_path->fill_rule;
+ polygon.limits = NULL;
+ polygon.num_limits = 0;
+ } else {
+ cairo_polygon_t next;
+
+ _cairo_polygon_init (&next, NULL, 0);
+ status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+ clip_path->tolerance,
+ &next);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = _cairo_polygon_intersect (&polygon, fill_rule,
+ &next, clip_path->fill_rule);
+ _cairo_polygon_fini (&next);
+ fill_rule = CAIRO_FILL_RULE_WINDING;
+ }
+ if (unlikely (status))
+ goto error;
+ }
+
+ clip_path = clip_path->prev;
+ }
+
+ if (polygon.num_edges) {
+ _cairo_polygon_translate (&polygon, -extents->x, -extents->y);
+ status = _cairo_composite_rectangles_init_for_polygon (&composite, surface,
+ CAIRO_OPERATOR_IN,
+ &_cairo_pattern_white.base,
+ &polygon,
+ NULL);
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ status = composite_polygon (compositor, &composite,
+ &polygon, fill_rule, antialias);
+ _cairo_composite_rectangles_fini (&composite);
+ _cairo_polygon_fini (&polygon);
+ if (unlikely (status))
+ goto error;
+ }
+
+ return surface;
+
+cleanup_polygon:
+ _cairo_polygon_fini (&polygon);
+error:
+ cairo_surface_destroy (surface);
+ return _cairo_int_surface_create_in_error (status);
+}
+
+static cairo_int_status_t
+fixup_unbounded_mask (const cairo_spans_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_composite_rectangles_t composite;
+ cairo_surface_t *clip;
+ cairo_int_status_t status;
+
+ TRACE((stderr, "%s\n", __FUNCTION__));
+
+ clip = get_clip_surface (compositor, extents->surface, extents->clip,
+ &extents->unbounded);
+ if (unlikely (clip->status)) {
+ if ((cairo_int_status_t)clip->status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ return CAIRO_STATUS_SUCCESS;
+
+ return clip->status;
+ }
+
+ status = _cairo_composite_rectangles_init_for_boxes (&composite,
+ extents->surface,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ boxes,
+ NULL);
+ if (unlikely (status))
+ goto cleanup_clip;
+
+ _cairo_pattern_init_for_surface (&composite.mask_pattern.surface, clip);
+ composite.mask_pattern.base.filter = CAIRO_FILTER_NEAREST;
+ composite.mask_pattern.base.extend = CAIRO_EXTEND_NONE;
+
+ status = composite_boxes (compositor, &composite, boxes);
+
+ _cairo_pattern_fini (&composite.mask_pattern.base);
+ _cairo_composite_rectangles_fini (&composite);
+
+cleanup_clip:
+ cairo_surface_destroy (clip);
+ return status;
+}
+
+static cairo_int_status_t
+fixup_unbounded_polygon (const cairo_spans_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_polygon_t polygon, intersect;
+ cairo_composite_rectangles_t composite;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+ cairo_int_status_t status;
+
+ TRACE((stderr, "%s\n", __FUNCTION__));
+
+ /* Can we treat the clip as a regular clear-polygon and use it to fill? */
+ status = _cairo_clip_get_polygon (extents->clip, &polygon,
+ &fill_rule, &antialias);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status= _cairo_polygon_init_boxes (&intersect, boxes);
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ status = _cairo_polygon_intersect (&polygon, fill_rule,
+ &intersect, CAIRO_FILL_RULE_WINDING);
+ _cairo_polygon_fini (&intersect);
+
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ status = _cairo_composite_rectangles_init_for_polygon (&composite,
+ extents->surface,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ &polygon,
+ NULL);
+ if (unlikely (status))
+ goto cleanup_polygon;
+
+ status = composite_polygon (compositor, &composite,
+ &polygon, fill_rule, antialias);
+
+ _cairo_composite_rectangles_fini (&composite);
+cleanup_polygon:
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+static cairo_int_status_t
+fixup_unbounded_boxes (const cairo_spans_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_boxes_t tmp, clear;
+ cairo_box_t box;
+ cairo_int_status_t status;
+
+ assert (boxes->is_pixel_aligned);
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (extents->bounded.width == extents->unbounded.width &&
+ extents->bounded.height == extents->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* subtract the drawn boxes from the unbounded area */
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (boxes->num_boxes) {
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ tmp.chunks.next = NULL;
+ if (unlikely (status))
+ goto error;
+ } else {
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+
+ status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ /* If we have a clip polygon, we need to intersect with that as well */
+ if (extents->clip->path) {
+ status = fixup_unbounded_polygon (compositor, extents, &clear);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ status = fixup_unbounded_mask (compositor, extents, &clear);
+ } else {
+ /* Otherwise just intersect with the clip boxes */
+ if (extents->clip->num_boxes) {
+ _cairo_boxes_init_for_array (&tmp,
+ extents->clip->boxes,
+ extents->clip->num_boxes);
+ status = _cairo_boxes_intersect (&clear, &tmp, &clear);
+ if (unlikely (status))
+ goto error;
+ }
+
+ if (clear.is_pixel_aligned) {
+ status = compositor->fill_boxes (extents->surface,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+ } else {
+ cairo_composite_rectangles_t composite;
+
+ status = _cairo_composite_rectangles_init_for_boxes (&composite,
+ extents->surface,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ &clear,
+ NULL);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = composite_boxes (compositor, &composite, &clear);
+ _cairo_composite_rectangles_fini (&composite);
+ }
+ }
+ }
+
+error:
+ _cairo_boxes_fini (&clear);
+ return status;
+}
+
+static cairo_surface_t *
+unwrap_source (const cairo_pattern_t *pattern)
+{
+ cairo_rectangle_int_t limit;
+
+ return _cairo_pattern_get_source ((cairo_surface_pattern_t *)pattern,
+ &limit);
+}
+
+static cairo_bool_t
+is_recording_pattern (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return FALSE;
+
+ surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+ return _cairo_surface_is_recording (surface);
+}
+
+static cairo_bool_t
+recording_pattern_contains_sample (const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample)
+{
+ cairo_recording_surface_t *surface;
+
+ if (! is_recording_pattern (pattern))
+ return FALSE;
+
+ if (pattern->extend == CAIRO_EXTEND_NONE)
+ return TRUE;
+
+ surface = (cairo_recording_surface_t *) unwrap_source (pattern);
+ if (surface->unbounded)
+ return TRUE;
+
+ return _cairo_rectangle_contains_rectangle (&surface->extents, sample);
+}
+
+static cairo_bool_t
+op_reduces_to_source (const cairo_composite_rectangles_t *extents,
+ cairo_bool_t no_mask)
+{
+ if (extents->op == CAIRO_OPERATOR_SOURCE)
+ return TRUE;
+
+ if (extents->surface->is_clear)
+ return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD;
+
+ if (no_mask && extents->op == CAIRO_OPERATOR_OVER)
+ return _cairo_pattern_is_opaque (&extents->source_pattern.base,
+ &extents->source_sample_area);
+
+ return FALSE;
+}
+
+static cairo_status_t
+upload_boxes (const cairo_spans_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ const cairo_surface_pattern_t *source = &extents->source_pattern.surface;
+ cairo_surface_t *src;
+ cairo_rectangle_int_t limit;
+ cairo_int_status_t status;
+ int tx, ty;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (source->base.filter == CAIRO_FILTER_GAUSSIAN)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ src = _cairo_pattern_get_source(source, &limit);
+ if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->base.matrix, &tx, &ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Check that the data is entirely within the image */
+ if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width ||
+ extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ tx += limit.x;
+ ty += limit.y;
+
+ if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
+ status = compositor->draw_image_boxes (dst,
+ (cairo_image_surface_t *)src,
+ boxes, tx, ty);
+ else
+ status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
+ tx, ty);
+
+ return status;
+}
+
+static cairo_bool_t
+_clip_is_region (const cairo_clip_t *clip)
+{
+ int i;
+
+ if (clip->is_region)
+ return TRUE;
+
+ if (clip->path)
+ return FALSE;
+
+ for (i = 0; i < clip->num_boxes; i++) {
+ const cairo_box_t *b = &clip->boxes[i];
+ if (!_cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static cairo_int_status_t
+composite_aligned_boxes (const cairo_spans_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_operator_t op = extents->op;
+ const cairo_pattern_t *source = &extents->source_pattern.base;
+ cairo_int_status_t status;
+ cairo_bool_t need_clip_mask = ! _clip_is_region (extents->clip);
+ cairo_bool_t op_is_source;
+ cairo_bool_t no_mask;
+ cairo_bool_t inplace;
+
+ TRACE ((stderr, "%s: need_clip_mask=%d, is-bounded=%d\n",
+ __FUNCTION__, need_clip_mask, extents->is_bounded));
+ if (need_clip_mask && ! extents->is_bounded) {
+ TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__));
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ no_mask = extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+ CAIRO_COLOR_IS_OPAQUE (&extents->mask_pattern.solid.color);
+ op_is_source = op_reduces_to_source (extents, no_mask);
+ inplace = ! need_clip_mask && op_is_source && no_mask;
+
+ TRACE ((stderr, "%s: op-is-source=%d [op=%d], no-mask=%d, inplace=%d\n",
+ __FUNCTION__, op_is_source, op, no_mask, inplace));
+
+ if (op == CAIRO_OPERATOR_SOURCE && (need_clip_mask || ! no_mask)) {
+ /* SOURCE with a mask is actually a LERP in cairo semantics */
+ if ((compositor->flags & CAIRO_SPANS_COMPOSITOR_HAS_LERP) == 0) {
+ TRACE ((stderr, "%s: unsupported lerp\n", __FUNCTION__));
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /* Are we just copying a recording surface? */
+ if (inplace &&
+ recording_pattern_contains_sample (&extents->source_pattern.base,
+ &extents->source_sample_area))
+ {
+ cairo_clip_t *recording_clip;
+ const cairo_pattern_t *source = &extents->source_pattern.base;
+
+ /* XXX could also do tiling repeat modes... */
+
+ /* first clear the area about to be overwritten */
+ if (! dst->is_clear)
+ status = compositor->fill_boxes (dst,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ boxes);
+
+ recording_clip = _cairo_clip_from_boxes (boxes);
+ status = _cairo_recording_surface_replay_with_clip (unwrap_source (source),
+ &source->matrix,
+ dst, recording_clip);
+ _cairo_clip_destroy (recording_clip);
+
+ return status;
+ }
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (! need_clip_mask && no_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_color_t *color;
+
+ color = &((cairo_solid_pattern_t *) source)->color;
+ if (op_is_source)
+ op = CAIRO_OPERATOR_SOURCE;
+ status = compositor->fill_boxes (dst, op, color, boxes);
+ } else if (inplace && source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ status = upload_boxes (compositor, extents, boxes);
+ }
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_surface_t *src;
+ cairo_surface_t *mask = NULL;
+ int src_x, src_y;
+ int mask_x = 0, mask_y = 0;
+
+ /* All typical cases will have been resolved before now... */
+ if (need_clip_mask) {
+ mask = get_clip_surface (compositor, dst, extents->clip,
+ &extents->bounded);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ mask_x = -extents->bounded.x;
+ mask_y = -extents->bounded.y;
+ }
+
+ /* XXX but this is still ugly */
+ if (! no_mask) {
+ src = compositor->pattern_to_surface (dst,
+ &extents->mask_pattern.base,
+ TRUE,
+ &extents->bounded,
+ &extents->mask_sample_area,
+ &src_x, &src_y);
+ if (unlikely (src->status)) {
+ cairo_surface_destroy (mask);
+ return src->status;
+ }
+
+ if (mask != NULL) {
+ status = compositor->composite_boxes (mask, CAIRO_OPERATOR_IN,
+ src, NULL,
+ src_x, src_y,
+ 0, 0,
+ mask_x, mask_y,
+ boxes, &extents->bounded);
+
+ cairo_surface_destroy (src);
+ } else {
+ mask = src;
+ mask_x = src_x;
+ mask_y = src_y;
+ }
+ }
+
+ src = compositor->pattern_to_surface (dst, source, FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (likely (src->status == CAIRO_STATUS_SUCCESS)) {
+ status = compositor->composite_boxes (dst, op, src, mask,
+ src_x, src_y,
+ mask_x, mask_y,
+ 0, 0,
+ boxes, &extents->bounded);
+ cairo_surface_destroy (src);
+ } else
+ status = src->status;
+
+ cairo_surface_destroy (mask);
+ }
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && ! extents->is_bounded)
+ status = fixup_unbounded_boxes (compositor, extents, boxes);
+
+ return status;
+}
+
+static cairo_bool_t
+composite_needs_clip (const cairo_composite_rectangles_t *composite,
+ const cairo_box_t *extents)
+{
+ return !_cairo_clip_contains_box (composite->clip, extents);
+}
+
+static cairo_int_status_t
+composite_boxes (const cairo_spans_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_abstract_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_int_status_t status;
+ cairo_box_t box;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ _cairo_box_from_rectangle (&box, &extents->unbounded);
+ if (composite_needs_clip (extents, &box)) {
+ TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__));
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ _cairo_rectangular_scan_converter_init (&converter, &extents->unbounded);
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ int i;
+
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto cleanup_converter;
+ }
+ }
+
+ status = compositor->renderer_init (&renderer, extents,
+ CAIRO_ANTIALIAS_DEFAULT, FALSE);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = converter.base.generate (&converter.base, &renderer.base);
+ compositor->renderer_fini (&renderer, status);
+
+cleanup_converter:
+ converter.base.destroy (&converter.base);
+ return status;
+}
+
+static cairo_int_status_t
+composite_polygon (const cairo_spans_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias)
+{
+ cairo_abstract_span_renderer_t renderer;
+ cairo_scan_converter_t *converter;
+ cairo_bool_t needs_clip;
+ cairo_int_status_t status;
+
+ if (extents->is_bounded)
+ needs_clip = extents->clip->path != NULL;
+ else
+ needs_clip = !_clip_is_region (extents->clip) || extents->clip->num_boxes > 1;
+ TRACE ((stderr, "%s - needs_clip=%d\n", __FUNCTION__, needs_clip));
+ if (needs_clip) {
+ TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__));
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ const cairo_rectangle_int_t *r = &extents->unbounded;
+
+ if (antialias == CAIRO_ANTIALIAS_FAST) {
+ converter = _cairo_tor22_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ fill_rule, antialias);
+ status = _cairo_tor22_scan_converter_add_polygon (converter, polygon);
+ } else if (antialias == CAIRO_ANTIALIAS_NONE) {
+ converter = _cairo_mono_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ fill_rule);
+ status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
+ } else {
+ converter = _cairo_tor_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ fill_rule, antialias);
+ status = _cairo_tor_scan_converter_add_polygon (converter, polygon);
+ }
+ }
+ if (unlikely (status))
+ goto cleanup_converter;
+
+ status = compositor->renderer_init (&renderer, extents,
+ antialias, needs_clip);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = converter->generate (converter, &renderer.base);
+ compositor->renderer_fini (&renderer, status);
+
+cleanup_converter:
+ converter->destroy (converter);
+ return status;
+}
+
+static cairo_int_status_t
+trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_box_t box;
+
+ _cairo_boxes_extents (boxes, &box);
+ return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+trim_extents_to_polygon (cairo_composite_rectangles_t *extents,
+ cairo_polygon_t *polygon)
+{
+ return _cairo_composite_rectangles_intersect_mask_extents (extents,
+ &polygon->extents);
+}
+
+static cairo_int_status_t
+clip_and_composite_boxes (const cairo_spans_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_int_status_t status;
+ cairo_polygon_t polygon;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ status = trim_extents_to_boxes (extents, boxes);
+ if (unlikely (status))
+ return status;
+
+ if (boxes->num_boxes == 0) {
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ return fixup_unbounded_boxes (compositor, extents, boxes);
+ }
+
+ /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+ if (extents->clip->path != NULL && extents->is_bounded) {
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+ cairo_clip_t *clip;
+
+ clip = _cairo_clip_copy (extents->clip);
+ clip = _cairo_clip_intersect_boxes (clip, boxes);
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ status = _cairo_clip_get_polygon (clip, &polygon,
+ &fill_rule, &antialias);
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *saved_clip = extents->clip;
+ extents->clip = clip;
+
+ status = clip_and_composite_polygon (compositor, extents, &polygon,
+ fill_rule, antialias);
+
+ clip = extents->clip;
+ extents->clip = saved_clip;
+
+ _cairo_polygon_fini (&polygon);
+ }
+ _cairo_clip_destroy (clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ if (boxes->is_pixel_aligned) {
+ status = composite_aligned_boxes (compositor, extents, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = composite_boxes (compositor, extents, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_polygon_init_boxes (&polygon, boxes);
+ if (unlikely (status))
+ return status;
+
+ status = composite_polygon (compositor, extents, &polygon,
+ CAIRO_FILL_RULE_WINDING,
+ CAIRO_ANTIALIAS_DEFAULT);
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+static cairo_int_status_t
+clip_and_composite_polygon (const cairo_spans_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ /* XXX simply uses polygon limits.point extemities, tessellation? */
+ status = trim_extents_to_polygon (extents, polygon);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_polygon_is_empty (polygon)) {
+ cairo_boxes_t boxes;
+
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_boxes_init (&boxes);
+ extents->bounded.width = extents->bounded.height = 0;
+ return fixup_unbounded_boxes (compositor, extents, &boxes);
+ }
+
+ if (extents->is_bounded && extents->clip->path) {
+ cairo_polygon_t clipper;
+ cairo_antialias_t clip_antialias;
+ cairo_fill_rule_t clip_fill_rule;
+
+ TRACE((stderr, "%s - combining shape with clip polygon\n",
+ __FUNCTION__));
+
+ status = _cairo_clip_get_polygon (extents->clip,
+ &clipper,
+ &clip_fill_rule,
+ &clip_antialias);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *old_clip;
+
+ if (clip_antialias == antialias) {
+ status = _cairo_polygon_intersect (polygon, fill_rule,
+ &clipper, clip_fill_rule);
+ _cairo_polygon_fini (&clipper);
+ if (unlikely (status))
+ return status;
+
+ old_clip = extents->clip;
+ extents->clip = _cairo_clip_copy_region (extents->clip);
+ _cairo_clip_destroy (old_clip);
+
+ status = trim_extents_to_polygon (extents, polygon);
+ if (unlikely (status))
+ return status;
+
+ fill_rule = CAIRO_FILL_RULE_WINDING;
+ } else {
+ _cairo_polygon_fini (&clipper);
+ }
+ }
+ }
+
+ return composite_polygon (compositor, extents,
+ polygon, fill_rule, antialias);
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_spans_compositor_paint (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+ cairo_boxes_t boxes;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ _cairo_clip_steal_boxes (extents->clip, &boxes);
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_spans_compositor_mask (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+ cairo_int_status_t status;
+ cairo_boxes_t boxes;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ _cairo_clip_steal_boxes (extents->clip, &boxes);
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_spans_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ TRACE_ (_cairo_debug_print_path (stderr, path));
+ TRACE_ (_cairo_debug_print_clip (stderr, extents->clip));
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask))
+ _cairo_boxes_limit (&boxes,
+ extents->clip->boxes,
+ extents->clip->num_boxes);
+
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING;
+
+ if (! _cairo_rectangle_contains_rectangle (&extents->unbounded,
+ &extents->mask))
+ {
+ if (extents->clip->num_boxes == 1) {
+ _cairo_polygon_init (&polygon, extents->clip->boxes, 1);
+ } else {
+ cairo_box_t limits;
+ _cairo_box_from_rectangle (&limits, &extents->unbounded);
+ _cairo_polygon_init (&polygon, &limits, 1);
+ }
+ }
+ else
+ {
+ _cairo_polygon_init (&polygon, NULL, 0);
+ }
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ TRACE_ (_cairo_debug_print_polygon (stderr, &polygon));
+ polygon.num_limits = 0;
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) {
+ status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
+ extents->clip->boxes,
+ extents->clip->num_boxes);
+ }
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *saved_clip = extents->clip;
+
+ if (extents->is_bounded) {
+ extents->clip = _cairo_clip_copy_path (extents->clip);
+ extents->clip = _cairo_clip_intersect_box(extents->clip,
+ &polygon.extents);
+ }
+
+ status = clip_and_composite_polygon (compositor, extents, &polygon,
+ fill_rule, antialias);
+
+ if (extents->is_bounded) {
+ _cairo_clip_destroy (extents->clip);
+ extents->clip = saved_clip;
+ }
+ }
+ _cairo_polygon_fini (&polygon);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_spans_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+ cairo_int_status_t status;
+
+ TRACE((stderr, "%s op=%d, antialias=%d\n", __FUNCTION__, extents->op, antialias));
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ TRACE((stderr, "%s - rectilinear\n", __FUNCTION__));
+
+ _cairo_boxes_init (&boxes);
+ if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask))
+ _cairo_boxes_limit (&boxes,
+ extents->clip->boxes,
+ extents->clip->num_boxes);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_polygon_t polygon;
+
+ TRACE((stderr, "%s - polygon\n", __FUNCTION__));
+
+ if (! _cairo_rectangle_contains_rectangle (&extents->unbounded,
+ &extents->mask))
+ {
+ TRACE((stderr, "%s - clipping to bounds\n", __FUNCTION__));
+ if (extents->clip->num_boxes == 1) {
+ _cairo_polygon_init (&polygon, extents->clip->boxes, 1);
+ } else {
+ cairo_box_t limits;
+ _cairo_box_from_rectangle (&limits, &extents->unbounded);
+ _cairo_polygon_init (&polygon, &limits, 1);
+ }
+ }
+ else
+ {
+ _cairo_polygon_init (&polygon, NULL, 0);
+ }
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ TRACE_ (_cairo_debug_print_polygon (stderr, &polygon));
+ polygon.num_limits = 0;
+
+ if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) {
+ TRACE((stderr, "%s - polygon intersect with %d clip boxes\n",
+ __FUNCTION__, extents->clip->num_boxes));
+ status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
+ extents->clip->boxes,
+ extents->clip->num_boxes);
+ }
+ TRACE_ (_cairo_debug_print_polygon (stderr, &polygon));
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *saved_clip = extents->clip;
+
+ if (extents->is_bounded) {
+ TRACE((stderr, "%s - polygon discard clip boxes\n",
+ __FUNCTION__));
+ extents->clip = _cairo_clip_copy_path (extents->clip);
+ extents->clip = _cairo_clip_intersect_box(extents->clip,
+ &polygon.extents);
+ }
+
+ status = clip_and_composite_polygon (compositor, extents, &polygon,
+ fill_rule, antialias);
+
+ if (extents->is_bounded) {
+ _cairo_clip_destroy (extents->clip);
+ extents->clip = saved_clip;
+ }
+ }
+ _cairo_polygon_fini (&polygon);
+
+ TRACE((stderr, "%s - polygon status=%d\n", __FUNCTION__, status));
+ }
+
+ return status;
+}
+
+void
+_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor,
+ const cairo_compositor_t *delegate)
+{
+ compositor->base.delegate = delegate;
+
+ compositor->base.paint = _cairo_spans_compositor_paint;
+ compositor->base.mask = _cairo_spans_compositor_mask;
+ compositor->base.fill = _cairo_spans_compositor_fill;
+ compositor->base.stroke = _cairo_spans_compositor_stroke;
+ compositor->base.glyphs = NULL;
+}
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
new file mode 100755
index 000000000..c42b5afa7
--- /dev/null
+++ b/src/cairo-spans-private.h
@@ -0,0 +1,206 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2008 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAIRO_SPANS_PRIVATE_H
+#define CAIRO_SPANS_PRIVATE_H
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+/* Number of bits of precision used for alpha. */
+#define CAIRO_SPANS_UNIT_COVERAGE_BITS 8
+#define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1)
+
+/* A structure representing an open-ended horizontal span of constant
+ * pixel coverage. */
+typedef struct _cairo_half_open_span {
+ int32_t x; /* The inclusive x-coordinate of the start of the span. */
+ uint8_t coverage; /* The pixel coverage for the pixels to the right. */
+ uint8_t inverse; /* between regular mask and clip */
+} cairo_half_open_span_t;
+
+/* Span renderer interface. Instances of renderers are provided by
+ * surfaces if they want to composite spans instead of trapezoids. */
+typedef struct _cairo_span_renderer cairo_span_renderer_t;
+struct _cairo_span_renderer {
+ /* Private status variable. */
+ cairo_status_t status;
+
+ /* Called to destroy the renderer. */
+ cairo_destroy_func_t destroy;
+
+ /* Render the spans on row y of the destination by whatever compositing
+ * method is required. */
+ cairo_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;
+
+ /* Generates coverage spans for rows for the added edges and calls
+ * the renderer function for each row. After generating spans the
+ * only valid thing to do with the converter is to destroy it. */
+ cairo_status_t (*generate) (void *abstract_converter,
+ cairo_span_renderer_t *renderer);
+
+ /* Private status. Read with _cairo_scan_converter_status(). */
+ cairo_status_t status;
+};
+
+/* Scan converter constructors. */
+
+cairo_private cairo_scan_converter_t *
+_cairo_tor_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias);
+cairo_private cairo_status_t
+_cairo_tor_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_tor22_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias);
+cairo_private cairo_status_t
+_cairo_tor22_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_mono_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule);
+cairo_private cairo_status_t
+_cairo_mono_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias);
+
+typedef struct _cairo_rectangular_scan_converter {
+ cairo_scan_converter_t base;
+
+ cairo_box_t extents;
+
+ struct _cairo_rectangular_scan_converter_chunk {
+ struct _cairo_rectangular_scan_converter_chunk *next;
+ void *base;
+ int count;
+ int size;
+ } chunks, *tail;
+ char buf[CAIRO_STACK_BUFFER_SIZE];
+ int num_rectangles;
+} cairo_rectangular_scan_converter_t;
+
+cairo_private void
+_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self,
+ const cairo_box_t *box,
+ int dir);
+
+typedef struct _cairo_botor_scan_converter {
+ cairo_scan_converter_t base;
+
+ cairo_box_t extents;
+ cairo_fill_rule_t fill_rule;
+
+ int xmin, xmax;
+
+ struct _cairo_botor_scan_converter_chunk {
+ struct _cairo_botor_scan_converter_chunk *next;
+ void *base;
+ int count;
+ int size;
+ } chunks, *tail;
+ char buf[CAIRO_STACK_BUFFER_SIZE];
+ int num_edges;
+} cairo_botor_scan_converter_t;
+
+cairo_private void
+_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self,
+ const cairo_box_t *extents,
+ cairo_fill_rule_t fill_rule);
+
+/* cairo-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/src/cairo-spans.c b/src/cairo-spans.c
new file mode 100755
index 000000000..b8d41800e
--- /dev/null
+++ b/src/cairo-spans.c
@@ -0,0 +1,248 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2008 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-fixed-private.h"
+#include "cairo-types-private.h"
+
+static void
+_cairo_nil_destroy (void *abstract)
+{
+ (void) abstract;
+}
+
+static cairo_status_t
+_cairo_nil_scan_converter_generate (void *abstract_converter,
+ cairo_span_renderer_t *renderer)
+{
+ (void) abstract_converter;
+ (void) renderer;
+ return _cairo_scan_converter_status (abstract_converter);
+}
+
+cairo_status_t
+_cairo_scan_converter_status (void *abstract_converter)
+{
+ cairo_scan_converter_t *converter = abstract_converter;
+ return converter->status;
+}
+
+cairo_status_t
+_cairo_scan_converter_set_error (void *abstract_converter,
+ cairo_status_t error)
+{
+ cairo_scan_converter_t *converter = abstract_converter;
+ if (error == CAIRO_STATUS_SUCCESS)
+ ASSERT_NOT_REACHED;
+ if (converter->status == CAIRO_STATUS_SUCCESS) {
+ converter->generate = _cairo_nil_scan_converter_generate;
+ converter->status = error;
+ }
+ return converter->status;
+}
+
+static void
+_cairo_nil_scan_converter_init (cairo_scan_converter_t *converter,
+ cairo_status_t status)
+{
+ converter->destroy = _cairo_nil_destroy;
+ converter->status = CAIRO_STATUS_SUCCESS;
+ status = _cairo_scan_converter_set_error (converter, status);
+}
+
+cairo_scan_converter_t *
+_cairo_scan_converter_create_in_error (cairo_status_t status)
+{
+#define RETURN_NIL {\
+ static cairo_scan_converter_t nil;\
+ _cairo_nil_scan_converter_init (&nil, status);\
+ return &nil;\
+ }
+ switch (status) {
+ case CAIRO_STATUS_SUCCESS:
+ case CAIRO_STATUS_LAST_STATUS:
+ ASSERT_NOT_REACHED;
+ break;
+ case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL;
+ case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL;
+ case CAIRO_STATUS_NULL_POINTER: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_STRING: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL;
+ case CAIRO_STATUS_READ_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL;
+ case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL;
+ case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_DASH: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL;
+ case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL;
+ case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL;
+ case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL;
+ case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL;
+ case CAIRO_STATUS_NO_MEMORY: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL;
+ case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
+ case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
+ case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
+ default:
+ break;
+ }
+ status = CAIRO_STATUS_NO_MEMORY;
+ RETURN_NIL;
+#undef RETURN_NIL
+}
+
+static cairo_status_t
+_cairo_nil_span_renderer_render_rows (
+ void *abstract_renderer,
+ int y,
+ int height,
+ const cairo_half_open_span_t *coverages,
+ unsigned num_coverages)
+{
+ (void) y;
+ (void) height;
+ (void) coverages;
+ (void) num_coverages;
+ return _cairo_span_renderer_status (abstract_renderer);
+}
+
+static cairo_status_t
+_cairo_nil_span_renderer_finish (void *abstract_renderer)
+{
+ return _cairo_span_renderer_status (abstract_renderer);
+}
+
+cairo_status_t
+_cairo_span_renderer_status (void *abstract_renderer)
+{
+ cairo_span_renderer_t *renderer = abstract_renderer;
+ return renderer->status;
+}
+
+cairo_status_t
+_cairo_span_renderer_set_error (
+ void *abstract_renderer,
+ cairo_status_t error)
+{
+ cairo_span_renderer_t *renderer = abstract_renderer;
+ if (error == CAIRO_STATUS_SUCCESS) {
+ ASSERT_NOT_REACHED;
+ }
+ if (renderer->status == CAIRO_STATUS_SUCCESS) {
+ renderer->render_rows = _cairo_nil_span_renderer_render_rows;
+ renderer->finish = _cairo_nil_span_renderer_finish;
+ renderer->status = error;
+ }
+ return renderer->status;
+}
+
+static void
+_cairo_nil_span_renderer_init (cairo_span_renderer_t *renderer,
+ cairo_status_t status)
+{
+ renderer->destroy = _cairo_nil_destroy;
+ renderer->status = CAIRO_STATUS_SUCCESS;
+ status = _cairo_span_renderer_set_error (renderer, status);
+}
+
+cairo_span_renderer_t *
+_cairo_span_renderer_create_in_error (cairo_status_t status)
+{
+#define RETURN_NIL {\
+ static cairo_span_renderer_t nil;\
+ _cairo_nil_span_renderer_init (&nil, status);\
+ return &nil;\
+ }
+ switch (status) {
+ case CAIRO_STATUS_SUCCESS:
+ case CAIRO_STATUS_LAST_STATUS:
+ ASSERT_NOT_REACHED;
+ break;
+ case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL;
+ case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL;
+ case CAIRO_STATUS_NULL_POINTER: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_STRING: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL;
+ case CAIRO_STATUS_READ_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL;
+ case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL;
+ case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_DASH: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL;
+ case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL;
+ case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL;
+ case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL;
+ case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL;
+ case CAIRO_STATUS_NO_MEMORY: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL;
+ case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
+ case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
+ case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
+ case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
+ case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
+ default:
+ break;
+ }
+ status = CAIRO_STATUS_NO_MEMORY;
+ RETURN_NIL;
+#undef RETURN_NIL
+}
diff --git a/src/cairo-spline.c b/src/cairo-spline.c
new file mode 100755
index 000000000..9ebb949c7
--- /dev/null
+++ b/src/cairo-spline.c
@@ -0,0 +1,430 @@
+/* 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-box-inline.h"
+#include "cairo-slope-private.h"
+
+cairo_bool_t
+_cairo_spline_intersects (const cairo_point_t *a,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d,
+ const cairo_box_t *box)
+{
+ cairo_box_t bounds;
+
+ if (_cairo_box_contains_point (box, a) ||
+ _cairo_box_contains_point (box, b) ||
+ _cairo_box_contains_point (box, c) ||
+ _cairo_box_contains_point (box, d))
+ {
+ return TRUE;
+ }
+
+ bounds.p2 = bounds.p1 = *a;
+ _cairo_box_add_point (&bounds, b);
+ _cairo_box_add_point (&bounds, c);
+ _cairo_box_add_point (&bounds, d);
+
+ if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
+ bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
+ {
+ return FALSE;
+ }
+
+#if 0 /* worth refining? */
+ bounds.p2 = bounds.p1 = *a;
+ _cairo_box_add_curve_to (&bounds, b, c, d);
+ if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
+ bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
+ {
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_spline_init (cairo_spline_t *spline,
+ cairo_spline_add_point_func_t add_point_func,
+ void *closure,
+ const cairo_point_t *a, const cairo_point_t *b,
+ const cairo_point_t *c, const cairo_point_t *d)
+{
+ /* If both tangents are zero, this is just a straight line */
+ if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y)
+ return FALSE;
+
+ spline->add_point_func = add_point_func;
+ spline->closure = closure;
+
+ spline->knots.a = *a;
+ spline->knots.b = *b;
+ spline->knots.c = *c;
+ spline->knots.d = *d;
+
+ if (a->x != b->x || a->y != b->y)
+ _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b);
+ else if (a->x != c->x || a->y != c->y)
+ _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c);
+ else if (a->x != d->x || a->y != d->y)
+ _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d);
+ else
+ return FALSE;
+
+ if (c->x != d->x || c->y != d->y)
+ _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d);
+ else if (b->x != d->x || b->y != d->y)
+ _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
+ else
+ return FALSE; /* just treat this as a straight-line from a -> d */
+
+ /* XXX if the initial, final and vector are all equal, this is just a line */
+
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_spline_add_point (cairo_spline_t *spline,
+ const cairo_point_t *point,
+ const cairo_point_t *knot)
+{
+ cairo_point_t *prev;
+ cairo_slope_t slope;
+
+ prev = &spline->last_point;
+ if (prev->x == point->x && prev->y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_slope_init (&slope, point, knot);
+
+ spline->last_point = *point;
+ return spline->add_point_func (spline->closure, point, &slope);
+}
+
+static void
+_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result)
+{
+ result->x = a->x + ((b->x - a->x) >> 1);
+ result->y = a->y + ((b->y - a->y) >> 1);
+}
+
+static void
+_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2)
+{
+ cairo_point_t ab, bc, cd;
+ cairo_point_t abbc, bccd;
+ cairo_point_t final;
+
+ _lerp_half (&s1->a, &s1->b, &ab);
+ _lerp_half (&s1->b, &s1->c, &bc);
+ _lerp_half (&s1->c, &s1->d, &cd);
+ _lerp_half (&ab, &bc, &abbc);
+ _lerp_half (&bc, &cd, &bccd);
+ _lerp_half (&abbc, &bccd, &final);
+
+ s2->a = final;
+ s2->b = bccd;
+ s2->c = cd;
+ s2->d = s1->d;
+
+ s1->b = ab;
+ s1->c = abbc;
+ s1->d = final;
+}
+
+/* Return an upper bound on the error (squared) that could result from
+ * approximating a spline as a line segment connecting the two endpoints. */
+static double
+_cairo_spline_error_squared (const cairo_spline_knots_t *knots)
+{
+ double bdx, bdy, berr;
+ double cdx, cdy, cerr;
+
+ /* We are going to compute the distance (squared) between each of the the b
+ * and c control points and the segment a-b. The maximum of these two
+ * distances will be our approximation error. */
+
+ bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x);
+ bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y);
+
+ cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x);
+ cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y);
+
+ if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) {
+ /* Intersection point (px):
+ * px = p1 + u(p2 - p1)
+ * (p - px) ∙ (p2 - p1) = 0
+ * Thus:
+ * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
+ */
+
+ double dx, dy, u, v;
+
+ dx = _cairo_fixed_to_double (knots->d.x - knots->a.x);
+ dy = _cairo_fixed_to_double (knots->d.y - knots->a.y);
+ v = dx * dx + dy * dy;
+
+ u = bdx * dx + bdy * dy;
+ if (u <= 0) {
+ /* bdx -= 0;
+ * bdy -= 0;
+ */
+ } else if (u >= v) {
+ bdx -= dx;
+ bdy -= dy;
+ } else {
+ bdx -= u/v * dx;
+ bdy -= u/v * dy;
+ }
+
+ u = cdx * dx + cdy * dy;
+ if (u <= 0) {
+ /* cdx -= 0;
+ * cdy -= 0;
+ */
+ } else if (u >= v) {
+ cdx -= dx;
+ cdy -= dy;
+ } else {
+ cdx -= u/v * dx;
+ cdy -= u/v * dy;
+ }
+ }
+
+ berr = bdx * bdx + bdy * bdy;
+ cerr = cdx * cdx + cdy * cdy;
+ if (berr > cerr)
+ return berr;
+ else
+ return cerr;
+}
+
+static cairo_status_t
+_cairo_spline_decompose_into (cairo_spline_knots_t *s1,
+ double tolerance_squared,
+ cairo_spline_t *result)
+{
+ cairo_spline_knots_t s2;
+ cairo_status_t status;
+
+ if (_cairo_spline_error_squared (s1) < tolerance_squared)
+ return _cairo_spline_add_point (result, &s1->a, &s1->b);
+
+ _de_casteljau (s1, &s2);
+
+ status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_spline_decompose_into (&s2, tolerance_squared, result);
+}
+
+cairo_status_t
+_cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
+{
+ cairo_spline_knots_t s1;
+ cairo_status_t status;
+
+ /* this is the entry point for spline decompose, we adjust the
+ final_slope if b, c, d are very close
+ */
+ if (_cairo_spline_error_squared (&spline->knots) < tolerance * tolerance)
+ spline->final_slope = spline->initial_slope;
+
+ s1 = spline->knots;
+ spline->last_point = s1.a;
+ status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline);
+ if (unlikely (status))
+ return status;
+
+ return spline->add_point_func (spline->closure,
+ &spline->knots.d, &spline->final_slope);
+}
+
+/* Note: this function is only good for computing bounds in device space. */
+cairo_status_t
+_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
+ void *closure,
+ const cairo_point_t *p0, const cairo_point_t *p1,
+ const cairo_point_t *p2, const cairo_point_t *p3)
+{
+ double x0, x1, x2, x3;
+ double y0, y1, y2, y3;
+ double a, b, c;
+ double t[4];
+ int t_num = 0, i;
+ cairo_status_t status;
+
+ x0 = _cairo_fixed_to_double (p0->x);
+ y0 = _cairo_fixed_to_double (p0->y);
+ x1 = _cairo_fixed_to_double (p1->x);
+ y1 = _cairo_fixed_to_double (p1->y);
+ x2 = _cairo_fixed_to_double (p2->x);
+ y2 = _cairo_fixed_to_double (p2->y);
+ x3 = _cairo_fixed_to_double (p3->x);
+ y3 = _cairo_fixed_to_double (p3->y);
+
+ /* The spline can be written as a polynomial of the four points:
+ *
+ * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
+ *
+ * for 0≤t≤1. Now, the X and Y components of the spline follow the
+ * same polynomial but with x and y replaced for p. To find the
+ * bounds of the spline, we just need to find the X and Y bounds.
+ * To find the bound, we take the derivative and equal it to zero,
+ * and solve to find the t's that give the extreme points.
+ *
+ * Here is the derivative of the curve, sorted on t:
+ *
+ * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
+ *
+ * Let:
+ *
+ * a = -p0+3p1-3p2+p3
+ * b = p0-2p1+p2
+ * c = -p0+p1
+ *
+ * Gives:
+ *
+ * a.t² + 2b.t + c = 0
+ *
+ * With:
+ *
+ * delta = b*b - a*c
+ *
+ * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
+ * delta is positive, and at -b/a if delta is zero.
+ */
+
+#define ADD(t0) \
+ { \
+ double _t0 = (t0); \
+ if (0 < _t0 && _t0 < 1) \
+ t[t_num++] = _t0; \
+ }
+
+#define FIND_EXTREMES(a,b,c) \
+ { \
+ if (a == 0) { \
+ if (b != 0) \
+ ADD (-c / (2*b)); \
+ } else { \
+ double b2 = b * b; \
+ double delta = b2 - a * c; \
+ if (delta > 0) { \
+ cairo_bool_t feasible; \
+ double _2ab = 2 * a * b; \
+ /* We are only interested in solutions t that satisfy 0<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, NULL);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < t_num; i++) {
+ cairo_point_t p;
+ double x, y;
+ double t_1_0, t_0_1;
+ double t_2_0, t_0_2;
+ double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
+
+ t_1_0 = t[i]; /* t */
+ t_0_1 = 1 - t_1_0; /* (1 - t) */
+
+ t_2_0 = t_1_0 * t_1_0; /* t * t */
+ t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
+
+ t_3_0 = t_2_0 * t_1_0; /* t * t * t */
+ t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */
+ t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */
+ t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */
+
+ /* Bezier polynomial */
+ x = x0 * t_0_3
+ + x1 * t_1_2_3
+ + x2 * t_2_1_3
+ + x3 * t_3_0;
+ y = y0 * t_0_3
+ + y1 * t_1_2_3
+ + y2 * t_2_1_3
+ + y3 * t_3_0;
+
+ p.x = _cairo_fixed_from_double (x);
+ p.y = _cairo_fixed_from_double (y);
+ status = add_point_func (closure, &p, NULL);
+ if (unlikely (status))
+ return status;
+ }
+
+ return add_point_func (closure, p3, NULL);
+}
diff --git a/src/cairo-stroke-dash-private.h b/src/cairo-stroke-dash-private.h
new file mode 100755
index 000000000..75c000cd7
--- /dev/null
+++ b/src/cairo-stroke-dash-private.h
@@ -0,0 +1,70 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_STROKE_DASH_PRIVATE_H
+#define CAIRO_STROKE_DASH_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_stroker_dash {
+ cairo_bool_t dashed;
+ unsigned int dash_index;
+ cairo_bool_t dash_on;
+ cairo_bool_t dash_starts_on;
+ double dash_remain;
+
+ double dash_offset;
+ const double *dashes;
+ unsigned int num_dashes;
+} cairo_stroker_dash_t;
+
+cairo_private void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style);
+
+cairo_private void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash);
+
+cairo_private void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_STROKE_DASH_PRIVATE_H */
diff --git a/src/cairo-stroke-dash.c b/src/cairo-stroke-dash.c
new file mode 100755
index 000000000..9494010f5
--- /dev/null
+++ b/src/cairo-stroke-dash.c
@@ -0,0 +1,96 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-stroke-dash-private.h"
+
+void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
+{
+ double offset;
+ cairo_bool_t on = TRUE;
+ unsigned int i = 0;
+
+ if (! dash->dashed)
+ return;
+
+ offset = dash->dash_offset;
+
+ /* We stop searching for a starting point as soon as the
+ offset reaches zero. Otherwise when an initial dash
+ segment shrinks to zero it will be skipped over. */
+ while (offset > 0.0 && offset >= dash->dashes[i]) {
+ offset -= dash->dashes[i];
+ on = !on;
+ if (++i == dash->num_dashes)
+ i = 0;
+ }
+
+ dash->dash_index = i;
+ dash->dash_on = dash->dash_starts_on = on;
+ dash->dash_remain = dash->dashes[i] - offset;
+}
+
+void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
+{
+ dash->dash_remain -= step;
+ if (dash->dash_remain < CAIRO_FIXED_ERROR_DOUBLE) {
+ if (++dash->dash_index == dash->num_dashes)
+ dash->dash_index = 0;
+
+ dash->dash_on = ! dash->dash_on;
+ dash->dash_remain += dash->dashes[dash->dash_index];
+ }
+}
+
+void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style)
+{
+ dash->dashed = style->dash != NULL;
+ if (! dash->dashed)
+ return;
+
+ dash->dashes = style->dash;
+ dash->num_dashes = style->num_dashes;
+ dash->dash_offset = style->dash_offset;
+
+ _cairo_stroker_dash_start (dash);
+}
diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
new file mode 100755
index 000000000..51c9414c0
--- /dev/null
+++ b/src/cairo-stroke-style.c
@@ -0,0 +1,354 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <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)
+{
+ 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_path_fixed_t *path,
+ const cairo_matrix_t *ctm,
+ double *dx, double *dy)
+{
+ double style_expansion = 0.5;
+
+ if (style->line_cap == CAIRO_LINE_CAP_SQUARE)
+ style_expansion = M_SQRT1_2;
+
+ if (style->line_join == CAIRO_LINE_JOIN_MITER &&
+ ! path->stroke_is_rectilinear &&
+ style_expansion < M_SQRT2 * style->miter_limit)
+ {
+ style_expansion = M_SQRT2 * style->miter_limit;
+ }
+
+ style_expansion *= style->line_width;
+
+ if (_cairo_matrix_has_unity_scale (ctm)) {
+ *dx = *dy = style_expansion;
+ } else {
+ *dx = style_expansion * hypot (ctm->xx, ctm->xy);
+ *dy = style_expansion * hypot (ctm->yy, ctm->yx);
+ }
+}
+
+void
+_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style,
+ const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm,
+ double *dx, double *dy)
+{
+ double style_expansion = 0.5 * style->line_width;
+ if (_cairo_matrix_has_unity_scale (ctm)) {
+ *dx = *dy = style_expansion;
+ } else {
+ *dx = style_expansion * hypot (ctm->xx, ctm->xy);
+ *dy = style_expansion * hypot (ctm->yy, ctm->yx);
+ }
+}
+
+void
+_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style,
+ const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm,
+ double *dx, double *dy)
+{
+ double style_expansion = 0.5;
+
+ if (style->line_join == CAIRO_LINE_JOIN_MITER &&
+ ! path->stroke_is_rectilinear &&
+ style_expansion < M_SQRT2 * style->miter_limit)
+ {
+ style_expansion = M_SQRT2 * style->miter_limit;
+ }
+
+ style_expansion *= style->line_width;
+
+ if (_cairo_matrix_has_unity_scale (ctm)) {
+ *dx = *dy = style_expansion;
+ } else {
+ *dx = style_expansion * hypot (ctm->xx, ctm->xy);
+ *dy = style_expansion * hypot (ctm->yy, ctm->yx);
+ }
+}
+/*
+ * Computes the period of a dashed stroke style.
+ * Returns 0 for non-dashed styles.
+ */
+double
+_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style)
+{
+ double period;
+ unsigned int i;
+
+ period = 0.0;
+ for (i = 0; i < style->num_dashes; i++)
+ period += style->dash[i];
+
+ if (style->num_dashes & 1)
+ period *= 2.0;
+
+ return period;
+}
+
+/*
+ * Coefficient of the linear approximation (minimizing square difference)
+ * of the surface covered by round caps
+ *
+ * This can be computed in the following way:
+ * the area inside the circle with radius w/2 and the region -d/2 <= x <= d/2 is:
+ * f(w,d) = 2 * integrate (sqrt (w*w/4 - x*x), x, -d/2, d/2)
+ * The square difference to a generic linear approximation (c*d) in the range (0,w) would be:
+ * integrate ((f(w,d) - c*d)^2, d, 0, w)
+ * To minimize this difference it is sufficient to find a solution of the differential with
+ * respect to c:
+ * solve ( diff (integrate ((f(w,d) - c*d)^2, d, 0, w), c), c)
+ * Which leads to c = 9/32*pi*w
+ * Since we're not interested in the true area, but just in a coverage extimate,
+ * we always divide the real area by the line width (w).
+ * The same computation for square caps would be
+ * f(w,d) = 2 * integrate(w/2, x, -d/2, d/2)
+ * c = 1*w
+ * but in this case it would not be an approximation, since f is already linear in d.
+ */
+#define ROUND_MINSQ_APPROXIMATION (9*M_PI/32)
+
+/*
+ * Computes the length of the "on" part of a dashed stroke style,
+ * taking into account also line caps.
+ * Returns 0 for non-dashed styles.
+ */
+double
+_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style)
+{
+ double stroked, cap_scale;
+ unsigned int i;
+
+ switch (style->line_cap) {
+ default: ASSERT_NOT_REACHED;
+ case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break;
+ case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break;
+ case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break;
+ }
+
+ stroked = 0.0;
+ if (style->num_dashes & 1) {
+ /* Each dash element is used both as on and as off. The order in which they are summed is
+ * irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */
+ for (i = 0; i < style->num_dashes; i++)
+ stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width);
+ } else {
+ /* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus
+ * their coverage is approximated based on the area covered by the caps of adjacent on dases. */
+ for (i = 0; i + 1 < style->num_dashes; i += 2)
+ stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width);
+ }
+
+ return stroked;
+}
+
+/*
+ * Verifies if _cairo_stroke_style_dash_approximate should be used to generate
+ * an approximation of the dash pattern in the specified style, when used for
+ * stroking a path with the given CTM and tolerance.
+ * Always %FALSE for non-dashed styles.
+ */
+cairo_bool_t
+_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ double tolerance)
+{
+ double period;
+
+ if (! style->num_dashes)
+ return FALSE;
+
+ period = _cairo_stroke_style_dash_period (style);
+ return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance;
+}
+
+/*
+ * Create a 2-dashes approximation of a dashed style, by making the "on" and "off"
+ * parts respect the original ratio.
+ */
+void
+_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ double tolerance,
+ double *dash_offset,
+ double *dashes,
+ unsigned int *num_dashes)
+{
+ double coverage, scale, offset;
+ cairo_bool_t on = TRUE;
+ unsigned int i = 0;
+
+ coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style);
+ coverage = MIN (coverage, 1.0);
+ scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0);
+
+ /* We stop searching for a starting point as soon as the
+ * offset reaches zero. Otherwise when an initial dash
+ * segment shrinks to zero it will be skipped over. */
+ offset = style->dash_offset;
+ while (offset > 0.0 && offset >= style->dash[i]) {
+ offset -= style->dash[i];
+ on = !on;
+ if (++i == style->num_dashes)
+ i = 0;
+ }
+
+ *num_dashes = 2;
+
+ /*
+ * We want to create a new dash pattern with the same relative coverage,
+ * but composed of just 2 elements with total length equal to scale.
+ * Based on the formula in _cairo_stroke_style_dash_stroked:
+ * scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width)
+ * = MIN (dashes[0] + cap_scale * (scale - dashes[0]),
+ * dashes[0] + cap_scale * line_width) =
+ * = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale,
+ * dashes[0] + cap_scale * line_width)
+ *
+ * Solving both cases we get:
+ * dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale)
+ * when scale - dashes[0] <= line_width
+ * dashes[0] = scale * coverage - cap_scale * line_width
+ * when scale - dashes[0] > line_width.
+ *
+ * Comparing the two cases we get:
+ * second > first
+ * second > scale * (coverage - cap_scale) / (1 - cap_scale)
+ * second - cap_scale * second - scale * coverage + scale * cap_scale > 0
+ * (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0
+ * - line_width - second + scale > 0
+ * scale - second > line_width
+ * which is the condition for the second solution to be the valid one.
+ * So when second > first, the second solution is the correct one (i.e.
+ * the solution is always MAX (first, second).
+ */
+ switch (style->line_cap) {
+ default:
+ ASSERT_NOT_REACHED;
+ dashes[0] = 0.0;
+ break;
+
+ case CAIRO_LINE_CAP_BUTT:
+ /* Simplified formula (substituting 0 for cap_scale): */
+ dashes[0] = scale * coverage;
+ break;
+
+ case CAIRO_LINE_CAP_ROUND:
+ dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION),
+ scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width);
+ break;
+
+ case CAIRO_LINE_CAP_SQUARE:
+ /*
+ * Special attention is needed to handle the case cap_scale == 1 (since the first solution
+ * is either indeterminate or -inf in this case). Since dash lengths are always >=0, using
+ * 0 as first solution always leads to the correct solution.
+ */
+ dashes[0] = MAX(0.0, scale * coverage - style->line_width);
+ break;
+ }
+
+ dashes[1] = scale - dashes[0];
+
+ *dash_offset = on ? 0.0 : dashes[0];
+}
diff --git a/src/cairo-surface-backend-private.h b/src/cairo-surface-backend-private.h
new file mode 100755
index 000000000..be624dfbe
--- /dev/null
+++ b/src/cairo-surface-backend-private.h
@@ -0,0 +1,262 @@
+/* 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_BACKEND_PRIVATE_H
+#define CAIRO_SURFACE_BACKEND_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_surface_backend {
+ cairo_surface_type_t type;
+
+ cairo_warn cairo_status_t
+ (*finish) (void *surface);
+
+ cairo_t *
+ (*create_context) (void *surface);
+
+ cairo_surface_t *
+ (*create_similar) (void *surface,
+ cairo_content_t content,
+ int width,
+ int height);
+ cairo_surface_t *
+ (*create_similar_image) (void *surface,
+ cairo_format_t format,
+ int width,
+ int height);
+
+ cairo_image_surface_t *
+ (*map_to_image) (void *surface,
+ const cairo_rectangle_int_t *extents);
+ cairo_int_status_t
+ (*unmap_image) (void *surface,
+ cairo_image_surface_t *image);
+
+ cairo_surface_t *
+ (*source) (void *abstract_surface,
+ cairo_rectangle_int_t *extents);
+
+ cairo_warn cairo_status_t
+ (*acquire_source_image) (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+ cairo_warn void
+ (*release_source_image) (void *abstract_surface,
+ cairo_image_surface_t *image_out,
+ void *image_extra);
+
+ cairo_surface_t *
+ (*snapshot) (void *surface);
+
+ cairo_warn cairo_int_status_t
+ (*copy_page) (void *surface);
+
+ cairo_warn cairo_int_status_t
+ (*show_page) (void *surface);
+
+ /* Get the extents of the current surface. For many surface types
+ * this will be as simple as { x=0, y=0, width=surface->width,
+ * height=surface->height}.
+ *
+ * If this function is not implemented, or if it returns
+ * FALSE the surface is considered to be
+ * boundless and infinite bounds are used for it.
+ */
+ cairo_bool_t
+ (*get_extents) (void *surface,
+ cairo_rectangle_int_t *extents);
+
+ void
+ (*get_font_options) (void *surface,
+ cairo_font_options_t *options);
+
+ cairo_warn cairo_status_t
+ (*flush) (void *surface,
+ unsigned flags);
+
+ cairo_warn cairo_status_t
+ (*mark_dirty_rectangle) (void *surface,
+ int x,
+ int y,
+ int width,
+ int height);
+
+ cairo_warn cairo_int_status_t
+ (*paint) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*mask) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*stroke) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*fill) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*fill_stroke) (void *surface,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ const cairo_path_fixed_t*path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*show_glyphs) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+ cairo_bool_t
+ (*has_show_text_glyphs) (void *surface);
+
+ cairo_warn cairo_int_status_t
+ (*show_text_glyphs) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+ const char **
+ (*get_supported_mime_types) (void *surface);
+
+ cairo_surface_t *
+ (*get_shadow_surface) (void *surface,
+ const cairo_bool_t has_blur,
+ int width,
+ int height,
+ int *width_out,
+ int *height_out);
+
+ cairo_surface_t *
+ (*get_glyph_shadow_surface) (void *surface,
+ int width,
+ int height,
+ cairo_bool_t for_source);
+
+ cairo_surface_t *
+ (*get_shadow_mask_surface) (void *surface,
+ int width,
+ int height,
+ unsigned index);
+
+ cairo_surface_t *
+ (*get_glyph_shadow_mask_surface) (void *surface,
+ int width,
+ int height,
+ unsigned index);
+
+ cairo_status_t
+ (*shadow_cache_acquire) (void *surface);
+
+ void
+ (*shadow_cache_release) (void *surface);
+
+ cairo_list_t *
+ (*get_shadow_cache) (void *surface);
+
+ unsigned long *
+ (*get_shadow_cache_size) (void *surface);
+
+ cairo_bool_t
+ (*has_shadow_cache) (void *surface);
+};
+
+cairo_private cairo_status_t
+_cairo_surface_default_acquire_source_image (void *surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+cairo_private void
+_cairo_surface_default_release_source_image (void *surface,
+ cairo_image_surface_t *image,
+ void *image_extra);
+
+cairo_private cairo_surface_t *
+_cairo_surface_default_source (void *surface,
+ cairo_rectangle_int_t *extents);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_BACKEND_PRIVATE_H */
diff --git a/src/cairo-surface-clipper-private.h b/src/cairo-surface-clipper-private.h
new file mode 100755
index 000000000..e5b00af7c
--- /dev/null
+++ b/src/cairo-surface-clipper-private.h
@@ -0,0 +1,71 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <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_surface_clipper_intersect_clip_path_func_t intersect_clip_path;
+};
+
+cairo_private cairo_status_t
+_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper,
+ cairo_surface_clipper_intersect_clip_path_func_t intersect);
+
+cairo_private void
+_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_CLIPPER_PRIVATE_H */
diff --git a/src/cairo-surface-clipper.c b/src/cairo-surface-clipper.c
new file mode 100755
index 000000000..5309362c6
--- /dev/null
+++ b/src/cairo-surface-clipper.c
@@ -0,0 +1,196 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-inline.h"
+#include "cairo-surface-clipper-private.h"
+
+/* A collection of routines to facilitate vector surface clipping */
+
+/* XXX Eliminate repeated paths and nested clips */
+
+static cairo_status_t
+_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
+ const cairo_box_t *box)
+{
+ cairo_status_t status;
+
+ status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_path_fixed_close_path (path);
+}
+
+static cairo_status_t
+_cairo_surface_clipper_intersect_clip_boxes (cairo_surface_clipper_t *clipper,
+ const cairo_clip_t *clip)
+{
+ cairo_path_fixed_t path;
+ cairo_status_t status;
+ int i;
+
+ if (clip->num_boxes == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Reconstruct the path for the clip boxes.
+ * XXX maybe a new clipper callback?
+ */
+
+ _cairo_path_fixed_init (&path);
+ for (i = 0; i < clip->num_boxes; i++) {
+ status = _cairo_path_fixed_add_box (&path, &clip->boxes[i]);
+ if (unlikely (status)) {
+ _cairo_path_fixed_fini (&path);
+ return status;
+ }
+ }
+
+ status = clipper->intersect_clip_path (clipper, &path,
+ CAIRO_FILL_RULE_WINDING,
+ 0.,
+ CAIRO_ANTIALIAS_DEFAULT);
+ _cairo_path_fixed_fini (&path);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper,
+ cairo_clip_path_t *clip_path,
+ cairo_clip_path_t *end)
+{
+ cairo_status_t status;
+
+ if (clip_path->prev != end) {
+ status =
+ _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+ clip_path->prev,
+ end);
+ if (unlikely (status))
+ return status;
+ }
+
+ return clipper->intersect_clip_path (clipper,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias);
+}
+
+cairo_status_t
+_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_bool_t incremental = FALSE;
+
+ if (_cairo_clip_equal (clip, clipper->clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* all clipped out state should never propagate this far */
+ assert (!_cairo_clip_is_all_clipped (clip));
+
+ /* XXX Is this an incremental clip? */
+ if (clipper->clip && clip &&
+ clip->num_boxes == clipper->clip->num_boxes &&
+ memcmp (clip->boxes, clipper->clip->boxes,
+ sizeof (cairo_box_t) * clip->num_boxes) == 0)
+ {
+ cairo_clip_path_t *clip_path = clip->path;
+ while (clip_path != NULL && clip_path != clipper->clip->path)
+ clip_path = clip_path->prev;
+
+ if (clip_path) {
+ incremental = TRUE;
+ status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+ clip->path,
+ clipper->clip->path);
+ }
+ }
+
+ _cairo_clip_destroy (clipper->clip);
+ clipper->clip = _cairo_clip_copy (clip);
+
+ if (incremental)
+ return status;
+
+ status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0);
+ if (unlikely (status))
+ return status;
+
+ if (clip == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_clipper_intersect_clip_boxes (clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ if (clip->path != NULL) {
+ status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+ clip->path,
+ NULL);
+ }
+
+ return status;
+}
+
+void
+_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper,
+ cairo_surface_clipper_intersect_clip_path_func_t func)
+{
+ clipper->clip = NULL;
+ clipper->intersect_clip_path = func;
+}
+
+void
+_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper)
+{
+ _cairo_clip_destroy (clipper->clip);
+ clipper->clip = NULL;
+}
diff --git a/src/cairo-surface-fallback-private.h b/src/cairo-surface-fallback-private.h
new file mode 100755
index 000000000..ecf7b0edf
--- /dev/null
+++ b/src/cairo-surface-fallback-private.h
@@ -0,0 +1,95 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H
+#define CAIRO_SURFACE_FALLBACK_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_FALLBACK_PRIVATE_H */
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
new file mode 100755
index 000000000..a0af15969
--- /dev/null
+++ b/src/cairo-surface-fallback.c
@@ -0,0 +1,115 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-surface-fallback-private.h"
+
+cairo_int_status_t
+_cairo_surface_fallback_paint (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ return _cairo_compositor_paint (&_cairo_fallback_compositor,
+ surface, op, source, clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_mask (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ return _cairo_compositor_mask (&_cairo_fallback_compositor,
+ surface, op, source, mask, clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_stroke (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t*path,
+ const cairo_stroke_style_t*style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ return _cairo_compositor_stroke (&_cairo_fallback_compositor,
+ surface, op, source, path,
+ style, ctm,ctm_inverse,
+ tolerance, antialias, clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_fill (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ return _cairo_compositor_fill (&_cairo_fallback_compositor,
+ surface, op, source, path,
+ fill_rule, tolerance, antialias,
+ clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ return _cairo_compositor_glyphs (&_cairo_fallback_compositor,
+ surface, op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+}
diff --git a/src/cairo-surface-inline.h b/src/cairo-surface-inline.h
new file mode 100755
index 000000000..85fbc9192
--- /dev/null
+++ b/src/cairo-surface-inline.h
@@ -0,0 +1,60 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SURFACE_INLINE_H
+#define CAIRO_SURFACE_INLINE_H
+
+#include "cairo-surface-private.h"
+
+static inline cairo_status_t
+__cairo_surface_flush (cairo_surface_t *surface, unsigned flags)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ if (surface->backend->flush)
+ status = surface->backend->flush (surface, flags);
+ return status;
+}
+
+static inline cairo_surface_t *
+_cairo_surface_reference (cairo_surface_t *surface)
+{
+ if (!CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ _cairo_reference_count_inc (&surface->ref_count);
+ return surface;
+}
+
+#endif /* CAIRO_SURFACE_INLINE_H */
diff --git a/src/cairo-surface-observer-inline.h b/src/cairo-surface-observer-inline.h
new file mode 100755
index 000000000..07b94770d
--- /dev/null
+++ b/src/cairo-surface-observer-inline.h
@@ -0,0 +1,59 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_OBSERVER_INLINE_H
+#define CAIRO_SURFACE_OBSERVER_INLINE_H
+
+#include "cairo-surface-observer-private.h"
+
+static inline cairo_surface_t *
+_cairo_surface_observer_get_target (cairo_surface_t *surface)
+{
+ return ((cairo_surface_observer_t *) surface)->target;
+}
+
+static inline cairo_bool_t
+_cairo_surface_is_observer (cairo_surface_t *surface)
+{
+ return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER;
+}
+
+static inline cairo_bool_t
+_cairo_device_is_observer (cairo_device_t *device)
+{
+ return device->backend->type == (cairo_device_type_t)CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER;
+}
+
+#endif /* CAIRO_SURFACE_OBSERVER_INLINE_H */
diff --git a/src/cairo-surface-observer-private.h b/src/cairo-surface-observer-private.h
new file mode 100755
index 000000000..6ed0c18d1
--- /dev/null
+++ b/src/cairo-surface-observer-private.h
@@ -0,0 +1,208 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_OBSERVER_PRIVATE_H
+#define CAIRO_SURFACE_OBSERVER_PRIVATE_H
+
+#include "cairoint.h"
+
+#include "cairo-device-private.h"
+#include "cairo-list-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-time-private.h"
+
+struct stat {
+ double min, max, sum, sum_sq;
+ unsigned count;
+};
+
+#define NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY+1)
+#define NUM_CAPS (CAIRO_LINE_CAP_SQUARE+1)
+#define NUM_JOINS (CAIRO_LINE_JOIN_BEVEL+1)
+#define NUM_ANTIALIAS (CAIRO_ANTIALIAS_BEST+1)
+#define NUM_FILL_RULE (CAIRO_FILL_RULE_EVEN_ODD+1)
+
+struct extents {
+ struct stat area;
+ unsigned int bounded, unbounded;
+};
+
+struct pattern {
+ unsigned int type[8]; /* native/record/other surface/gradients */
+};
+
+struct path {
+ unsigned int type[5]; /* empty/pixel/rectilinear/straight/curved */
+};
+
+struct clip {
+ unsigned int type[6]; /* none, region, boxes, single path, polygon, general */
+};
+
+typedef struct _cairo_observation cairo_observation_t;
+typedef struct _cairo_observation_record cairo_observation_record_t;
+typedef struct _cairo_device_observer cairo_device_observer_t;
+
+struct _cairo_observation_record {
+ cairo_content_t target_content;
+ int target_width;
+ int target_height;
+
+ int index;
+ cairo_operator_t op;
+ int source;
+ int mask;
+ int num_glyphs;
+ int path;
+ int fill_rule;
+ double tolerance;
+ int antialias;
+ int clip;
+ cairo_time_t elapsed;
+};
+
+struct _cairo_observation {
+ int num_surfaces;
+ int num_contexts;
+ int num_sources_acquired;
+
+ /* XXX put interesting stats here! */
+
+ struct paint {
+ cairo_time_t elapsed;
+ unsigned int count;
+ struct extents extents;
+ unsigned int operators[NUM_OPERATORS];
+ struct pattern source;
+ struct clip clip;
+ unsigned int noop;
+
+ cairo_observation_record_t slowest;
+ } paint;
+
+ struct mask {
+ cairo_time_t elapsed;
+ unsigned int count;
+ struct extents extents;
+ unsigned int operators[NUM_OPERATORS];
+ struct pattern source;
+ struct pattern mask;
+ struct clip clip;
+ unsigned int noop;
+
+ cairo_observation_record_t slowest;
+ } mask;
+
+ struct fill {
+ cairo_time_t elapsed;
+ unsigned int count;
+ struct extents extents;
+ unsigned int operators[NUM_OPERATORS];
+ struct pattern source;
+ struct path path;
+ unsigned int antialias[NUM_ANTIALIAS];
+ unsigned int fill_rule[NUM_FILL_RULE];
+ struct clip clip;
+ unsigned int noop;
+
+ cairo_observation_record_t slowest;
+ } fill;
+
+ struct stroke {
+ cairo_time_t elapsed;
+ unsigned int count;
+ struct extents extents;
+ unsigned int operators[NUM_OPERATORS];
+ unsigned int caps[NUM_CAPS];
+ unsigned int joins[NUM_CAPS];
+ unsigned int antialias[NUM_ANTIALIAS];
+ struct pattern source;
+ struct path path;
+ struct stat line_width;
+ struct clip clip;
+ unsigned int noop;
+
+ cairo_observation_record_t slowest;
+ } stroke;
+
+ struct glyphs {
+ cairo_time_t elapsed;
+ unsigned int count;
+ struct extents extents;
+ unsigned int operators[NUM_OPERATORS];
+ struct pattern source;
+ struct clip clip;
+ unsigned int noop;
+
+ cairo_observation_record_t slowest;
+ } glyphs;
+
+ cairo_array_t timings;
+ cairo_recording_surface_t *record;
+};
+
+struct _cairo_device_observer {
+ cairo_device_t base;
+ cairo_device_t *target;
+
+ cairo_observation_t log;
+};
+
+struct callback_list {
+ cairo_list_t link;
+
+ cairo_surface_observer_callback_t func;
+ void *data;
+};
+
+struct _cairo_surface_observer {
+ cairo_surface_t base;
+ cairo_surface_t *target;
+
+ cairo_observation_t log;
+
+ cairo_list_t paint_callbacks;
+ cairo_list_t mask_callbacks;
+ cairo_list_t fill_callbacks;
+ cairo_list_t stroke_callbacks;
+ cairo_list_t glyphs_callbacks;
+
+ cairo_list_t flush_callbacks;
+ cairo_list_t finish_callbacks;
+};
+
+#endif /* CAIRO_SURFACE_OBSERVER_PRIVATE_H */
diff --git a/src/cairo-surface-observer.c b/src/cairo-surface-observer.c
new file mode 100755
index 000000000..8bbd6109c
--- /dev/null
+++ b/src/cairo-surface-observer.c
@@ -0,0 +1,2104 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-observer-inline.h"
+
+#include "cairo-array-private.h"
+#include "cairo-combsort-inline.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-pattern-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-subsurface-inline.h"
+#include "cairo-reference-count-private.h"
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+#include "cairo-script-private.h"
+#endif
+
+static const cairo_surface_backend_t _cairo_surface_observer_backend;
+
+/* observation/stats */
+
+static void init_stats (struct stat *s)
+{
+ s->min = HUGE_VAL;
+ s->max = -HUGE_VAL;
+}
+
+static void init_extents (struct extents *e)
+{
+ init_stats (&e->area);
+}
+
+static void init_pattern (struct pattern *p)
+{
+}
+
+static void init_path (struct path *p)
+{
+}
+
+static void init_clip (struct clip *c)
+{
+}
+
+static void init_paint (struct paint *p)
+{
+ init_extents (&p->extents);
+ init_pattern (&p->source);
+ init_clip (&p->clip);
+}
+
+static void init_mask (struct mask *m)
+{
+ init_extents (&m->extents);
+ init_pattern (&m->source);
+ init_pattern (&m->mask);
+ init_clip (&m->clip);
+}
+
+static void init_fill (struct fill *f)
+{
+ init_extents (&f->extents);
+ init_pattern (&f->source);
+ init_path (&f->path);
+ init_clip (&f->clip);
+}
+
+static void init_stroke (struct stroke *s)
+{
+ init_extents (&s->extents);
+ init_pattern (&s->source);
+ init_path (&s->path);
+ init_clip (&s->clip);
+}
+
+static void init_glyphs (struct glyphs *g)
+{
+ init_extents (&g->extents);
+ init_pattern (&g->source);
+ init_clip (&g->clip);
+}
+
+static cairo_status_t
+log_init (cairo_observation_t *log,
+ cairo_bool_t record)
+{
+ memset (log, 0, sizeof(*log));
+
+ init_paint (&log->paint);
+ init_mask (&log->mask);
+ init_fill (&log->fill);
+ init_stroke (&log->stroke);
+ init_glyphs (&log->glyphs);
+
+ _cairo_array_init (&log->timings, sizeof (cairo_observation_record_t));
+
+ if (record) {
+ log->record = (cairo_recording_surface_t *)
+ cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+ if (unlikely (log->record->base.status))
+ return log->record->base.status;
+
+ log->record->optimize_clears = FALSE;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+log_fini (cairo_observation_t *log)
+{
+ _cairo_array_fini (&log->timings);
+ cairo_surface_destroy (&log->record->base);
+}
+
+static cairo_surface_t*
+get_pattern_surface (const cairo_pattern_t *pattern)
+{
+ return ((cairo_surface_pattern_t *)pattern)->surface;
+}
+
+static int
+classify_pattern (const cairo_pattern_t *pattern,
+ const cairo_surface_t *target)
+{
+ int classify;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ if (get_pattern_surface (pattern)->type == target->type)
+ classify = 0;
+ else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING)
+ classify = 1;
+ else
+ classify = 2;
+ break;
+ default:
+ case CAIRO_PATTERN_TYPE_SOLID:
+ classify = 3;
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ classify = 4;
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ classify = 5;
+ break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ classify = 6;
+ break;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ classify = 7;
+ break;
+ }
+ return classify;
+}
+
+static void
+add_pattern (struct pattern *stats,
+ const cairo_pattern_t *pattern,
+ const cairo_surface_t *target)
+{
+ stats->type[classify_pattern(pattern, target)]++;
+}
+
+static int
+classify_path (const cairo_path_fixed_t *path,
+ cairo_bool_t is_fill)
+{
+ int classify;
+
+ /* XXX improve for stroke */
+ classify = -1;
+ if (is_fill) {
+ if (path->fill_is_empty)
+ classify = 0;
+ else if (_cairo_path_fixed_fill_is_rectilinear (path))
+ classify = path->fill_maybe_region ? 1 : 2;
+ } else {
+ if (_cairo_path_fixed_stroke_is_rectilinear (path))
+ classify = 2;
+ }
+ if (classify == -1)
+ classify = 3 + (path->has_curve_to != 0);
+
+ return classify;
+}
+
+static void
+add_path (struct path *stats,
+ const cairo_path_fixed_t *path,
+ cairo_bool_t is_fill)
+{
+ stats->type[classify_path(path, is_fill)]++;
+}
+
+static int
+classify_clip (const cairo_clip_t *clip)
+{
+ int classify;
+
+ if (clip == NULL)
+ classify = 0;
+ else if (_cairo_clip_is_region (clip))
+ classify = 1;
+ else if (clip->path == NULL)
+ classify = 2;
+ else if (clip->path->prev == NULL)
+ classify = 3;
+ else if (_cairo_clip_is_polygon (clip))
+ classify = 4;
+ else
+ classify = 5;
+
+ return classify;
+}
+
+static void
+add_clip (struct clip *stats,
+ const cairo_clip_t *clip)
+{
+ stats->type[classify_clip (clip)]++;
+}
+
+static void
+stats_add (struct stat *s, double v)
+{
+ if (v < s->min)
+ s->min = v;
+ if (v > s->max)
+ s->max = v;
+ s->sum += v;
+ s->sum_sq += v*v;
+ s->count++;
+}
+
+static void
+add_extents (struct extents *stats,
+ const cairo_composite_rectangles_t *extents)
+{
+ const cairo_rectangle_int_t *r = extents->is_bounded ? &extents->bounded :&extents->unbounded;
+ stats_add (&stats->area, r->width * r->height);
+ stats->bounded += extents->is_bounded != 0;
+ stats->unbounded += extents->is_bounded == 0;
+}
+
+/* device interface */
+
+static void
+_cairo_device_observer_lock (void *_device)
+{
+ cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+ cairo_status_t ignored;
+
+ /* cairo_device_acquire() can fail for nil and finished
+ * devices. We don't care about observing them. */
+ ignored = cairo_device_acquire (device->target);
+}
+
+static void
+_cairo_device_observer_unlock (void *_device)
+{
+ cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+ cairo_device_release (device->target);
+}
+
+static cairo_status_t
+_cairo_device_observer_flush (void *_device)
+{
+ cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+
+ if (device->target == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ cairo_device_flush (device->target);
+ return device->target->status;
+}
+
+static void
+_cairo_device_observer_finish (void *_device)
+{
+ cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+ log_fini (&device->log);
+ cairo_device_finish (device->target);
+}
+
+static void
+_cairo_device_observer_destroy (void *_device)
+{
+ cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+ cairo_device_destroy (device->target);
+ free (device);
+}
+
+static const cairo_device_backend_t _cairo_device_observer_backend = {
+ CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER,
+
+ _cairo_device_observer_lock,
+ _cairo_device_observer_unlock,
+
+ _cairo_device_observer_flush,
+ _cairo_device_observer_finish,
+ _cairo_device_observer_destroy,
+};
+
+static cairo_device_t *
+_cairo_device_create_observer_internal (cairo_device_t *target,
+ cairo_bool_t record)
+{
+ cairo_device_observer_t *device;
+ cairo_status_t status;
+
+ device = malloc (sizeof (cairo_device_observer_t));
+ if (unlikely (device == NULL))
+ return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_device_init (&device->base, &_cairo_device_observer_backend);
+ status = log_init (&device->log, record);
+ if (unlikely (status)) {
+ free (device);
+ return _cairo_device_create_in_error (status);
+ }
+
+ device->target = cairo_device_reference (target);
+
+ return &device->base;
+}
+
+/* surface interface */
+
+static cairo_device_observer_t *
+to_device (cairo_surface_observer_t *suface)
+{
+ return (cairo_device_observer_t *)suface->base.device;
+}
+
+static cairo_surface_t *
+_cairo_surface_create_observer_internal (cairo_device_t *device,
+ cairo_surface_t *target)
+{
+ cairo_surface_observer_t *surface;
+ cairo_status_t status;
+
+ surface = malloc (sizeof (cairo_surface_observer_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_surface_observer_backend, device,
+ target->content);
+
+ status = log_init (&surface->log,
+ ((cairo_device_observer_t *)device)->log.record != NULL);
+ if (unlikely (status)) {
+ free (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ surface->target = cairo_surface_reference (target);
+ surface->base.type = surface->target->type;
+ surface->base.is_clear = surface->target->is_clear;
+
+ cairo_list_init (&surface->paint_callbacks);
+ cairo_list_init (&surface->mask_callbacks);
+ cairo_list_init (&surface->fill_callbacks);
+ cairo_list_init (&surface->stroke_callbacks);
+ cairo_list_init (&surface->glyphs_callbacks);
+
+ cairo_list_init (&surface->flush_callbacks);
+ cairo_list_init (&surface->finish_callbacks);
+
+ surface->log.num_surfaces++;
+ to_device (surface)->log.num_surfaces++;
+
+ return &surface->base;
+}
+
+static inline void
+do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head)
+{
+ struct callback_list *cb;
+
+ cairo_list_foreach_entry (cb, struct callback_list, head, link)
+ cb->func (&surface->base, surface->target, cb->data);
+}
+
+
+static cairo_status_t
+_cairo_surface_observer_finish (void *abstract_surface)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+
+ do_callbacks (surface, &surface->finish_callbacks);
+
+ cairo_surface_destroy (surface->target);
+ log_fini (&surface->log);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_surface_observer_t *other = abstract_other;
+ cairo_surface_t *target, *surface;
+
+ target = NULL;
+ if (other->target->backend->create_similar)
+ target = other->target->backend->create_similar (other->target, content,
+ width, height);
+ if (target == NULL)
+ target = _cairo_image_surface_create_with_content (content,
+ width, height);
+
+ surface = _cairo_surface_create_observer_internal (other->base.device,
+ target);
+ cairo_surface_destroy (target);
+
+ return surface;
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_create_similar_image (void *other,
+ cairo_format_t format,
+ int width, int height)
+{
+ cairo_surface_observer_t *surface = other;
+
+ if (surface->target->backend->create_similar_image)
+ return surface->target->backend->create_similar_image (surface->target,
+ format,
+ width, height);
+
+ return NULL;
+}
+
+static cairo_image_surface_t *
+_cairo_surface_observer_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ return _cairo_surface_map_to_image (surface->target, extents);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ return _cairo_surface_unmap_image (surface->target, image);
+}
+
+static void
+record_target (cairo_observation_record_t *r,
+ cairo_surface_t *target)
+{
+ cairo_rectangle_int_t extents;
+
+ r->target_content = target->content;
+ if (_cairo_surface_get_extents (target, &extents)) {
+ r->target_width = extents.width;
+ r->target_height = extents.height;
+ } else {
+ r->target_width = -1;
+ r->target_height = -1;
+ }
+}
+
+static cairo_observation_record_t *
+record_paint (cairo_observation_record_t *r,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ record_target (r, target);
+
+ r->op = op;
+ r->source = classify_pattern (source, target);
+ r->mask = -1;
+ r->num_glyphs = -1;
+ r->path = -1;
+ r->fill_rule = -1;
+ r->tolerance = -1;
+ r->antialias = -1;
+ r->clip = classify_clip (clip);
+ r->elapsed = elapsed;
+
+ return r;
+}
+
+static cairo_observation_record_t *
+record_mask (cairo_observation_record_t *r,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ record_target (r, target);
+
+ r->op = op;
+ r->source = classify_pattern (source, target);
+ r->mask = classify_pattern (mask, target);
+ r->num_glyphs = -1;
+ r->path = -1;
+ r->fill_rule = -1;
+ r->tolerance = -1;
+ r->antialias = -1;
+ r->clip = classify_clip (clip);
+ r->elapsed = elapsed;
+
+ return r;
+}
+
+static cairo_observation_record_t *
+record_fill (cairo_observation_record_t *r,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ record_target (r, target);
+
+ r->op = op;
+ r->source = classify_pattern (source, target);
+ r->mask = -1;
+ r->num_glyphs = -1;
+ r->path = classify_path (path, TRUE);
+ r->fill_rule = fill_rule;
+ r->tolerance = tolerance;
+ r->antialias = antialias;
+ r->clip = classify_clip (clip);
+ r->elapsed = elapsed;
+
+ return r;
+}
+
+static cairo_observation_record_t *
+record_stroke (cairo_observation_record_t *r,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ record_target (r, target);
+
+ r->op = op;
+ r->source = classify_pattern (source, target);
+ r->mask = -1;
+ r->num_glyphs = -1;
+ r->path = classify_path (path, FALSE);
+ r->fill_rule = -1;
+ r->tolerance = tolerance;
+ r->antialias = antialias;
+ r->clip = classify_clip (clip);
+ r->elapsed = elapsed;
+
+ return r;
+}
+
+static cairo_observation_record_t *
+record_glyphs (cairo_observation_record_t *r,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ record_target (r, target);
+
+ r->op = op;
+ r->source = classify_pattern (source, target);
+ r->mask = -1;
+ r->path = -1;
+ r->num_glyphs = num_glyphs;
+ r->fill_rule = -1;
+ r->tolerance = -1;
+ r->antialias = -1;
+ r->clip = classify_clip (clip);
+ r->elapsed = elapsed;
+
+ return r;
+}
+
+static void
+add_record (cairo_observation_t *log,
+ cairo_observation_record_t *r)
+{
+ cairo_int_status_t status;
+
+ r->index = log->record ? log->record->commands.num_elements : 0;
+
+ status = _cairo_array_append (&log->timings, r);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+}
+
+static void
+sync (cairo_surface_t *target, int x, int y)
+{
+ cairo_rectangle_int_t extents;
+
+ extents.x = x;
+ extents.y = y;
+ extents.width = 1;
+ extents.height = 1;
+
+ _cairo_surface_unmap_image (target,
+ _cairo_surface_map_to_image (target,
+ &extents));
+}
+
+static void
+midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
+{
+ *x = extents->bounded.x + extents->bounded.width / 2;
+ *y = extents->bounded.y + extents->bounded.height / 2;
+}
+
+static void
+add_record_paint (cairo_observation_t *log,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ cairo_observation_record_t record;
+ cairo_int_status_t status;
+
+ add_record (log,
+ record_paint (&record, target, op, source, clip, elapsed));
+
+ /* We have to bypass the high-level surface layer in case it tries to be
+ * too smart and discard operations; we need to record exactly what just
+ * happened on the target.
+ */
+ if (log->record) {
+ status = log->record->base.backend->paint (&log->record->base,
+ op, source, clip);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed))
+ log->paint.slowest = record;
+ log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_device_observer_t *device = to_device (surface);
+ cairo_composite_rectangles_t composite;
+ cairo_int_status_t status;
+ cairo_time_t t;
+ int x, y;
+
+ /* XXX device locking */
+
+ surface->log.paint.count++;
+ surface->log.paint.operators[op]++;
+ add_pattern (&surface->log.paint.source, source, surface->target);
+ add_clip (&surface->log.paint.clip, clip);
+
+ device->log.paint.count++;
+ device->log.paint.operators[op]++;
+ add_pattern (&device->log.paint.source, source, surface->target);
+ add_clip (&device->log.paint.clip, clip);
+
+ status = _cairo_composite_rectangles_init_for_paint (&composite,
+ surface->target,
+ op, source,
+ clip);
+ if (unlikely (status)) {
+ surface->log.paint.noop++;
+ device->log.paint.noop++;
+ return status;
+ }
+
+ midpt (&composite, &x, &y);
+
+ add_extents (&surface->log.paint.extents, &composite);
+ add_extents (&device->log.paint.extents, &composite);
+ _cairo_composite_rectangles_fini (&composite);
+
+ t = _cairo_time_get ();
+ status = _cairo_surface_paint (surface->target,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ sync (surface->target, x, y);
+ t = _cairo_time_get_delta (t);
+
+ add_record_paint (&surface->log, surface->target, op, source, clip, t);
+ add_record_paint (&device->log, surface->target, op, source, clip, t);
+
+ do_callbacks (surface, &surface->paint_callbacks);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_mask (cairo_observation_t *log,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ cairo_observation_record_t record;
+ cairo_int_status_t status;
+
+ add_record (log,
+ record_mask (&record, target, op, source, mask, clip, elapsed));
+
+ if (log->record) {
+ status = log->record->base.backend->mask (&log->record->base,
+ op, source, mask, clip);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed))
+ log->mask.slowest = record;
+ log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_device_observer_t *device = to_device (surface);
+ cairo_composite_rectangles_t composite;
+ cairo_int_status_t status;
+ cairo_time_t t;
+ int x, y;
+
+ surface->log.mask.count++;
+ surface->log.mask.operators[op]++;
+ add_pattern (&surface->log.mask.source, source, surface->target);
+ add_pattern (&surface->log.mask.mask, mask, surface->target);
+ add_clip (&surface->log.mask.clip, clip);
+
+ device->log.mask.count++;
+ device->log.mask.operators[op]++;
+ add_pattern (&device->log.mask.source, source, surface->target);
+ add_pattern (&device->log.mask.mask, mask, surface->target);
+ add_clip (&device->log.mask.clip, clip);
+
+ status = _cairo_composite_rectangles_init_for_mask (&composite,
+ surface->target,
+ op, source, mask,
+ clip);
+ if (unlikely (status)) {
+ surface->log.mask.noop++;
+ device->log.mask.noop++;
+ return status;
+ }
+
+ midpt (&composite, &x, &y);
+
+ add_extents (&surface->log.mask.extents, &composite);
+ add_extents (&device->log.mask.extents, &composite);
+ _cairo_composite_rectangles_fini (&composite);
+
+ t = _cairo_time_get ();
+ status = _cairo_surface_mask (surface->target,
+ op, source, mask,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ sync (surface->target, x, y);
+ t = _cairo_time_get_delta (t);
+
+ add_record_mask (&surface->log,
+ surface->target, op, source, mask, clip,
+ t);
+ add_record_mask (&device->log,
+ surface->target, op, source, mask, clip,
+ t);
+
+ do_callbacks (surface, &surface->mask_callbacks);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_fill (cairo_observation_t *log,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ cairo_observation_record_t record;
+ cairo_int_status_t status;
+
+ add_record (log,
+ record_fill (&record,
+ target, op, source,
+ path, fill_rule, tolerance, antialias,
+ clip, elapsed));
+
+ if (log->record) {
+ status = log->record->base.backend->fill (&log->record->base,
+ op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed))
+ log->fill.slowest = record;
+ log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_device_observer_t *device = to_device (surface);
+ cairo_composite_rectangles_t composite;
+ cairo_int_status_t status;
+ cairo_time_t t;
+ int x, y;
+
+ surface->log.fill.count++;
+ surface->log.fill.operators[op]++;
+ surface->log.fill.fill_rule[fill_rule]++;
+ surface->log.fill.antialias[antialias]++;
+ add_pattern (&surface->log.fill.source, source, surface->target);
+ add_path (&surface->log.fill.path, path, TRUE);
+ add_clip (&surface->log.fill.clip, clip);
+
+ device->log.fill.count++;
+ device->log.fill.operators[op]++;
+ device->log.fill.fill_rule[fill_rule]++;
+ device->log.fill.antialias[antialias]++;
+ add_pattern (&device->log.fill.source, source, surface->target);
+ add_path (&device->log.fill.path, path, TRUE);
+ add_clip (&device->log.fill.clip, clip);
+
+ status = _cairo_composite_rectangles_init_for_fill (&composite,
+ surface->target,
+ op, source, path,
+ clip);
+ if (unlikely (status)) {
+ surface->log.fill.noop++;
+ device->log.fill.noop++;
+ return status;
+ }
+
+ midpt (&composite, &x, &y);
+
+ add_extents (&surface->log.fill.extents, &composite);
+ add_extents (&device->log.fill.extents, &composite);
+ _cairo_composite_rectangles_fini (&composite);
+
+ t = _cairo_time_get ();
+ status = _cairo_surface_fill (surface->target,
+ op, source, path,
+ fill_rule, tolerance, antialias,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ sync (surface->target, x, y);
+ t = _cairo_time_get_delta (t);
+
+ add_record_fill (&surface->log,
+ surface->target, op, source, path,
+ fill_rule, tolerance, antialias,
+ clip, t);
+
+ add_record_fill (&device->log,
+ surface->target, op, source, path,
+ fill_rule, tolerance, antialias,
+ clip, t);
+
+ do_callbacks (surface, &surface->fill_callbacks);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_stroke (cairo_observation_t *log,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ cairo_observation_record_t record;
+ cairo_int_status_t status;
+
+ add_record (log,
+ record_stroke (&record,
+ target, op, source,
+ path, style, ctm,ctm_inverse,
+ tolerance, antialias,
+ clip, elapsed));
+
+ if (log->record) {
+ status = log->record->base.backend->stroke (&log->record->base,
+ op, source,
+ path, style, ctm,ctm_inverse,
+ tolerance, antialias,
+ clip);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed))
+ log->stroke.slowest = record;
+ log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_device_observer_t *device = to_device (surface);
+ cairo_composite_rectangles_t composite;
+ cairo_int_status_t status;
+ cairo_time_t t;
+ int x, y;
+
+ surface->log.stroke.count++;
+ surface->log.stroke.operators[op]++;
+ surface->log.stroke.antialias[antialias]++;
+ surface->log.stroke.caps[style->line_cap]++;
+ surface->log.stroke.joins[style->line_join]++;
+ add_pattern (&surface->log.stroke.source, source, surface->target);
+ add_path (&surface->log.stroke.path, path, FALSE);
+ add_clip (&surface->log.stroke.clip, clip);
+
+ device->log.stroke.count++;
+ device->log.stroke.operators[op]++;
+ device->log.stroke.antialias[antialias]++;
+ device->log.stroke.caps[style->line_cap]++;
+ device->log.stroke.joins[style->line_join]++;
+ add_pattern (&device->log.stroke.source, source, surface->target);
+ add_path (&device->log.stroke.path, path, FALSE);
+ add_clip (&device->log.stroke.clip, clip);
+
+ status = _cairo_composite_rectangles_init_for_stroke (&composite,
+ surface->target,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status)) {
+ surface->log.stroke.noop++;
+ device->log.stroke.noop++;
+ return status;
+ }
+
+ midpt (&composite, &x, &y);
+
+ add_extents (&surface->log.stroke.extents, &composite);
+ add_extents (&device->log.stroke.extents, &composite);
+ _cairo_composite_rectangles_fini (&composite);
+
+ t = _cairo_time_get ();
+ status = _cairo_surface_stroke (surface->target,
+ op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ sync (surface->target, x, y);
+ t = _cairo_time_get_delta (t);
+
+ add_record_stroke (&surface->log,
+ surface->target, op, source, path,
+ style, ctm,ctm_inverse,
+ tolerance, antialias,
+ clip, t);
+
+ add_record_stroke (&device->log,
+ surface->target, op, source, path,
+ style, ctm,ctm_inverse,
+ tolerance, antialias,
+ clip, t);
+
+ do_callbacks (surface, &surface->stroke_callbacks);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_glyphs (cairo_observation_t *log,
+ cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip,
+ cairo_time_t elapsed)
+{
+ cairo_observation_record_t record;
+ cairo_int_status_t status;
+
+ add_record (log,
+ record_glyphs (&record,
+ target, op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip, elapsed));
+
+ if (log->record) {
+ status = log->record->base.backend->show_text_glyphs (&log->record->base,
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font,
+ clip);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed))
+ log->glyphs.slowest = record;
+ log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_device_observer_t *device = to_device (surface);
+ cairo_composite_rectangles_t composite;
+ cairo_int_status_t status;
+ cairo_glyph_t *dev_glyphs;
+ cairo_time_t t;
+ int x, y;
+
+ surface->log.glyphs.count++;
+ surface->log.glyphs.operators[op]++;
+ add_pattern (&surface->log.glyphs.source, source, surface->target);
+ add_clip (&surface->log.glyphs.clip, clip);
+
+ device->log.glyphs.count++;
+ device->log.glyphs.operators[op]++;
+ add_pattern (&device->log.glyphs.source, source, surface->target);
+ add_clip (&device->log.glyphs.clip, clip);
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&composite,
+ surface->target,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ NULL);
+ if (unlikely (status)) {
+ surface->log.glyphs.noop++;
+ device->log.glyphs.noop++;
+ return status;
+ }
+
+ midpt (&composite, &x, &y);
+
+ add_extents (&surface->log.glyphs.extents, &composite);
+ add_extents (&device->log.glyphs.extents, &composite);
+ _cairo_composite_rectangles_fini (&composite);
+
+ /* XXX We have to copy the glyphs, because the backend is allowed to
+ * modify! */
+ dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (dev_glyphs == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
+
+ t = _cairo_time_get ();
+ status = _cairo_surface_show_text_glyphs (surface->target, op, source,
+ NULL, 0,
+ dev_glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font,
+ clip);
+ free (dev_glyphs);
+ if (unlikely (status))
+ return status;
+
+ sync (surface->target, x, y);
+ t = _cairo_time_get_delta (t);
+
+ add_record_glyphs (&surface->log,
+ surface->target, op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip, t);
+
+ add_record_glyphs (&device->log,
+ surface->target, op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip, t);
+
+ do_callbacks (surface, &surface->glyphs_callbacks);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_surface_observer_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+
+ do_callbacks (surface, &surface->flush_callbacks);
+ return _cairo_surface_flush (surface->target, flags);
+}
+
+static cairo_status_t
+_cairo_surface_observer_mark_dirty (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (surface->target->backend->mark_dirty_rectangle)
+ status = surface->target->backend->mark_dirty_rectangle (surface->target,
+ x,y, width,height);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_copy_page (void *abstract_surface)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (surface->target->backend->copy_page)
+ status = surface->target->backend->copy_page (surface->target);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_show_page (void *abstract_surface)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (surface->target->backend->show_page)
+ status = surface->target->backend->show_page (surface->target);
+
+ return status;
+}
+
+static cairo_bool_t
+_cairo_surface_observer_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ return _cairo_surface_get_extents (surface->target, extents);
+}
+
+static void
+_cairo_surface_observer_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+
+ if (surface->target->backend->get_font_options != NULL)
+ surface->target->backend->get_font_options (surface->target, options);
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+ return _cairo_surface_get_source (surface->target, extents);
+}
+
+static cairo_status_t
+_cairo_surface_observer_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+
+ surface->log.num_sources_acquired++;
+ to_device (surface)->log.num_sources_acquired++;
+
+ return _cairo_surface_acquire_source_image (surface->target,
+ image_out, image_extra);
+}
+
+static void
+_cairo_surface_observer_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+
+ _cairo_surface_release_source_image (surface->target, image, image_extra);
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_snapshot (void *abstract_surface)
+{
+ cairo_surface_observer_t *surface = abstract_surface;
+
+ /* XXX hook onto the snapshot so that we measure number of reads */
+
+ if (surface->target->backend->snapshot)
+ return surface->target->backend->snapshot (surface->target);
+
+ return NULL;
+}
+
+static cairo_t *
+_cairo_surface_observer_create_context(void *target)
+{
+ cairo_surface_observer_t *surface = target;
+
+ if (_cairo_surface_is_subsurface (&surface->base))
+ surface = (cairo_surface_observer_t *)
+ _cairo_surface_subsurface_get_target (&surface->base);
+
+ surface->log.num_contexts++;
+ to_device (surface)->log.num_contexts++;
+
+ return surface->target->backend->create_context (target);
+}
+
+static const cairo_surface_backend_t _cairo_surface_observer_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
+ _cairo_surface_observer_finish,
+
+ _cairo_surface_observer_create_context,
+
+ _cairo_surface_observer_create_similar,
+ _cairo_surface_observer_create_similar_image,
+ _cairo_surface_observer_map_to_image,
+ _cairo_surface_observer_unmap_image,
+
+ _cairo_surface_observer_source,
+ _cairo_surface_observer_acquire_source_image,
+ _cairo_surface_observer_release_source_image,
+ _cairo_surface_observer_snapshot,
+
+ _cairo_surface_observer_copy_page,
+ _cairo_surface_observer_show_page,
+
+ _cairo_surface_observer_get_extents,
+ _cairo_surface_observer_get_font_options,
+
+ _cairo_surface_observer_flush,
+ _cairo_surface_observer_mark_dirty,
+
+ _cairo_surface_observer_paint,
+ _cairo_surface_observer_mask,
+ _cairo_surface_observer_stroke,
+ _cairo_surface_observer_fill,
+ NULL, /* fill-stroke */
+ _cairo_surface_observer_glyphs,
+};
+
+/**
+ * cairo_surface_create_observer:
+ * @target: an existing surface for which the observer will watch
+ *
+ * Create a new surface that exists solely to watch another is doing. In
+ * the process it will log operations and times, which are fast, which are
+ * slow, which are frequent, etc.
+ *
+ * Return value: a pointer to the newly allocated surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_surface_create_observer (cairo_surface_t *target,
+ cairo_surface_observer_mode_t mode)
+{
+ cairo_device_t *device;
+ cairo_surface_t *surface;
+ cairo_bool_t record;
+
+ if (unlikely (target->status))
+ return _cairo_surface_create_in_error (target->status);
+ if (unlikely (target->finished))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS;
+ device = _cairo_device_create_observer_internal (target->device, record);
+ if (unlikely (device->status)) {
+ cairo_status_t status = device->status;
+ cairo_device_destroy (device);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ surface = _cairo_surface_create_observer_internal (device, target);
+ cairo_device_destroy (device);
+
+ return surface;
+}
+
+static cairo_status_t
+_cairo_surface_observer_add_callback (cairo_list_t *head,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ struct callback_list *cb;
+
+ cb = malloc (sizeof (*cb));
+ if (unlikely (cb == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ cairo_list_add (&cb->link, head);
+ cb->func = func;
+ cb->data = data;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return abstract_surface->status;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *)abstract_surface;
+ return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
+ func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return abstract_surface->status;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *)abstract_surface;
+ return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
+ func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return abstract_surface->status;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *)abstract_surface;
+ return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
+ func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return abstract_surface->status;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *)abstract_surface;
+ return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
+ func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return abstract_surface->status;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *)abstract_surface;
+ return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
+ func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return abstract_surface->status;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *)abstract_surface;
+ return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
+ func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return abstract_surface->status;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *)abstract_surface;
+ return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
+ func, data);
+}
+
+static void
+print_extents (cairo_output_stream_t *stream, const struct extents *e)
+{
+ _cairo_output_stream_printf (stream,
+ " extents: total %g, avg %g [unbounded %d]\n",
+ e->area.sum,
+ e->area.sum / e->area.count,
+ e->unbounded);
+}
+
+static inline int ordercmp (int a, int b, const unsigned int *array)
+{
+ /* high to low */
+ return array[b] - array[a];
+}
+CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
+
+static void
+print_array (cairo_output_stream_t *stream,
+ const unsigned int *array,
+ const char **names,
+ int count)
+{
+ int order[64] = {0,};
+ int i, j;
+
+ assert (count < ARRAY_LENGTH (order));
+ for (i = j = 0; i < count; i++) {
+ if (array[i] != 0)
+ order[j++] = i;
+ }
+
+ sort_order (order, j, (void *)array);
+ for (i = 0; i < j; i++)
+ _cairo_output_stream_printf (stream, " %d %s%s",
+ array[order[i]], names[order[i]],
+ i < j -1 ? "," : "");
+}
+
+static const char *operator_names[] = {
+ "CLEAR", /* CAIRO_OPERATOR_CLEAR */
+
+ "SOURCE", /* CAIRO_OPERATOR_SOURCE */
+ "OVER", /* CAIRO_OPERATOR_OVER */
+ "IN", /* CAIRO_OPERATOR_IN */
+ "OUT", /* CAIRO_OPERATOR_OUT */
+ "ATOP", /* CAIRO_OPERATOR_ATOP */
+
+ "DEST", /* CAIRO_OPERATOR_DEST */
+ "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
+ "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
+ "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
+ "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
+
+ "XOR", /* CAIRO_OPERATOR_XOR */
+ "ADD", /* CAIRO_OPERATOR_ADD */
+ "SATURATE", /* CAIRO_OPERATOR_SATURATE */
+
+ "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
+ "SCREEN", /* CAIRO_OPERATOR_SCREEN */
+ "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
+ "DARKEN", /* CAIRO_OPERATOR_DARKEN */
+ "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
+ "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
+ "BURN", /* CAIRO_OPERATOR_COLOR_BURN */
+ "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
+ "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
+ "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
+ "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
+ "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
+ "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
+ "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
+ "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
+};
+static void
+print_operators (cairo_output_stream_t *stream, unsigned int *array)
+{
+ _cairo_output_stream_printf (stream, " op:");
+ print_array (stream, array, operator_names, NUM_OPERATORS);
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *fill_rule_names[] = {
+ "non-zero",
+ "even-odd",
+};
+static void
+print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
+{
+ _cairo_output_stream_printf (stream, " fill rule:");
+ print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *cap_names[] = {
+ "butt", /* CAIRO_LINE_CAP_BUTT */
+ "round", /* CAIRO_LINE_CAP_ROUND */
+ "square" /* CAIRO_LINE_CAP_SQUARE */
+};
+static void
+print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
+{
+ _cairo_output_stream_printf (stream, " caps:");
+ print_array (stream, array, cap_names, NUM_CAPS);
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *join_names[] = {
+ "miter", /* CAIRO_LINE_JOIN_MITER */
+ "round", /* CAIRO_LINE_JOIN_ROUND */
+ "bevel", /* CAIRO_LINE_JOIN_BEVEL */
+};
+static void
+print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
+{
+ _cairo_output_stream_printf (stream, " joins:");
+ print_array (stream, array, join_names, NUM_JOINS);
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *antialias_names[] = {
+ "default",
+ "none",
+ "gray",
+ "subpixel",
+ "fast",
+ "good",
+ "best"
+};
+static void
+print_antialias (cairo_output_stream_t *stream, unsigned int *array)
+{
+ _cairo_output_stream_printf (stream, " antialias:");
+ print_array (stream, array, antialias_names, NUM_ANTIALIAS);
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *pattern_names[] = {
+ "native",
+ "record",
+ "other surface",
+ "solid",
+ "linear",
+ "radial",
+ "mesh",
+ "raster"
+};
+static void
+print_pattern (cairo_output_stream_t *stream,
+ const char *name,
+ const struct pattern *p)
+{
+ _cairo_output_stream_printf (stream, " %s:", name);
+ print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *path_names[] = {
+ "empty",
+ "pixel-aligned",
+ "rectliinear",
+ "straight",
+ "curved",
+};
+static void
+print_path (cairo_output_stream_t *stream,
+ const struct path *p)
+{
+ _cairo_output_stream_printf (stream, " path:");
+ print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *clip_names[] = {
+ "none",
+ "region",
+ "boxes",
+ "single path",
+ "polygon",
+ "general",
+};
+static void
+print_clip (cairo_output_stream_t *stream, const struct clip *c)
+{
+ _cairo_output_stream_printf (stream, " clip:");
+ print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
+ _cairo_output_stream_printf (stream, "\n");
+}
+
+static void
+print_record (cairo_output_stream_t *stream,
+ cairo_observation_record_t *r)
+{
+ _cairo_output_stream_printf (stream, " op: %s\n", operator_names[r->op]);
+ _cairo_output_stream_printf (stream, " source: %s\n",
+ pattern_names[r->source]);
+ if (r->mask != -1)
+ _cairo_output_stream_printf (stream, " mask: %s\n",
+ pattern_names[r->mask]);
+ if (r->num_glyphs != -1)
+ _cairo_output_stream_printf (stream, " num_glyphs: %d\n",
+ r->num_glyphs);
+ if (r->path != -1)
+ _cairo_output_stream_printf (stream, " path: %s\n",
+ path_names[r->path]);
+ if (r->fill_rule != -1)
+ _cairo_output_stream_printf (stream, " fill rule: %s\n",
+ fill_rule_names[r->fill_rule]);
+ if (r->antialias != -1)
+ _cairo_output_stream_printf (stream, " antialias: %s\n",
+ antialias_names[r->antialias]);
+ _cairo_output_stream_printf (stream, " clip: %s\n", clip_names[r->clip]);
+ _cairo_output_stream_printf (stream, " elapsed: %f ns\n",
+ _cairo_time_to_ns (r->elapsed));
+}
+
+static double percent (cairo_time_t a, cairo_time_t b)
+{
+ /* Fake %.1f */
+ return _cairo_round (_cairo_time_to_s (a) * 1000 /
+ _cairo_time_to_s (b)) / 10;
+}
+
+static cairo_bool_t
+replay_record (cairo_observation_t *log,
+ cairo_observation_record_t *r,
+ cairo_device_t *script)
+{
+#if CAIRO_HAS_SCRIPT_SURFACE
+ cairo_surface_t *surface;
+ cairo_int_status_t status;
+
+ if (log->record == NULL || script == NULL)
+ return FALSE;
+
+ surface = cairo_script_surface_create (script,
+ r->target_content,
+ r->target_width,
+ r->target_height);
+ status =
+ _cairo_recording_surface_replay_one (log->record, r->index, surface);
+ cairo_surface_destroy (surface);
+
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static cairo_time_t
+_cairo_observation_total_elapsed (cairo_observation_t *log)
+{
+ cairo_time_t total;
+
+ total = log->paint.elapsed;
+ total = _cairo_time_add (total, log->mask.elapsed);
+ total = _cairo_time_add (total, log->fill.elapsed);
+ total = _cairo_time_add (total, log->stroke.elapsed);
+ total = _cairo_time_add (total, log->glyphs.elapsed);
+
+ return total;
+}
+
+static void
+_cairo_observation_print (cairo_output_stream_t *stream,
+ cairo_observation_t *log)
+{
+ cairo_device_t *script;
+ cairo_time_t total;
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+ script = _cairo_script_context_create_internal (stream);
+ _cairo_script_context_attach_snapshots (script, FALSE);
+#else
+ script = NULL;
+#endif
+
+ total = _cairo_observation_total_elapsed (log);
+
+ _cairo_output_stream_printf (stream, "elapsed: %f\n",
+ _cairo_time_to_ns (total));
+ _cairo_output_stream_printf (stream, "surfaces: %d\n",
+ log->num_surfaces);
+ _cairo_output_stream_printf (stream, "contexts: %d\n",
+ log->num_contexts);
+ _cairo_output_stream_printf (stream, "sources acquired: %d\n",
+ log->num_sources_acquired);
+
+
+ _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
+ log->paint.count, log->paint.noop,
+ _cairo_time_to_ns (log->paint.elapsed),
+ percent (log->paint.elapsed, total));
+ if (log->paint.count) {
+ print_extents (stream, &log->paint.extents);
+ print_operators (stream, log->paint.operators);
+ print_pattern (stream, "source", &log->paint.source);
+ print_clip (stream, &log->paint.clip);
+
+ _cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
+ percent (log->paint.slowest.elapsed,
+ log->paint.elapsed));
+ print_record (stream, &log->paint.slowest);
+
+ _cairo_output_stream_printf (stream, "\n");
+ if (replay_record (log, &log->paint.slowest, script))
+ _cairo_output_stream_printf (stream, "\n\n");
+ }
+
+ _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
+ log->mask.count, log->mask.noop,
+ _cairo_time_to_ns (log->mask.elapsed),
+ percent (log->mask.elapsed, total));
+ if (log->mask.count) {
+ print_extents (stream, &log->mask.extents);
+ print_operators (stream, log->mask.operators);
+ print_pattern (stream, "source", &log->mask.source);
+ print_pattern (stream, "mask", &log->mask.mask);
+ print_clip (stream, &log->mask.clip);
+
+ _cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
+ percent (log->mask.slowest.elapsed,
+ log->mask.elapsed));
+ print_record (stream, &log->mask.slowest);
+
+ _cairo_output_stream_printf (stream, "\n");
+ if (replay_record (log, &log->mask.slowest, script))
+ _cairo_output_stream_printf (stream, "\n\n");
+ }
+
+ _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
+ log->fill.count, log->fill.noop,
+ _cairo_time_to_ns (log->fill.elapsed),
+ percent (log->fill.elapsed, total));
+ if (log->fill.count) {
+ print_extents (stream, &log->fill.extents);
+ print_operators (stream, log->fill.operators);
+ print_pattern (stream, "source", &log->fill.source);
+ print_path (stream, &log->fill.path);
+ print_fill_rule (stream, log->fill.fill_rule);
+ print_antialias (stream, log->fill.antialias);
+ print_clip (stream, &log->fill.clip);
+
+ _cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
+ percent (log->fill.slowest.elapsed,
+ log->fill.elapsed));
+ print_record (stream, &log->fill.slowest);
+
+ _cairo_output_stream_printf (stream, "\n");
+ if (replay_record (log, &log->fill.slowest, script))
+ _cairo_output_stream_printf (stream, "\n\n");
+ }
+
+ _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
+ log->stroke.count, log->stroke.noop,
+ _cairo_time_to_ns (log->stroke.elapsed),
+ percent (log->stroke.elapsed, total));
+ if (log->stroke.count) {
+ print_extents (stream, &log->stroke.extents);
+ print_operators (stream, log->stroke.operators);
+ print_pattern (stream, "source", &log->stroke.source);
+ print_path (stream, &log->stroke.path);
+ print_antialias (stream, log->stroke.antialias);
+ print_line_caps (stream, log->stroke.caps);
+ print_line_joins (stream, log->stroke.joins);
+ print_clip (stream, &log->stroke.clip);
+
+ _cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
+ percent (log->stroke.slowest.elapsed,
+ log->stroke.elapsed));
+ print_record (stream, &log->stroke.slowest);
+
+ _cairo_output_stream_printf (stream, "\n");
+ if (replay_record (log, &log->stroke.slowest, script))
+ _cairo_output_stream_printf (stream, "\n\n");
+ }
+
+ _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
+ log->glyphs.count, log->glyphs.noop,
+ _cairo_time_to_ns (log->glyphs.elapsed),
+ percent (log->glyphs.elapsed, total));
+ if (log->glyphs.count) {
+ print_extents (stream, &log->glyphs.extents);
+ print_operators (stream, log->glyphs.operators);
+ print_pattern (stream, "source", &log->glyphs.source);
+ print_clip (stream, &log->glyphs.clip);
+
+ _cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
+ percent (log->glyphs.slowest.elapsed,
+ log->glyphs.elapsed));
+ print_record (stream, &log->glyphs.slowest);
+
+ _cairo_output_stream_printf (stream, "\n");
+ if (replay_record (log, &log->glyphs.slowest, script))
+ _cairo_output_stream_printf (stream, "\n\n");
+ }
+
+ cairo_device_destroy (script);
+}
+
+cairo_status_t
+cairo_surface_observer_print (cairo_surface_t *abstract_surface,
+ cairo_write_func_t write_func,
+ void *closure)
+{
+ cairo_output_stream_t *stream;
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (abstract_surface->status))
+ return abstract_surface->status;
+
+ if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ surface = (cairo_surface_observer_t *) abstract_surface;
+
+ stream = _cairo_output_stream_create (write_func, NULL, closure);
+ _cairo_observation_print (stream, &surface->log);
+ return _cairo_output_stream_destroy (stream);
+}
+
+double
+cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
+{
+ cairo_surface_observer_t *surface;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+ return -1;
+
+ if (! _cairo_surface_is_observer (abstract_surface))
+ return -1;
+
+ surface = (cairo_surface_observer_t *) abstract_surface;
+ return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
+}
+
+cairo_status_t
+cairo_device_observer_print (cairo_device_t *abstract_device,
+ cairo_write_func_t write_func,
+ void *closure)
+{
+ cairo_output_stream_t *stream;
+ cairo_device_observer_t *device;
+
+ if (unlikely (abstract_device->status))
+ return abstract_device->status;
+
+ if (unlikely (! _cairo_device_is_observer (abstract_device)))
+ return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ device = (cairo_device_observer_t *) abstract_device;
+
+ stream = _cairo_output_stream_create (write_func, NULL, closure);
+ _cairo_observation_print (stream, &device->log);
+ return _cairo_output_stream_destroy (stream);
+}
+
+double
+cairo_device_observer_elapsed (cairo_device_t *abstract_device)
+{
+ cairo_device_observer_t *device;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+ return -1;
+
+ if (! _cairo_device_is_observer (abstract_device))
+ return -1;
+
+ device = (cairo_device_observer_t *) abstract_device;
+ return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
+}
+
+double
+cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
+{
+ cairo_device_observer_t *device;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+ return -1;
+
+ if (! _cairo_device_is_observer (abstract_device))
+ return -1;
+
+ device = (cairo_device_observer_t *) abstract_device;
+ return _cairo_time_to_ns (device->log.paint.elapsed);
+}
+
+double
+cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
+{
+ cairo_device_observer_t *device;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+ return -1;
+
+ if (! _cairo_device_is_observer (abstract_device))
+ return -1;
+
+ device = (cairo_device_observer_t *) abstract_device;
+ return _cairo_time_to_ns (device->log.mask.elapsed);
+}
+
+double
+cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
+{
+ cairo_device_observer_t *device;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+ return -1;
+
+ if (! _cairo_device_is_observer (abstract_device))
+ return -1;
+
+ device = (cairo_device_observer_t *) abstract_device;
+ return _cairo_time_to_ns (device->log.fill.elapsed);
+}
+
+double
+cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
+{
+ cairo_device_observer_t *device;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+ return -1;
+
+ if (! _cairo_device_is_observer (abstract_device))
+ return -1;
+
+ device = (cairo_device_observer_t *) abstract_device;
+ return _cairo_time_to_ns (device->log.stroke.elapsed);
+}
+
+double
+cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
+{
+ cairo_device_observer_t *device;
+
+ if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+ return -1;
+
+ if (! _cairo_device_is_observer (abstract_device))
+ return -1;
+
+ device = (cairo_device_observer_t *) abstract_device;
+ return _cairo_time_to_ns (device->log.glyphs.elapsed);
+}
diff --git a/src/cairo-surface-offset-private.h b/src/cairo-surface-offset-private.h
new file mode 100755
index 000000000..310ba5691
--- /dev/null
+++ b/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,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_mask (cairo_surface_t *target,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_stroke (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_fill (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_glyphs (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip);
+
+#endif /* CAIRO_SURFACE_OFFSET_PRIVATE_H */
diff --git a/src/cairo-surface-offset.c b/src/cairo-surface-offset.c
new file mode 100755
index 000000000..98f57f298
--- /dev/null
+++ b/src/cairo-surface-offset.c
@@ -0,0 +1,308 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-offset-private.h"
+
+/* A collection of routines to facilitate drawing to an alternate surface. */
+
+static void
+_copy_transformed_pattern (cairo_pattern_t *pattern,
+ const cairo_pattern_t *original,
+ const cairo_matrix_t *ctm_inverse)
+{
+ _cairo_pattern_init_static_copy (pattern, original);
+
+ if (! _cairo_matrix_is_identity (ctm_inverse))
+ _cairo_pattern_transform (pattern, ctm_inverse);
+}
+
+cairo_status_t
+_cairo_surface_offset_paint (cairo_surface_t *target,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+ cairo_pattern_union_t source_copy;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ }
+
+ status = _cairo_surface_paint (target, op, source, dev_clip);
+
+ if (dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_mask (cairo_surface_t *target,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+ cairo_pattern_union_t source_copy;
+ cairo_pattern_union_t mask_copy;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ _copy_transformed_pattern (&mask_copy.base, mask, &m);
+ source = &source_copy.base;
+ mask = &mask_copy.base;
+ }
+
+ status = _cairo_surface_mask (target, op,
+ source, mask,
+ dev_clip);
+
+ if (dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_stroke (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+ cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+ cairo_matrix_t dev_ctm = *ctm;
+ cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
+ cairo_pattern_union_t source_copy;
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+ status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+ if (unlikely (status))
+ goto FINISH;
+
+ _cairo_path_fixed_translate (&path_copy,
+ _cairo_fixed_from_int (-x),
+ _cairo_fixed_from_int (-y));
+ dev_path = &path_copy;
+
+ cairo_matrix_init_translate (&m, -x, -y);
+ cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
+ }
+
+ status = _cairo_surface_stroke (surface, op, source,
+ dev_path, stroke_style,
+ &dev_ctm, &dev_ctm_inverse,
+ tolerance, antialias,
+ dev_clip);
+
+FINISH:
+ if (dev_path != path)
+ _cairo_path_fixed_fini (dev_path);
+ if (dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_fill (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+ cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+ cairo_pattern_union_t source_copy;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+ status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+ if (unlikely (status))
+ goto FINISH;
+
+ _cairo_path_fixed_translate (&path_copy,
+ _cairo_fixed_from_int (-x),
+ _cairo_fixed_from_int (-y));
+ dev_path = &path_copy;
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ }
+
+ status = _cairo_surface_fill (surface, op, source,
+ dev_path, fill_rule,
+ tolerance, antialias,
+ dev_clip);
+
+FINISH:
+ if (dev_path != path)
+ _cairo_path_fixed_fini (dev_path);
+ if (dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_glyphs (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+ cairo_pattern_union_t source_copy;
+ cairo_glyph_t *dev_glyphs;
+ int i;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (dev_glyphs == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+
+ for (i = 0; i < num_glyphs; i++) {
+ dev_glyphs[i].x -= x;
+ dev_glyphs[i].y -= y;
+ }
+ }
+
+ status = _cairo_surface_show_text_glyphs (surface, op, source,
+ NULL, 0,
+ dev_glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font,
+ dev_clip);
+
+ if (dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+ free (dev_glyphs);
+
+ return status;
+}
diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h
new file mode 100755
index 000000000..f20ab0739
--- /dev/null
+++ b/src/cairo-surface-private.h
@@ -0,0 +1,121 @@
+/* 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"
+#include "cairo-surface-backend-private.h"
+
+typedef void (*cairo_surface_func_t) (cairo_surface_t *);
+
+struct _cairo_surface {
+ const cairo_surface_backend_t *backend;
+ cairo_device_t *device;
+
+ /* We allow surfaces to override the backend->type by shoving something
+ * else into surface->type. This is for "wrapper" surfaces that want to
+ * hide their internal type from the user-level API. */
+ cairo_surface_type_t type;
+
+ cairo_content_t content;
+
+ cairo_reference_count_t ref_count;
+ cairo_status_t status;
+ unsigned int unique_id;
+ unsigned int serial;
+ cairo_damage_t *damage;
+
+ unsigned _finishing : 1;
+ unsigned finished : 1;
+ unsigned is_clear : 1;
+ unsigned has_font_options : 1;
+ unsigned owns_device : 1;
+
+ cairo_user_data_array_t user_data;
+ cairo_user_data_array_t mime_data;
+
+ cairo_matrix_t device_transform;
+ cairo_matrix_t device_transform_inverse;
+ cairo_list_t device_transform_observers;
+
+ /* The actual resolution of the device, in dots per inch. */
+ double x_resolution;
+ double y_resolution;
+
+ /* The resolution that should be used when generating image-based
+ * fallback; generally only used by the analysis/paginated
+ * surfaces
+ */
+ double x_fallback_resolution;
+ double y_fallback_resolution;
+
+ /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */
+ cairo_surface_t *snapshot_of;
+ cairo_surface_func_t snapshot_detach;
+ /* current snapshots of this surface*/
+ cairo_list_t snapshots;
+ /* place upon snapshot list */
+ cairo_list_t snapshot;
+
+ /*
+ * Surface font options, falling back to backend's default options,
+ * and set using _cairo_surface_set_font_options(), and propagated by
+ * cairo_surface_create_similar().
+ */
+ cairo_font_options_t font_options;
+};
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_in_error (cairo_status_t status);
+
+cairo_private cairo_surface_t *
+_cairo_int_surface_create_in_error (cairo_int_status_t status);
+
+cairo_private cairo_surface_t *
+_cairo_surface_get_source (cairo_surface_t *surface,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_flush (cairo_surface_t *surface, unsigned flags);
+
+#endif /* CAIRO_SURFACE_PRIVATE_H */
diff --git a/src/cairo-surface-scale-translate-private.h b/src/cairo-surface-scale-translate-private.h
new file mode 100755
index 000000000..b13d3366d
--- /dev/null
+++ b/src/cairo-surface-scale-translate-private.h
@@ -0,0 +1,163 @@
+/* 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
+ * Copyright © 2013 Henry Song
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Henry Song <henry.song@samsung.h>
+ */
+
+#ifndef CAIRO_SURFACE_SCALE_TRANSLATE_PRIVATE_H
+#define CAIRO_SURFACE_SCALE_TRANSLATE_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_status_t
+_cairo_surface_scale_translate_paint (cairo_surface_t *target,
+ const cairo_bool_t clear_bg,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_paint_get_offset_extents (cairo_surface_t *target,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_rectangle_t *extents,
+ cairo_bool_t *bounded);
+
+cairo_private cairo_status_t
+_cairo_surface_scale_translate_mask (cairo_surface_t *target,
+ const cairo_bool_t clear_bg,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_mask_get_offset_extents (cairo_surface_t *target,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_pattern_t *mask_out,
+ cairo_rectangle_t *extents,
+ cairo_bool_t *bounded);
+
+cairo_private cairo_status_t
+_cairo_surface_scale_translate_stroke (cairo_surface_t *surface,
+ const cairo_color_t *bg_color,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ 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,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_stroke_get_offset_extents (cairo_surface_t *target,
+ cairo_bool_t is_inset,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_path_fixed_t *path_out,
+ cairo_matrix_t *ctm_out,
+ cairo_matrix_t *ctm_inverse_out,
+ cairo_rectangle_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_scale_translate_fill (cairo_surface_t *surface,
+ const cairo_color_t *bg_color,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fill_get_offset_extents (cairo_surface_t *target,
+ cairo_bool_t is_inset,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_fill_rule_t fill_rule,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_path_fixed_t *path_out,
+ cairo_rectangle_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_translate_glyphs (cairo_surface_t *surface,
+ const cairo_color_t *bg_color,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_glyphs_get_offset_extents (cairo_surface_t *target,
+ cairo_bool_t is_inset,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_glyph_t *glyphs_out,
+ cairo_rectangle_t *extents);
+
+#endif /* CAIRO_SURFACE_SCALE_TRANSLATE_PRIVATE_H */
diff --git a/src/cairo-surface-scale-translate.c b/src/cairo-surface-scale-translate.c
new file mode 100755
index 000000000..1729b81ad
--- /dev/null
+++ b/src/cairo-surface-scale-translate.c
@@ -0,0 +1,689 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2013 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):
+ * Henry Song <henry.song@samsung.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-scale-translate-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);
+}
+
+static void
+_transformed_pattern (cairo_pattern_t *pattern,
+ const cairo_matrix_t *ctm_inverse)
+{
+ if (! _cairo_matrix_is_identity (ctm_inverse))
+ _cairo_pattern_transform (pattern, ctm_inverse);
+}
+
+cairo_status_t
+_cairo_surface_scale_translate_paint (cairo_surface_t *target,
+ const cairo_bool_t clear_bg,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip = NULL;
+ cairo_matrix_t m;
+ cairo_pattern_t *clear_pattern;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! _cairo_matrix_is_identity (matrix)) {
+ if (clip) {
+ dev_clip = _cairo_clip_copy (clip);
+ dev_clip = _cairo_clip_transform (dev_clip, matrix);
+ }
+
+ m = *matrix;
+ status = cairo_matrix_invert (&m);
+ _transformed_pattern (source, &m);
+ }
+
+ if (clear_bg) {
+ clear_pattern = cairo_pattern_create_rgba (0, 0, 0, 0);
+ status = _cairo_surface_paint (target, CAIRO_OPERATOR_SOURCE,
+ clear_pattern, dev_clip);
+ cairo_pattern_destroy (clear_pattern);
+ }
+
+ status = _cairo_surface_paint (target, op, source, dev_clip);
+
+ if (dev_clip && dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ return status;
+}
+
+cairo_private cairo_status_t
+_cairo_surface_paint_get_offset_extents (cairo_surface_t *target,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_rectangle_t *extents,
+ cairo_bool_t *bounded)
+{
+ cairo_matrix_t m;
+ cairo_rectangle_t rect, temp;
+ cairo_rectangle_int_t int_rect;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip)) {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ cairo_matrix_init_translate (&m, -x_offset, -y_offset);
+ _copy_transformed_pattern (source_out, source, &m);
+
+ _cairo_surface_get_extents (target, &int_rect);
+ rect.x = int_rect.x;
+ rect.y = int_rect.y;
+ rect.width = int_rect.width;
+ rect.height = int_rect.height;
+
+ _cairo_pattern_get_exact_extents (source_out, &temp);
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+
+ *bounded = TRUE;
+
+ if (rect.width == _cairo_unbounded_rectangle.width ||
+ rect.height == _cairo_unbounded_rectangle.height) {
+ const cairo_rectangle_int_t *clip_extent = _cairo_clip_get_extents (clip);
+ *bounded = FALSE;
+ temp.x = clip_extent->x;
+ temp.y = clip_extent->y;
+ temp.width = clip_extent->width;
+ temp.height = clip_extent->height;
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+ if (rect.width == _cairo_unbounded_rectangle.width ||
+ rect.height == _cairo_unbounded_rectangle.height)
+ rect.width = rect.height = 0;
+ }
+
+ *extents = rect;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_scale_translate_mask (cairo_surface_t *target,
+ const cairo_bool_t clear_bg,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip = NULL;
+ cairo_matrix_t m;
+ cairo_pattern_t *clear_pattern;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! _cairo_matrix_is_identity (matrix)) {
+ if (clip) {
+ dev_clip = _cairo_clip_copy (clip);
+ dev_clip = _cairo_clip_transform (dev_clip, matrix);
+ }
+
+ m = *matrix;
+ status = cairo_matrix_invert (&m);
+ _transformed_pattern (source, &m);
+ _transformed_pattern (mask, &m);
+ }
+
+ if (clear_bg) {
+ clear_pattern = cairo_pattern_create_rgba (0, 0, 0, 0);
+ status = _cairo_surface_paint (target, CAIRO_OPERATOR_SOURCE,
+ clear_pattern, dev_clip);
+ cairo_pattern_destroy (clear_pattern);
+ }
+
+ status = _cairo_surface_mask (target, op,
+ source, mask,
+ dev_clip);
+
+ if (dev_clip && dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ return status;
+}
+
+cairo_private cairo_status_t
+_cairo_surface_mask_get_offset_extents (cairo_surface_t *target,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_pattern_t *mask_out,
+ cairo_rectangle_t *extents,
+ cairo_bool_t *bounded)
+{
+ cairo_matrix_t m;
+ cairo_rectangle_t rect, temp;
+ cairo_rectangle_int_t int_rect;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip)) {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ cairo_matrix_init_translate (&m, -x_offset, -y_offset);
+ _copy_transformed_pattern (source_out, source, &m);
+ _copy_transformed_pattern (mask_out, mask, &m);
+
+ _cairo_surface_get_extents (target, &int_rect);
+ rect.x = int_rect.x;
+ rect.y = int_rect.y;
+ rect.width = int_rect.width;
+ rect.height = int_rect.height;
+
+ _cairo_pattern_get_exact_extents (source_out, &temp);
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+
+ _cairo_pattern_get_exact_extents (mask_out, &temp);
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+
+ *bounded = TRUE;
+
+ if (rect.width == _cairo_unbounded_rectangle.width ||
+ rect.height == _cairo_unbounded_rectangle.height) {
+ const cairo_rectangle_int_t *clip_extent = _cairo_clip_get_extents (clip);
+ *bounded = FALSE;
+ temp.x = clip_extent->x;
+ temp.y = clip_extent->y;
+ temp.width = clip_extent->width;
+ temp.height = clip_extent->height;
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+
+ if (rect.width == _cairo_unbounded_rectangle.width ||
+ rect.height == _cairo_unbounded_rectangle.height)
+ rect.width = rect.height = 0;
+ }
+
+ *extents = rect;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_scale_translate_stroke (cairo_surface_t *surface,
+ const cairo_color_t *bg_color,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ 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,
+ const cairo_clip_t *clip)
+{
+ cairo_path_fixed_t *dev_path = (cairo_path_fixed_t *) path;
+ cairo_clip_t *dev_clip = NULL;
+ cairo_matrix_t dev_ctm = *ctm;
+ cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
+ cairo_status_t status;
+ cairo_matrix_t m;
+ cairo_pattern_t *clear_pattern;
+ cairo_stroke_style_t style_copy;
+ double dash[2];
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ memcpy (&style_copy, stroke_style, sizeof (cairo_stroke_style_t));
+
+ if (! _cairo_matrix_is_identity (matrix)) {
+ if (clip) {
+ dev_clip = _cairo_clip_copy (clip);
+ dev_clip = _cairo_clip_transform (dev_clip, matrix);
+ }
+
+ _cairo_path_fixed_transform (dev_path, matrix);
+
+ cairo_matrix_multiply (&dev_ctm, &dev_ctm, matrix);
+
+ m = *matrix;
+ status = cairo_matrix_invert (&m);
+
+ _transformed_pattern (source, &m);
+ cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
+
+ if (_cairo_stroke_style_dash_can_approximate (&style_copy, matrix, tolerance)) {
+ style_copy.dash = dash;
+ _cairo_stroke_style_dash_approximate (stroke_style, matrix,
+ tolerance,
+ &style_copy.dash_offset,
+ style_copy.dash,
+ &style_copy.num_dashes);
+ }
+ }
+
+ if (bg_color) {
+ clear_pattern = _cairo_pattern_create_solid (bg_color);
+ status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE,
+ clear_pattern, dev_clip);
+ cairo_pattern_destroy (clear_pattern);
+ }
+
+ status = _cairo_surface_stroke (surface, op, source,
+ dev_path, &style_copy,
+ &dev_ctm, &dev_ctm_inverse,
+ tolerance, antialias,
+ dev_clip);
+
+ if (dev_clip && dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+ return status;
+}
+
+cairo_private cairo_status_t
+_cairo_surface_stroke_get_offset_extents (cairo_surface_t *target,
+ cairo_bool_t is_inset,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_path_fixed_t *path_out,
+ cairo_matrix_t *ctm_out,
+ cairo_matrix_t *ctm_inverse_out,
+ cairo_rectangle_t *extents)
+{
+ cairo_status_t status;
+ cairo_matrix_t m;
+ cairo_rectangle_t rect, temp;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip)) {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ *ctm_out = *ctm;
+ *ctm_inverse_out = *ctm_inverse;
+
+ cairo_matrix_init_translate (&m, -x_offset, -y_offset);
+ _copy_transformed_pattern (source_out, source, &m);
+
+ status = _cairo_path_fixed_init_copy (path_out, path);
+ if (unlikely (status))
+ return status;
+
+ if (x_offset != 0.0 || y_offset != 0.0) {
+ cairo_matrix_multiply (ctm_inverse_out, ctm_inverse_out, &m);
+
+ _cairo_path_fixed_translate (path_out,
+ _cairo_fixed_from_double (x_offset),
+ _cairo_fixed_from_double (y_offset));
+
+ cairo_matrix_init_translate (&m, x_offset, y_offset);
+ cairo_matrix_multiply (ctm_out, ctm_out, &m);
+ }
+
+ _cairo_pattern_get_exact_extents (source_out, &rect);
+
+ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
+ _cairo_path_fixed_approximate_stroke_exact_extents (path_out,
+ stroke_style,
+ ctm_out, &temp);
+ else {
+ status = _cairo_path_fixed_stroke_exact_extents (path_out,
+ stroke_style,
+ ctm_out,
+ ctm_inverse_out,
+ tolerance, &temp);
+ if (unlikely (status)) {
+ extents->width = extents->height = 0;
+ return status;
+ }
+ }
+
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+
+ if (is_inset) {
+ rect.x -= x_offset;
+ rect.y -= y_offset;
+ rect.width += fabs (x_offset);
+ rect.height += fabs (y_offset);
+ }
+ *extents = rect;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_scale_translate_fill (cairo_surface_t *surface,
+ const cairo_color_t *bg_color,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t *dev_path = (cairo_path_fixed_t *) path;
+ cairo_clip_t *dev_clip = NULL;
+ cairo_matrix_t m;
+ cairo_pattern_t *clear_pattern;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! _cairo_matrix_is_identity (matrix)) {
+ if (clip) {
+ dev_clip = _cairo_clip_copy (clip);
+ dev_clip = _cairo_clip_transform (dev_clip, matrix);
+ }
+
+ _cairo_path_fixed_transform (dev_path, matrix);
+
+ m = *matrix;
+ status = cairo_matrix_invert (&m);
+ _transformed_pattern (source, &m);
+ }
+
+ if (bg_color) {
+ clear_pattern = _cairo_pattern_create_solid (bg_color);
+ status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE,
+ clear_pattern, dev_clip);
+ cairo_pattern_destroy (clear_pattern);
+ }
+
+ status = _cairo_surface_fill (surface, op, source,
+ dev_path, fill_rule,
+ tolerance, antialias,
+ dev_clip);
+
+ if (dev_clip && dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ return status;
+}
+
+cairo_private cairo_status_t
+_cairo_surface_fill_get_offset_extents (cairo_surface_t *target,
+ cairo_bool_t is_inset,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_fill_rule_t fill_rule,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_path_fixed_t *path_out,
+ cairo_rectangle_t *extents)
+{
+ cairo_status_t status;
+ cairo_matrix_t m;
+ cairo_rectangle_t rect, temp;
+ const cairo_rectangle_int_t *clip_rect;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip)) {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ cairo_matrix_init_translate (&m, -x_offset, -y_offset);
+ _copy_transformed_pattern (source_out, source, &m);
+
+ status = _cairo_path_fixed_init_copy (path_out, path);
+ if (unlikely (status))
+ return status;
+
+ if (x_offset != 0.0 || y_offset != 0.0) {
+ _cairo_path_fixed_translate (path_out,
+ _cairo_fixed_from_double (x_offset),
+ _cairo_fixed_from_double (y_offset));
+ }
+
+ _cairo_pattern_get_exact_extents (source_out, &rect);
+
+ if (! source->shadow.path_is_fill_with_spread) {
+ _cairo_path_fixed_approximate_fill_exact_extents (path_out, &temp);
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+ }
+ else {
+ clip_rect = _cairo_clip_get_extents (clip);
+ temp.x = clip_rect->x;
+ temp.y = clip_rect->y;
+ temp.width = clip_rect->width;
+ temp.height = clip_rect->height;
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+ }
+
+ if (is_inset) {
+ rect.x -= x_offset;
+ rect.y -= y_offset;
+ rect.width += abs (x_offset);
+ rect.height += abs (y_offset);
+ }
+ *extents = rect;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_translate_glyphs (cairo_surface_t *surface,
+ const cairo_color_t *bg_color,
+ const cairo_matrix_t *matrix,
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+ cairo_glyph_t *dev_glyphs = glyphs;
+ cairo_pattern_t *clear_pattern;
+ int i;
+ cairo_matrix_t inverse_matrix;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ inverse_matrix = *matrix;
+ status = cairo_matrix_invert (&inverse_matrix);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_matrix_is_identity (matrix)) {
+ dev_clip = _cairo_clip_copy_with_translation (clip, matrix->x0,
+ matrix->y0);
+
+ _transformed_pattern (source, matrix);
+
+ for (i = 0; i < num_glyphs; i++) {
+ dev_glyphs[i].x += matrix->x0;
+ dev_glyphs[i].y += matrix->y0;
+ }
+ }
+
+ if (bg_color) {
+ clear_pattern = _cairo_pattern_create_solid (bg_color);
+ status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE,
+ clear_pattern, NULL);
+ cairo_pattern_destroy (clear_pattern);
+ }
+
+ status = _cairo_surface_show_text_glyphs (surface, op, source,
+ NULL, 0,
+ dev_glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font,
+ dev_clip);
+
+ if (dev_clip != clip)
+ _cairo_clip_destroy (dev_clip);
+
+ _transformed_pattern (source, &inverse_matrix);
+ return status;
+}
+
+cairo_private cairo_status_t
+_cairo_surface_glyphs_get_offset_extents (cairo_surface_t *target,
+ cairo_bool_t is_inset,
+ double x_offset, double y_offset,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ cairo_pattern_t *source_out,
+ cairo_glyph_t *glyphs_out,
+ cairo_rectangle_t *extents)
+{
+ cairo_matrix_t m;
+ cairo_rectangle_t rect, temp;
+ cairo_rectangle_int_t int_rect;
+ const cairo_rectangle_int_t *clip_rect;
+ int i;
+ cairo_bool_t result;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (_cairo_clip_is_all_clipped (clip)) {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ memcpy (glyphs_out, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+
+ cairo_matrix_init_translate (&m, -x_offset, -y_offset);
+ _copy_transformed_pattern (source_out, source, &m);
+
+ if (x_offset != 0.0) {
+ for (i = 0; i < num_glyphs; i++)
+ glyphs_out[i].x += x_offset;
+ }
+
+ if (y_offset != 0.0) {
+ for (i = 0; i < num_glyphs; i++)
+ glyphs_out[i].y += y_offset;
+ }
+
+ _cairo_surface_get_extents (target, &int_rect);
+ clip_rect = _cairo_clip_get_extents (clip);
+ _cairo_rectangle_intersect (&int_rect, clip_rect);
+
+ rect.x = int_rect.x;
+ rect.y = int_rect.y;
+ rect.width = int_rect.width;
+ rect.height = int_rect.height;
+
+ _cairo_pattern_get_exact_extents (source_out, &temp);
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+
+ result = _cairo_scaled_font_glyph_approximate_extents (scaled_font,
+ glyphs_out,
+ num_glyphs,
+ &int_rect);
+ if (! result)
+ return CAIRO_STATUS_USER_FONT_ERROR;
+
+ temp.x = int_rect.x;
+ temp.y = int_rect.y;
+ temp.width = int_rect.width;
+ temp.height = int_rect.height;
+ _cairo_rectangle_exact_intersect (&rect, &temp);
+ *extents = rect;
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-surface-shadow-private.h b/src/cairo-surface-shadow-private.h
new file mode 100755
index 000000000..43c7e6d2a
--- /dev/null
+++ b/src/cairo-surface-shadow-private.h
@@ -0,0 +1,98 @@
+/* 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
+ * Copyright © 2013 Samsung Research America, Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Henry Song <henry.song@samsung.com>
+ */
+
+#ifndef CAIRO_SURFACE_SHADOW_PRIVATE_H
+#define CAIRO_SURFACE_SHADOW_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#define MAX_SHADOW_CACHE_SIZE 1048576
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_status_t
+_cairo_surface_shadow_paint (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow);
+
+cairo_private cairo_status_t
+_cairo_surface_shadow_mask (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow);
+
+cairo_private cairo_status_t
+_cairo_surface_shadow_stroke (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow);
+
+cairo_private cairo_status_t
+_cairo_surface_shadow_fill (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow);
+
+cairo_private cairo_status_t
+_cairo_surface_shadow_glyphs (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow);
+
+#endif /* CAIRO_SURFACE_SHADOW_PRIVATE_H */
diff --git a/src/cairo-surface-shadow.c b/src/cairo-surface-shadow.c
new file mode 100755
index 000000000..5224bee40
--- /dev/null
+++ b/src/cairo-surface-shadow.c
@@ -0,0 +1,2286 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2013 Samsung Research America, Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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):
+ * Henry Song <henry.song@samsung.com
+ */
+
+#include "cairoint.h"
+#include "cairo-surface-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-shadow-private.h"
+#include "cairo-surface-scale-translate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-device-private.h"
+#include "cairo-image-surface-private.h"
+
+#define MAX_SHADOW_SIZE 1024
+
+typedef struct _cairo_shadow_cache_list {
+ cairo_list_t *caches;
+ unsigned long *size;
+ cairo_bool_t locked;
+} cairo_shadow_cache_list_t;
+
+static unsigned long
+_cairo_stroke_style_hash (unsigned long hash,
+ const cairo_stroke_style_t *style)
+{
+ hash = _cairo_hash_bytes (hash, style, sizeof (cairo_stroke_style_t));
+ if (style->num_dashes)
+ hash = _cairo_hash_bytes (hash, style->dash, sizeof (double) * style->num_dashes);
+ return hash;
+}
+
+static unsigned long
+_cairo_matrix_hash (unsigned long hash, const cairo_matrix_t *matrix)
+{
+ return _cairo_hash_bytes (hash, matrix, sizeof (cairo_matrix_t));
+}
+
+static unsigned long
+_cairo_path_fixed_rel_hash (unsigned long hash, const cairo_path_fixed_t *path)
+{
+ const cairo_path_buf_t *buf;
+ unsigned int count;
+ cairo_path_fixed_t path_copy;
+ cairo_status_t status;
+ unsigned int i;
+ cairo_fixed_t dx, dy;
+
+ status = _cairo_path_fixed_init_copy (&path_copy, path);
+ if (unlikely (status))
+ return hash;
+
+ dx = path_copy.buf.points[0].x;
+ dy = path_copy.buf.points[0].y;
+
+ cairo_path_foreach_buf_start (buf, &path_copy) {
+ for (i = 0; i < buf->num_points; i++) {
+ buf->points[i].x -= dx;
+ buf->points[i].y -= dy;
+ }
+ } cairo_path_foreach_buf_end (buf, &path_copy);
+
+ count = 0;
+ cairo_path_foreach_buf_start (buf, &path_copy) {
+ hash = _cairo_hash_bytes (hash, buf->op,
+ buf->num_ops * sizeof (buf->op[0]));
+ count += buf->num_ops;
+ } cairo_path_foreach_buf_end (buf, &path_copy);
+ hash = _cairo_hash_bytes (hash, &count, sizeof (count));
+
+ count = 0;
+ cairo_path_foreach_buf_start (buf, &path_copy) {
+ hash = _cairo_hash_bytes (hash, buf->points,
+ buf->num_points * sizeof (buf->points[0]));
+ count += buf->num_points;
+ } cairo_path_foreach_buf_end (buf, &path_copy);
+ hash = _cairo_hash_bytes (hash, &count, sizeof (count));
+
+ _cairo_path_fixed_fini (&path_copy);
+
+ return hash;
+}
+
+static unsigned long
+_cairo_shadow_hash (unsigned long hash, const cairo_shadow_t *shadow)
+{
+ return _cairo_hash_bytes (hash, shadow, sizeof (cairo_shadow_t) - sizeof (cairo_bool_t));
+}
+
+static unsigned long
+_cairo_shadow_hash_for_paint (const cairo_pattern_t *source,
+ const cairo_shadow_t *shadow)
+{
+ unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+ cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET;
+
+ hash = _cairo_pattern_hash_with_hash (hash, source, use_color);
+ return _cairo_shadow_hash (hash, shadow);
+}
+
+static unsigned long
+_cairo_shadow_hash_for_mask (const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_shadow_t *shadow)
+{
+ unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+ cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET;
+
+ hash = _cairo_pattern_hash_with_hash (hash, source, use_color);
+ hash = _cairo_pattern_hash_with_hash (hash, mask, use_color);
+ return _cairo_shadow_hash (hash, shadow);
+}
+
+static unsigned long
+_cairo_shadow_hash_for_fill (const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ const cairo_shadow_t *shadow)
+{
+ unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+ /* FIXME: for OVER operator, we don't need to hash the source
+ * color, for other operators, we might */
+ cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET;
+ use_color = FALSE;
+
+ hash = _cairo_pattern_hash_with_hash (hash, source, use_color);
+ hash = _cairo_path_fixed_rel_hash (hash, path);
+ hash = _cairo_hash_bytes (hash, &fill_rule, sizeof (cairo_fill_rule_t));
+ return _cairo_shadow_hash (hash, shadow);
+}
+
+static unsigned long
+_cairo_shadow_hash_for_stroke (const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_shadow_t *shadow)
+{
+ unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+
+ /* FIXME: for OVER operator, we don't need to hash the source
+ * color, for other operators, we might */
+ cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET;
+ use_color = FALSE;
+
+ hash = _cairo_pattern_hash_with_hash (hash, source, use_color);
+ hash = _cairo_path_fixed_rel_hash (hash, path);
+ hash = _cairo_stroke_style_hash (hash, stroke_style);
+ hash = _cairo_matrix_hash (hash, ctm);
+ return _cairo_shadow_hash (hash, shadow);
+}
+
+static void
+_cairo_shadow_cache_init (cairo_shadow_cache_t *shadow_cache,
+ cairo_surface_t *cache_surface,
+ unsigned long size,
+ unsigned long hash,
+ int x_blur,
+ int y_blur,
+ double scale)
+{
+ cairo_list_init (&shadow_cache->link);
+ shadow_cache->surface = cairo_surface_reference (cache_surface);
+ shadow_cache->size = size;
+ shadow_cache->hash = hash;
+ shadow_cache->x_blur = x_blur;
+ shadow_cache->y_blur = y_blur;
+ shadow_cache->scale = scale;
+}
+
+static void
+_cairo_shadow_cache_destroy (cairo_shadow_cache_t *shadow_cache)
+{
+ cairo_list_del (&shadow_cache->link);
+ cairo_surface_destroy (shadow_cache->surface);
+ free (shadow_cache);
+}
+
+static void
+_cairo_shadow_cache_list_shrink_to_accomodate (cairo_shadow_cache_list_t *shadow_caches,
+ unsigned long additional)
+{
+ cairo_shadow_cache_t *shadow_cache;
+
+ while (*(shadow_caches->size) + additional > MAX_SHADOW_CACHE_SIZE) {
+ shadow_cache = cairo_list_last_entry (shadow_caches->caches,
+ cairo_shadow_cache_t,
+ link);
+ *(shadow_caches->size) -= shadow_cache->size;
+ _cairo_shadow_cache_destroy (shadow_cache);
+ }
+}
+
+static cairo_shadow_cache_t *
+_cairo_shadow_cache_list_find (cairo_shadow_cache_list_t *shadow_caches,
+ unsigned long hash)
+{
+ cairo_shadow_cache_t *shadow_cache;
+
+ cairo_list_foreach_entry (shadow_cache,
+ cairo_shadow_cache_t,
+ shadow_caches->caches, link)
+ if (shadow_cache->hash == hash) {
+ return shadow_cache;
+ }
+
+ return NULL;
+}
+
+static double
+_calculate_shadow_extents_scale (cairo_rectangle_int_t *extents,
+ int shadow_width, int shadow_height)
+{
+ double x_scale = (double)extents->width / (double)shadow_width;
+ double y_scale = (double)extents->height / (double)shadow_height;
+
+ return MIN (1.0, MIN (x_scale, y_scale));
+}
+
+static void
+_cairo_shadow_cache_list_init (cairo_shadow_cache_list_t *shadow_cache_list,
+ cairo_surface_t *target)
+{
+ cairo_status_t status;
+ cairo_device_t *device = NULL;
+
+ if(target != NULL)
+ device = target->device;
+
+ if (device != NULL) {
+ shadow_cache_list->caches = &device->shadow_caches;
+ shadow_cache_list->size = &device->shadow_caches_size;
+ shadow_cache_list->locked = FALSE;
+ }
+ else if (target != NULL &&
+ target->backend &&
+ target->backend->has_shadow_cache &&
+ target->backend->has_shadow_cache (target)) {
+ status = target->backend->shadow_cache_acquire (target);
+ shadow_cache_list->locked = TRUE;
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ shadow_cache_list->caches = target->backend->get_shadow_cache (target);
+ if (shadow_cache_list->caches) {
+ shadow_cache_list->size = target->backend->get_shadow_cache_size (target);
+ }
+ }
+ }
+}
+
+static cairo_surface_t*
+_cairo_ensure_shadow_surface (cairo_surface_t *target,
+ cairo_rectangle_int_t *shadow_surface_extents,
+ int x_blur, int y_blur,
+ int shadow_width, int shadow_height)
+{
+ int width_out, height_out;
+ cairo_content_t content;
+ cairo_surface_t *shadow_surface;
+ cairo_bool_t has_blur = ! (x_blur == 0 && y_blur == 0);
+
+ if (target->backend->get_shadow_surface)
+ shadow_surface = target->backend->get_shadow_surface (target,
+ has_blur,
+ shadow_width,
+ shadow_height,
+ &width_out,
+ &height_out);
+ else {
+ if (has_blur) {
+ width_out = MIN (shadow_width, MAX_SHADOW_SIZE) * 0.5;
+ height_out = MIN (shadow_width, MAX_SHADOW_SIZE) * 0.5;
+ }
+ else {
+ width_out = MIN (shadow_width, MAX_SHADOW_SIZE);
+ height_out = MIN (shadow_width, MAX_SHADOW_SIZE);
+ }
+
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+ shadow_surface = cairo_surface_create_similar (target,
+ content,
+ width_out,
+ height_out);
+ _cairo_surface_release_device_reference (shadow_surface);
+ }
+
+ shadow_surface_extents->x = 0;
+ shadow_surface_extents->y = 0;
+ shadow_surface_extents->width = width_out;
+ shadow_surface_extents->height = height_out;
+
+ return shadow_surface;
+}
+
+/* A collection of routines to draw shadow*/
+
+cairo_status_t
+_cairo_surface_shadow_paint (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern = NULL;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_rectangle_int_t shadow_surface_extents;
+
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+
+ cairo_matrix_t m;
+ double scale;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+ cairo_content_t content;
+
+ unsigned long hash = 0;
+ cairo_shadow_cache_t *shadow_cache = NULL;
+ cairo_device_t *device = target->device;
+ unsigned long size;
+ cairo_surface_t *cache_surface = NULL;
+ cairo_bool_t bounded;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 &&
+ source->shadow.y_blur == 0.0);
+
+ cairo_shadow_cache_list_t shadow_cache_list;
+
+ if (shadow->type != CAIRO_SHADOW_DROP)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 &&
+ shadow->x_offset == 0.0 && shadow->y_offset == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_shadow_cache_list_init (&shadow_cache_list, target);
+ if (shadow_cache_list.caches != NULL) {
+ hash = _cairo_shadow_hash_for_paint (source, shadow);
+ shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash);
+ }
+
+ if (shadow_cache != NULL) {
+ /* paint the shadow surface to target */
+ x_blur = shadow_cache->x_blur;
+ y_blur = shadow_cache->y_blur;
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+
+ status = _cairo_surface_paint_get_offset_extents (target,
+ x_offset,
+ y_offset,
+ source,
+ clip,
+ &shadow_source.base,
+ &shadow_extents,
+ &bounded);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op, color_pattern,
+ shadow_pattern, clip);
+ cairo_list_move (&shadow_cache->link, shadow_cache_list.caches);
+ goto FINISH;
+ }
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ status = _cairo_surface_paint_get_offset_extents (target,
+ x_offset, y_offset,
+ source,
+ clip,
+ &shadow_source.base,
+ &shadow_extents,
+ &bounded);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 && shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2);
+ shadow_height = ceil (shadow_extents.height + y_blur * 2);
+
+ shadow_surface = _cairo_ensure_shadow_surface (target,
+ &shadow_surface_extents,
+ x_blur, y_blur,
+ shadow_width, shadow_height);
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ if ((device || shadow_cache_list.locked) &&
+ shadow->enable_cache && bounded && has_blur) {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ cache_surface = cairo_surface_create_similar (target, content,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height);
+ if (unlikely (cache_surface->status))
+ goto FINISH;
+
+ if (device)
+ _cairo_surface_release_device_reference (cache_surface);
+ }
+
+ scale = _calculate_shadow_extents_scale (&shadow_surface_extents,
+ shadow_width,
+ shadow_height);
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ /* paint with offset and scale */
+ status = _cairo_surface_scale_translate_paint (shadow_surface,
+ TRUE,
+ &m,
+ CAIRO_OPERATOR_OVER,
+ &shadow_source.base,
+ NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * scale * 0.5,
+ shadow_copy.y_blur * scale * 0.5);
+
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024);
+ if (unlikely (status))
+ goto FINISH;
+
+ if ((shadow_cache_list.locked ||device) &&
+ shadow->enable_cache && bounded && has_blur) {
+ status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER,
+ color_pattern, shadow_pattern, NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (shadow_pattern);
+
+ size = shadow_surface_extents.width * shadow_surface_extents.height;
+ _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list,
+ size);
+
+ shadow_cache = malloc (sizeof (cairo_shadow_cache_t));
+ _cairo_shadow_cache_init (shadow_cache,
+ cache_surface,
+ size,
+ hash,
+ x_blur,
+ y_blur,
+ scale);
+
+ cairo_list_add (&shadow_cache->link, shadow_cache_list.caches);
+ *shadow_cache_list.size += size;
+
+ shadow_pattern = cairo_pattern_create_for_surface (cache_surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ cairo_pattern_destroy (color_pattern);
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+ }
+ else
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op,
+ color_pattern, shadow_pattern, clip);
+
+FINISH:
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ cairo_surface_destroy (shadow_surface);
+ cairo_surface_destroy (cache_surface);
+
+ if (shadow_cache_list.locked)
+ target->backend->shadow_cache_release (target);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_shadow_mask (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_pattern_union_t shadow_mask;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern = NULL;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_rectangle_int_t shadow_surface_extents;
+ cairo_content_t content;
+
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+
+ cairo_matrix_t m;
+ double scale;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+
+ unsigned long hash = 0;
+ cairo_shadow_cache_t *shadow_cache = NULL;
+ cairo_device_t *device = target->device;
+ unsigned long size;
+ cairo_surface_t *cache_surface = NULL;
+ cairo_bool_t bounded;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 &&
+ source->shadow.y_blur == 0.0);
+
+ cairo_shadow_cache_list_t shadow_cache_list;
+
+ if (shadow->type != CAIRO_SHADOW_DROP)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 &&
+ shadow->x_offset == 0.0 && shadow->y_offset == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->x_blur == 0.0 && shadow->y_blur == 0.0) {
+ status = _cairo_surface_mask_get_offset_extents (target,
+ x_offset,
+ y_offset,
+ source,
+ mask,
+ clip,
+ &shadow_source.base,
+ &shadow_mask.base,
+ &shadow_extents,
+ &bounded);
+ if (unlikely (status)) {
+ return status;
+ }
+
+ cairo_matrix_init_identity (&m);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ /* stroke to target with offset */
+ shadow_source.base.shadow.type = CAIRO_SHADOW_NONE;
+ shadow_source.base.shadow.draw_shadow_only = FALSE;
+ status = _cairo_surface_scale_translate_mask (target,
+ FALSE,
+ &m,
+ op,
+ &shadow_source.base,
+ &shadow_mask.base,
+ clip);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ return status;
+ }
+
+ _cairo_shadow_cache_list_init (&shadow_cache_list, target);
+ if (shadow_cache_list.caches != NULL) {
+ hash = _cairo_shadow_hash_for_mask (source, mask, shadow);
+ shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash);
+ }
+
+ if (shadow_cache != NULL) {
+ /* paint the shadow surface to target */
+ x_blur = shadow_cache->x_blur;
+ y_blur = shadow_cache->y_blur;
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+
+ status = _cairo_surface_mask_get_offset_extents (target,
+ x_offset,
+ y_offset,
+ source,
+ mask,
+ clip,
+ &shadow_source.base,
+ &shadow_mask.base,
+ &shadow_extents,
+ &bounded);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op, color_pattern,
+ shadow_pattern, clip);
+ cairo_list_move (&shadow_cache->link, shadow_cache_list.caches);
+ goto FINISH;
+ }
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ status = _cairo_surface_mask_get_offset_extents (target,
+ x_offset, y_offset,
+ source,
+ mask,
+ clip,
+ &shadow_source.base,
+ &shadow_mask.base,
+ &shadow_extents,
+ &bounded);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 && shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2);
+ shadow_height = ceil (shadow_extents.height + y_blur * 2);
+
+ shadow_surface = _cairo_ensure_shadow_surface (target,
+ &shadow_surface_extents,
+ x_blur, y_blur,
+ shadow_width, shadow_height);
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && bounded && has_blur) {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ cache_surface = cairo_surface_create_similar (target, content,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height);
+ if (unlikely (cache_surface->status))
+ goto FINISH;
+
+ if (device)
+ _cairo_surface_release_device_reference (cache_surface);
+ }
+
+ scale = _calculate_shadow_extents_scale (&shadow_surface_extents,
+ shadow_width,
+ shadow_height);
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ /* paint with offset and scale */
+ status = _cairo_surface_scale_translate_mask (shadow_surface,
+ TRUE,
+ &m,
+ CAIRO_OPERATOR_OVER,
+ &shadow_source.base,
+ &shadow_mask.base,
+ NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * scale * 0.5,
+ shadow_copy.y_blur * scale * 0.5);
+
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024);
+ if (unlikely (status))
+ goto FINISH;
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && bounded && has_blur) {
+ status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER,
+ color_pattern, shadow_pattern, NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (shadow_pattern);
+
+ size = shadow_surface_extents.width * shadow_surface_extents.height;
+ _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list,
+ size);
+
+ shadow_cache = malloc (sizeof (cairo_shadow_cache_t));
+ _cairo_shadow_cache_init (shadow_cache,
+ cache_surface,
+ size,
+ hash,
+ x_blur,
+ y_blur,
+ scale);
+
+ cairo_list_add (&shadow_cache->link, shadow_cache_list.caches);
+ *shadow_cache_list.size += size;
+
+ shadow_pattern = cairo_pattern_create_for_surface (cache_surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ cairo_pattern_destroy (color_pattern);
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+ }
+ else
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op,
+ color_pattern, shadow_pattern, clip);
+
+FINISH:
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ cairo_surface_destroy (shadow_surface);
+ cairo_surface_destroy (cache_surface);
+
+ if (shadow_cache_list.locked)
+ target->backend->shadow_cache_release (target);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_inset_shadow_stroke (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_path_fixed_t shadow_path;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern = NULL;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_int_t shadow_surface_extents;
+ cairo_matrix_t shadow_ctm, shadow_ctm_inverse;
+ cairo_content_t content;
+
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+ cairo_color_t bg_color;
+
+ cairo_matrix_t m;
+ double scale;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+ unsigned long hash = 0;
+ cairo_shadow_cache_t *shadow_cache = NULL;
+ cairo_device_t *device = target->device;
+ unsigned long size;
+ cairo_surface_t *cache_surface = NULL;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 &&
+ source->shadow.y_blur == 0.0);
+ double line_width = stroke_style->line_width;
+
+ cairo_shadow_cache_list_t shadow_cache_list;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_shadow_cache_list_init (&shadow_cache_list, target);
+ if (shadow_cache_list.caches != NULL) {
+ hash = _cairo_shadow_hash_for_stroke (source, path, stroke_style, ctm, shadow);
+ shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash);
+ }
+
+ if (shadow_cache != NULL) {
+ /* paint the shadow surface to target */
+ x_blur = shadow_cache->x_blur;
+ y_blur = shadow_cache->y_blur;
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+
+ status = _cairo_surface_stroke_get_offset_extents (target,
+ TRUE,
+ x_offset,
+ y_offset,
+ source,
+ path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance, clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_ctm,
+ &shadow_ctm_inverse,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_stroke (target, op, shadow_pattern,
+ path, stroke_style,
+ ctm, ctm_inverse, tolerance,
+ antialias, clip);
+ cairo_list_move (&shadow_cache->link, shadow_cache_list.caches);
+ goto FINISH;
+ }
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ status = _cairo_surface_stroke_get_offset_extents (target,
+ TRUE,
+ x_offset, y_offset,
+ source,
+ path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance, clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_ctm,
+ &shadow_ctm_inverse,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2);
+ shadow_height = ceil (shadow_extents.height + y_blur * 2);
+
+ shadow_surface = _cairo_ensure_shadow_surface (target,
+ &shadow_surface_extents,
+ x_blur, y_blur,
+ shadow_width, shadow_height);
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ _cairo_surface_get_extents (shadow_surface, &extents);
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ cache_surface = cairo_surface_create_similar (target, content,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height);
+ if (unlikely (cache_surface->status))
+ goto FINISH;
+
+ if (device)
+ _cairo_surface_release_device_reference (cache_surface);
+ }
+
+ scale = _calculate_shadow_extents_scale (&shadow_surface_extents,
+ shadow_width,
+ shadow_height);
+ if (line_width * scale <= 1.0)
+ ((cairo_stroke_style_t *)stroke_style)->line_width = line_width / scale;
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ _cairo_color_init_rgba (&bg_color,
+ shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ /* paint with offset and scale */
+ status = _cairo_surface_scale_translate_stroke (shadow_surface,
+ &bg_color,
+ &m,
+ CAIRO_OPERATOR_CLEAR,
+ &shadow_source.base,
+ &shadow_path,
+ stroke_style,
+ &shadow_ctm,
+ &shadow_ctm_inverse,
+ tolerance,
+ antialias,
+ NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * scale * 0.5,
+ shadow_copy.y_blur * scale * 0.5);
+
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern,
+ line_width * scale);
+ if (unlikely (status))
+ goto FINISH;
+
+ /* blur to mask surface */
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ status = _cairo_surface_paint (cache_surface, CAIRO_OPERATOR_OVER,
+ shadow_pattern, NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (shadow_pattern);
+
+ size = shadow_surface_extents.width * shadow_surface_extents.height;
+ _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list,
+ size);
+
+ shadow_cache = malloc (sizeof (cairo_shadow_cache_t));
+ _cairo_shadow_cache_init (shadow_cache,
+ cache_surface,
+ size,
+ hash,
+ x_blur,
+ y_blur,
+ scale);
+
+ cairo_list_add (&shadow_cache->link, shadow_cache_list.caches);
+ *shadow_cache_list.size += size;
+
+ shadow_pattern = cairo_pattern_create_for_surface (cache_surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_stroke (target, op, shadow_pattern,
+ path, stroke_style, ctm,
+ ctm_inverse, tolerance,
+ antialias, clip);
+
+ }
+ else {
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+ status = _cairo_surface_stroke (target, op, shadow_pattern,
+ path, stroke_style,
+ ctm, ctm_inverse,
+ tolerance, antialias, clip);
+ }
+
+FINISH:
+ _cairo_path_fixed_fini (&shadow_path);
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ cairo_surface_destroy (shadow_surface);
+ cairo_surface_destroy (cache_surface);
+
+ if (shadow_cache_list.locked)
+ target->backend->shadow_cache_release (target);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ ((cairo_stroke_style_t *)stroke_style)->line_width = line_width;
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_shadow_stroke (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_path_fixed_t shadow_path;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern = NULL;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_rectangle_int_t shadow_surface_extents;
+ cairo_matrix_t shadow_ctm, shadow_ctm_inverse;
+ cairo_content_t content;
+ cairo_color_t bg_color;
+
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+
+ cairo_matrix_t m;
+ double scale;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+ unsigned long hash = 0;
+ cairo_shadow_cache_t *shadow_cache = NULL;
+ cairo_device_t *device = target->device;
+ unsigned long size;
+ cairo_surface_t *cache_surface = NULL;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 &&
+ source->shadow.y_blur == 0.0);
+ double line_width = stroke_style->line_width;
+
+ cairo_shadow_cache_list_t shadow_cache_list;
+
+ if (shadow->type == CAIRO_SHADOW_NONE)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 &&
+ shadow->x_offset == 0.0 && shadow->y_offset == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->type == CAIRO_SHADOW_INSET)
+ return _cairo_surface_inset_shadow_stroke (target, op, source,
+ path, stroke_style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip, shadow);
+
+ _cairo_shadow_cache_list_init (&shadow_cache_list, target);
+ if (shadow_cache_list.caches != NULL) {
+ hash = _cairo_shadow_hash_for_stroke (source, path, stroke_style, ctm, shadow);
+ shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash);
+ }
+
+ if (shadow_cache != NULL) {
+ /* paint the shadow surface to target */
+ x_blur = shadow_cache->x_blur;
+ y_blur = shadow_cache->y_blur;
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+
+ status = _cairo_surface_stroke_get_offset_extents (target,
+ FALSE,
+ x_offset,
+ y_offset,
+ source,
+ path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance, clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_ctm,
+ &shadow_ctm_inverse,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op, color_pattern,
+ shadow_pattern, clip);
+ cairo_list_move (&shadow_cache->link, shadow_cache_list.caches);
+ goto FINISH;
+ }
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ status = _cairo_surface_stroke_get_offset_extents (target,
+ FALSE,
+ x_offset, y_offset,
+ source,
+ path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance, clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_ctm,
+ &shadow_ctm_inverse,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2);
+ shadow_height = ceil (shadow_extents.height + y_blur * 2);
+
+ shadow_surface = _cairo_ensure_shadow_surface (target,
+ &shadow_surface_extents,
+ x_blur, y_blur,
+ shadow_width, shadow_height);
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ cache_surface = cairo_surface_create_similar (target, content,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height);
+ if (unlikely (cache_surface->status))
+ goto FINISH;
+
+ if (device)
+ _cairo_surface_release_device_reference (cache_surface);
+ }
+
+ scale = _calculate_shadow_extents_scale (&shadow_surface_extents,
+ shadow_width,
+ shadow_height);
+
+ if (line_width * scale <= 1.0)
+ ((cairo_stroke_style_t *)stroke_style)->line_width = line_width / scale;
+
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ /* paint with offset and scale */
+ _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0);
+ status = _cairo_surface_scale_translate_stroke (shadow_surface,
+ &bg_color,
+ &m,
+ CAIRO_OPERATOR_OVER,
+ &shadow_source.base,
+ &shadow_path,
+ stroke_style,
+ &shadow_ctm,
+ &shadow_ctm_inverse,
+ tolerance,
+ antialias,
+ NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * scale * 0.5,
+ shadow_copy.y_blur * scale * 0.5);
+
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern,
+ line_width * scale);
+ if (unlikely (status))
+ goto FINISH;
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER,
+ color_pattern, shadow_pattern, NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (shadow_pattern);
+
+ size = shadow_surface_extents.width * shadow_surface_extents.height;
+ _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list,
+ size);
+
+ shadow_cache = malloc (sizeof (cairo_shadow_cache_t));
+ _cairo_shadow_cache_init (shadow_cache,
+ cache_surface,
+ size,
+ hash,
+ x_blur,
+ y_blur,
+ scale);
+
+ cairo_list_add (&shadow_cache->link, shadow_cache_list.caches);
+ *shadow_cache_list.size += size;
+
+ shadow_pattern = cairo_pattern_create_for_surface (cache_surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ cairo_pattern_destroy (color_pattern);
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+ }
+ else
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op,
+ color_pattern, shadow_pattern, clip);
+
+FINISH:
+ _cairo_path_fixed_fini (&shadow_path);
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ cairo_surface_destroy (shadow_surface);
+ cairo_surface_destroy (cache_surface);
+
+ if (shadow_cache_list.locked)
+ target->backend->shadow_cache_release (target);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ ((cairo_stroke_style_t *)stroke_style)->line_width = line_width;
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_inset_shadow_fill (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_path_fixed_t shadow_path;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern = NULL;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_surface_t *cache_surface = NULL;
+ cairo_rectangle_int_t shadow_surface_extents;
+ cairo_rectangle_int_t extents;
+ cairo_content_t content;
+
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+
+ cairo_matrix_t m;
+ double scale;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+ unsigned long hash = 0;
+ cairo_shadow_cache_t *shadow_cache = NULL;
+ cairo_device_t *device = target->device;
+ unsigned long size;
+ cairo_color_t bg_color;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 &&
+ source->shadow.y_blur == 0.0);
+
+ cairo_shadow_cache_list_t shadow_cache_list;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_shadow_cache_list_init (&shadow_cache_list, target);
+ if (shadow_cache_list.caches != NULL) {
+ hash = _cairo_shadow_hash_for_fill (source, path, fill_rule, shadow);
+ shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash);
+ }
+
+ if (shadow_cache != NULL) {
+ /* paint the shadow surface to target */
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+ x_blur = shadow_cache->x_blur;
+ y_blur = shadow_cache->y_blur;
+
+ status = _cairo_surface_fill_get_offset_extents (target,
+ TRUE,
+ x_offset,
+ y_offset,
+ source,
+ path,
+ fill_rule,
+ clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ if (! shadow->path_is_fill_with_spread)
+ status = _cairo_surface_fill (target, op, shadow_pattern,
+ path, fill_rule, tolerance,
+ antialias, clip);
+ else
+ status = _cairo_surface_paint (target, op, shadow_pattern,
+ clip);
+
+ cairo_list_move (&shadow_cache->link, shadow_cache_list.caches);
+ goto FINISH;
+ }
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ status = _cairo_surface_fill_get_offset_extents (target,
+ TRUE,
+ x_offset, y_offset,
+ source,
+ path,
+ fill_rule,
+ clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 && shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2);
+ shadow_height = ceil (shadow_extents.height + y_blur * 2);
+
+ shadow_surface = _cairo_ensure_shadow_surface (target,
+ &shadow_surface_extents,
+ x_blur, y_blur,
+ shadow_width, shadow_height);
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ _cairo_surface_get_extents (shadow_surface, &extents);
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ cache_surface = cairo_surface_create_similar (target, content,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height);
+ if (unlikely (cache_surface->status))
+ goto FINISH;
+
+ if (device)
+ _cairo_surface_release_device_reference (cache_surface);
+ }
+
+ scale = _calculate_shadow_extents_scale (&shadow_surface_extents,
+ shadow_width,
+ shadow_height);
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ _cairo_color_init_rgba (&bg_color,
+ shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+ /* paint with offset and scale */
+ status = _cairo_surface_scale_translate_fill (shadow_surface,
+ &bg_color,
+ &m,
+ CAIRO_OPERATOR_CLEAR,
+ &shadow_source.base,
+ &shadow_path,
+ fill_rule,
+ tolerance,
+ antialias,
+ NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * scale * 0.5,
+ shadow_copy.y_blur * scale * 0.5);
+
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024);
+ if (unlikely (status))
+ goto FINISH;
+
+ /* blur to cache surface */
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ status = _cairo_surface_paint (cache_surface, CAIRO_OPERATOR_OVER,
+ shadow_pattern, NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (shadow_pattern);
+
+ size = shadow_surface_extents.width * shadow_surface_extents.height;
+ _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list,
+ size);
+
+ shadow_cache = malloc (sizeof (cairo_shadow_cache_t));
+ _cairo_shadow_cache_init (shadow_cache,
+ cache_surface,
+ size,
+ hash,
+ x_blur,
+ y_blur,
+ scale);
+
+ cairo_list_add (&shadow_cache->link, shadow_cache_list.caches);
+ *shadow_cache_list.size += size;
+
+ shadow_pattern = cairo_pattern_create_for_surface (cache_surface);
+
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+ if (! shadow_copy.path_is_fill_with_spread)
+ status = _cairo_surface_fill (target, op, shadow_pattern,
+ path, fill_rule, tolerance,
+ antialias, clip);
+ else
+ status = _cairo_surface_paint (target, op, shadow_pattern,
+ clip);
+ }
+ else {
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+ if (! shadow_copy.path_is_fill_with_spread)
+ status = _cairo_surface_fill (target, op, shadow_pattern,
+ path, fill_rule, tolerance,
+ antialias, clip);
+ else
+ status = _cairo_surface_paint (target, op, shadow_pattern,
+ clip);
+ }
+FINISH:
+ _cairo_path_fixed_fini (&shadow_path);
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ if (cache_surface)
+ cairo_surface_destroy (cache_surface);
+
+ cairo_surface_destroy (shadow_surface);
+
+ if (shadow_cache_list.locked)
+ target->backend->shadow_cache_release (target);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_shadow_fill (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_path_fixed_t shadow_path;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern = NULL;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_rectangle_int_t shadow_surface_extents;
+ cairo_content_t content;
+
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+ cairo_color_t bg_color;
+
+ cairo_matrix_t m;
+ double scale;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+ unsigned long hash = 0;
+ cairo_shadow_cache_t *shadow_cache = NULL;
+ cairo_device_t *device = target->device;
+ unsigned long size;
+ cairo_surface_t *cache_surface = NULL;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 &&
+ source->shadow.y_blur == 0.0);
+
+ cairo_shadow_cache_list_t shadow_cache_list;
+
+ if (shadow->type == CAIRO_SHADOW_NONE)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 &&
+ shadow->x_offset == 0.0 && shadow->y_offset == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->type == CAIRO_SHADOW_INSET)
+ return _cairo_surface_inset_shadow_fill (target, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip, shadow);
+
+ if (shadow->x_blur == 0.0 && shadow->y_blur == 0.0) {
+ status = _cairo_surface_fill_get_offset_extents (target,
+ FALSE,
+ x_offset,
+ y_offset,
+ source,
+ path,
+ fill_rule,
+ clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_extents);
+ if (unlikely (status)) {
+ _cairo_path_fixed_fini (&shadow_path);
+ return status;
+ }
+
+ cairo_matrix_init_identity (&m);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ /* stroke to target with offset */
+ shadow_source.base.shadow.type = CAIRO_SHADOW_NONE;
+ shadow_source.base.shadow.draw_shadow_only = FALSE;
+ status = _cairo_surface_scale_translate_fill (target,
+ NULL,
+ &m,
+ op,
+ &shadow_source.base,
+ &shadow_path,
+ fill_rule,
+ tolerance,
+ antialias,
+ clip);
+
+ _cairo_path_fixed_fini (&shadow_path);
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ return status;
+ }
+
+ _cairo_shadow_cache_list_init (&shadow_cache_list, target);
+ if (shadow_cache_list.caches != NULL) {
+ hash = _cairo_shadow_hash_for_fill (source, path, fill_rule, shadow);
+ shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash);
+ }
+
+ if (shadow_cache != NULL) {
+ /* paint the shadow surface to target */
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+ x_blur = shadow_cache->x_blur;
+ y_blur = shadow_cache->y_blur;
+
+ status = _cairo_surface_fill_get_offset_extents (target,
+ FALSE,
+ x_offset,
+ y_offset,
+ source,
+ path,
+ fill_rule,
+ clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 || shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op, color_pattern,
+ shadow_pattern, clip);
+ cairo_list_move (&shadow_cache->link, shadow_cache_list.caches);
+ goto FINISH;
+ }
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ status = _cairo_surface_fill_get_offset_extents (target,
+ FALSE,
+ x_offset, y_offset,
+ source,
+ path,
+ fill_rule,
+ clip,
+ &shadow_source.base,
+ &shadow_path,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 && shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2);
+ shadow_height = ceil (shadow_extents.height + y_blur * 2);
+
+ shadow_surface = _cairo_ensure_shadow_surface (target,
+ &shadow_surface_extents,
+ x_blur, y_blur,
+ shadow_width, shadow_height);
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ cache_surface = cairo_surface_create_similar (target, content,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height);
+ if (unlikely (cache_surface->status))
+ goto FINISH;
+
+ if (device)
+ _cairo_surface_release_device_reference (cache_surface);
+ }
+
+ scale = _calculate_shadow_extents_scale (&shadow_surface_extents,
+ shadow_width,
+ shadow_height);
+ cairo_matrix_init_scale (&m, scale, scale);
+ cairo_matrix_translate (&m, -x_offset, -y_offset);
+
+ /* paint with offset and scale */
+ _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0);
+ status = _cairo_surface_scale_translate_fill (shadow_surface,
+ &bg_color,
+ &m,
+ CAIRO_OPERATOR_OVER,
+ &shadow_source.base,
+ &shadow_path,
+ fill_rule,
+ tolerance,
+ antialias,
+ NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * scale * 0.5,
+ shadow_copy.y_blur * scale * 0.5);
+
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024);
+ if (unlikely (status))
+ goto FINISH;
+
+ if ((shadow_cache_list.locked || device) &&
+ shadow->enable_cache && has_blur) {
+ status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER,
+ color_pattern, shadow_pattern, NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (shadow_pattern);
+
+ size = shadow_surface_extents.width * shadow_surface_extents.height;
+ _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list,
+ size);
+
+ shadow_cache = malloc (sizeof (cairo_shadow_cache_t));
+ _cairo_shadow_cache_init (shadow_cache,
+ cache_surface,
+ size,
+ hash,
+ x_blur,
+ y_blur,
+ scale);
+
+ cairo_list_add (&shadow_cache->link, shadow_cache_list.caches);
+ *shadow_cache_list.size += size;
+
+ shadow_pattern = cairo_pattern_create_for_surface (cache_surface);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ cairo_pattern_destroy (color_pattern);
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ 1.0);
+ }
+ else
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op,
+ color_pattern, shadow_pattern, clip);
+
+FINISH:
+ _cairo_path_fixed_fini (&shadow_path);
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ cairo_surface_destroy (cache_surface);
+
+ cairo_surface_destroy (shadow_surface);
+
+ if (shadow_cache_list.locked)
+ target->backend->shadow_cache_release (target);
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_inset_shadow_glyphs (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern = NULL;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_surface_t *mask_surface = NULL;
+ cairo_rectangle_int_t shadow_surface_extents;
+ cairo_glyph_t *shadow_glyphs;
+ cairo_content_t content;
+ cairo_color_t bg_color;
+
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+
+ cairo_matrix_t m;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ shadow_glyphs = (cairo_glyph_t *)_cairo_malloc_ab (num_glyphs,
+ sizeof (cairo_glyph_t));
+ if (shadow_glyphs == NULL) {
+ status = CAIRO_STATUS_NO_MEMORY;
+ goto FINISH;
+ }
+
+ status = _cairo_surface_glyphs_get_offset_extents (target,
+ TRUE,
+ 0, 0,
+ source,
+ scaled_font,
+ glyphs,
+ num_glyphs,
+ clip,
+ &shadow_source.base,
+ shadow_glyphs,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 && shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2 + fabs (shadow->x_offset));
+ shadow_height = ceil (shadow_extents.height + y_blur * 2 + fabs (shadow->y_offset));
+
+ if (target->backend->get_glyph_shadow_surface) {
+ shadow_surface = target->backend->get_glyph_shadow_surface (target,
+ shadow_width,
+ shadow_height,
+ FALSE);
+ }
+ else {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+ shadow_surface = cairo_surface_create_similar (target,
+ content,
+ shadow_width,
+ shadow_height);
+ _cairo_surface_release_device_reference (shadow_surface);
+ }
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ if(! _cairo_surface_get_extents (shadow_surface, &shadow_surface_extents))
+ goto FINISH;
+
+ if (target->backend->get_glyph_shadow_mask_surface) {
+ mask_surface = target->backend->get_glyph_shadow_mask_surface (shadow_surface,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height,
+ 0);
+ }
+ else {
+ mask_surface = cairo_surface_create_similar (shadow_surface,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ shadow_surface_extents.width,
+ shadow_surface_extents.height);
+ _cairo_surface_release_device_reference (mask_surface);
+ }
+ if (! mask_surface || unlikely (mask_surface->status))
+ goto FINISH;
+
+ cairo_matrix_init_translate (&m, -x_offset, -y_offset);
+
+ /* paint with offset and scale */
+ _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0);
+ color_pattern = cairo_pattern_create_rgba (1, 1, 1, 1);
+
+ status = _cairo_surface_translate_glyphs (mask_surface,
+ &bg_color,
+ &m,
+ CAIRO_OPERATOR_OVER,
+ color_pattern,
+ scaled_font,
+ shadow_glyphs,
+ num_glyphs,
+ NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ /* with fast path, we paint shadow color and source directly to
+ * shadow_surface, and then blur to target */
+ cairo_pattern_destroy (color_pattern);
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ status = _cairo_surface_paint (shadow_surface,
+ CAIRO_OPERATOR_SOURCE,
+ color_pattern, NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ shadow_pattern = cairo_pattern_create_for_surface (mask_surface);
+ cairo_pattern_destroy (color_pattern);
+ color_pattern = cairo_pattern_create_rgba (0, 0, 0, 0);
+
+ status = _cairo_surface_mask (shadow_surface, CAIRO_OPERATOR_SOURCE,
+ color_pattern, shadow_pattern,
+ NULL);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (shadow_pattern);
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * 0.5,
+ shadow_copy.y_blur * 0.5);
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_destroy (color_pattern);
+ color_pattern = cairo_pattern_create_for_surface (mask_surface);
+ cairo_pattern_set_matrix (color_pattern, &m);
+
+ cairo_matrix_translate (&m, -shadow->x_offset,
+ -shadow->y_offset);
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op, shadow_pattern,
+ color_pattern, clip);
+FINISH:
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ free (shadow_glyphs);
+
+ cairo_surface_destroy (shadow_surface);
+ cairo_surface_destroy (mask_surface);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_shadow_glyphs (cairo_surface_t *target,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_clip_t *clip,
+ const cairo_shadow_t *shadow)
+{
+ cairo_status_t status;
+ cairo_pattern_union_t shadow_source;
+ cairo_rectangle_t shadow_extents;
+ cairo_pattern_t *shadow_pattern = NULL;
+ cairo_pattern_t *color_pattern;
+ cairo_surface_t *shadow_surface = NULL;
+ cairo_rectangle_int_t shadow_surface_extents;
+
+ cairo_glyph_t *shadow_glyphs;
+ cairo_content_t content;
+ cairo_color_t bg_color;
+ int shadow_width, shadow_height;
+ int x_blur, y_blur;
+ cairo_shadow_t shadow_copy = *shadow;
+
+ cairo_matrix_t m;
+ double x_offset = shadow->x_offset;
+ double y_offset = shadow->y_offset;
+ cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+
+ if (shadow->type == CAIRO_SHADOW_NONE)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->color.alpha == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 &&
+ shadow->x_offset == 0.0 && shadow->y_offset == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shadow->type == CAIRO_SHADOW_INSET)
+ return _cairo_surface_inset_shadow_glyphs (target, op, source,
+ scaled_font, glyphs,
+ num_glyphs, clip,
+ shadow);
+ shadow_glyphs = (cairo_glyph_t *)_cairo_malloc_ab (num_glyphs,
+ sizeof (cairo_glyph_t));
+ if (shadow_glyphs == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE;
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE;
+
+ x_blur = ceil (shadow_copy.x_blur);
+ y_blur = ceil (shadow_copy.y_blur);
+
+ color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red,
+ shadow_copy.color.green,
+ shadow_copy.color.blue,
+ shadow_copy.color.alpha);
+
+ status = _cairo_surface_glyphs_get_offset_extents (target,
+ FALSE,
+ x_offset, y_offset,
+ source,
+ scaled_font,
+ glyphs,
+ num_glyphs,
+ clip,
+ &shadow_source.base,
+ shadow_glyphs,
+ &shadow_extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (shadow_extents.width == 0 && shadow_extents.height == 0)
+ goto FINISH;
+
+ x_offset = shadow_extents.x - x_blur;
+ y_offset = shadow_extents.y - y_blur;
+
+ shadow_width = ceil (shadow_extents.width + x_blur * 2);
+ shadow_height = ceil (shadow_extents.height + y_blur * 2);
+
+ if (target->backend->get_glyph_shadow_surface)
+ shadow_surface = target->backend->get_glyph_shadow_surface (target,
+ shadow_width,
+ shadow_height,
+ FALSE);
+ else {
+ content = cairo_surface_get_content (target);
+ if (content == CAIRO_CONTENT_COLOR)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+ shadow_surface = cairo_surface_create_similar (target,
+ content,
+ shadow_width,
+ shadow_height);
+ _cairo_surface_release_device_reference (shadow_surface);
+ }
+ if (! shadow_surface || unlikely (shadow_surface->status))
+ goto FINISH;
+
+ if(! _cairo_surface_get_extents (shadow_surface, &shadow_surface_extents))
+ goto FINISH;
+
+ cairo_matrix_init_translate (&m, -x_offset, -y_offset);
+
+ /* paint with offset and scale */
+ _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0);
+ status = _cairo_surface_translate_glyphs (shadow_surface,
+ &bg_color,
+ &m,
+ CAIRO_OPERATOR_OVER,
+ &shadow_source.base,
+ scaled_font,
+ shadow_glyphs,
+ num_glyphs,
+ NULL);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ shadow_pattern = cairo_pattern_create_for_surface (shadow_surface);
+ cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN);
+ cairo_pattern_set_sigma (shadow_pattern,
+ shadow_copy.x_blur * 0.5,
+ shadow_copy.y_blur * 0.5);
+
+ status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024);
+ if (unlikely (status))
+ goto FINISH;
+
+ cairo_pattern_set_matrix (shadow_pattern, &m);
+
+ status = _cairo_surface_mask (target, op, color_pattern,
+ shadow_pattern, NULL);
+
+FINISH:
+ cairo_pattern_destroy (color_pattern);
+
+ if (shadow_pattern)
+ cairo_pattern_destroy (shadow_pattern);
+
+ free (shadow_glyphs);
+
+ cairo_surface_destroy (shadow_surface);
+
+ ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only;
+ ((cairo_pattern_t *)source)->shadow.type = shadow_type;
+ return status;
+}
diff --git a/src/cairo-surface-snapshot-inline.h b/src/cairo-surface-snapshot-inline.h
new file mode 100755
index 000000000..bf89c772b
--- /dev/null
+++ b/src/cairo-surface-snapshot-inline.h
@@ -0,0 +1,67 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SNAPSHOT_INLINE_H
+#define CAIRO_SURFACE_SNAPSHOT_INLINE_H
+
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-inline.h"
+
+static inline cairo_bool_t
+_cairo_surface_snapshot_is_reused (cairo_surface_t *surface)
+{
+ return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) > 2;
+}
+
+static inline cairo_surface_t *
+_cairo_surface_snapshot_get_target (cairo_surface_t *surface)
+{
+ cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
+ cairo_surface_t *target;
+
+ CAIRO_MUTEX_LOCK (snapshot->mutex);
+ target = _cairo_surface_reference (snapshot->target);
+ CAIRO_MUTEX_UNLOCK (snapshot->mutex);
+
+ return target;
+}
+
+static inline cairo_bool_t
+_cairo_surface_is_snapshot (cairo_surface_t *surface)
+{
+ return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT;
+}
+
+#endif /* CAIRO_SURFACE_SNAPSHOT_INLINE_H */
diff --git a/src/cairo-surface-snapshot-private.h b/src/cairo-surface-snapshot-private.h
new file mode 100755
index 000000000..58bee7b49
--- /dev/null
+++ b/src/cairo-surface-snapshot-private.h
@@ -0,0 +1,51 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
+#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
+
+#include "cairo-mutex-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+struct _cairo_surface_snapshot {
+ cairo_surface_t base;
+
+ cairo_mutex_t mutex;
+ cairo_surface_t *target;
+ cairo_surface_t *clone;
+};
+
+#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */
diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c
new file mode 100755
index 000000000..68bf9054e
--- /dev/null
+++ b/src/cairo-surface-snapshot.c
@@ -0,0 +1,289 @@
+/* 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-image-surface-private.h"
+#include "cairo-surface-snapshot-inline.h"
+
+static cairo_status_t
+_cairo_surface_snapshot_finish (void *abstract_surface)
+{
+ cairo_surface_snapshot_t *surface = abstract_surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (surface->clone != NULL) {
+ cairo_surface_finish (surface->clone);
+ status = surface->clone->status;
+
+ cairo_surface_destroy (surface->clone);
+ }
+
+ CAIRO_MUTEX_FINI (surface->mutex);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_snapshot_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_surface_snapshot_t *surface = abstract_surface;
+ cairo_surface_t *target;
+ cairo_status_t status;
+
+ target = _cairo_surface_snapshot_get_target (&surface->base);
+ status = _cairo_surface_flush (target, flags);
+ cairo_surface_destroy (target);
+
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_surface_snapshot_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_surface_snapshot_t *surface = abstract_surface;
+ return _cairo_surface_get_source (surface->target, extents); /* XXX racy */
+}
+
+struct snapshot_extra {
+ cairo_surface_t *target;
+ void *extra;
+};
+
+static cairo_status_t
+_cairo_surface_snapshot_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **extra_out)
+{
+ cairo_surface_snapshot_t *surface = abstract_surface;
+ struct snapshot_extra *extra;
+ cairo_status_t status;
+
+ extra = malloc (sizeof (*extra));
+ if (unlikely (extra == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ extra->target = _cairo_surface_snapshot_get_target (&surface->base);
+ status = _cairo_surface_acquire_source_image (extra->target, image_out, &extra->extra);
+ if (unlikely (status)) {
+ cairo_surface_destroy (extra->target);
+ free (extra);
+ }
+
+ *extra_out = extra;
+ return status;
+}
+
+static void
+_cairo_surface_snapshot_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *_extra)
+{
+ struct snapshot_extra *extra = _extra;
+
+ _cairo_surface_release_source_image (extra->target, image, extra->extra);
+ cairo_surface_destroy (extra->target);
+ free (extra);
+}
+
+static cairo_bool_t
+_cairo_surface_snapshot_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_surface_snapshot_t *surface = abstract_surface;
+ cairo_surface_t *target;
+ cairo_bool_t bounded;
+
+ target = _cairo_surface_snapshot_get_target (&surface->base);
+ bounded = _cairo_surface_get_extents (target, extents);
+ cairo_surface_destroy (target);
+
+ return bounded;
+}
+
+static const cairo_surface_backend_t _cairo_surface_snapshot_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT,
+ _cairo_surface_snapshot_finish,
+ NULL,
+
+ NULL, /* create similar */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_snapshot_source,
+ _cairo_surface_snapshot_acquire_source_image,
+ _cairo_surface_snapshot_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_surface_snapshot_get_extents,
+ NULL, /* get-font-options */
+
+ _cairo_surface_snapshot_flush,
+};
+
+static void
+_cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
+{
+ cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
+ cairo_image_surface_t *image;
+ cairo_surface_t *clone;
+ void *extra;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s: target=%d\n",
+ __FUNCTION__, snapshot->target->unique_id));
+
+ /* We need to make an image copy of the original surface since the
+ * snapshot may exceed the lifetime of the original device, i.e.
+ * when we later need to use the snapshot the data may have already
+ * been lost.
+ */
+
+ CAIRO_MUTEX_LOCK (snapshot->mutex);
+
+ if (snapshot->target->backend->snapshot != NULL) {
+ clone = snapshot->target->backend->snapshot (snapshot->target);
+ if (clone != NULL) {
+ assert (clone->status || ! _cairo_surface_is_snapshot (clone));
+ goto done;
+ }
+ }
+
+ /* XXX copy to a similar surface, leave acquisition till later?
+ * We should probably leave such decisions to the backend in case we
+ * rely upon devices/connections like Xlib.
+ */
+ status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
+ if (unlikely (status)) {
+ snapshot->target = _cairo_surface_create_in_error (status);
+ status = _cairo_surface_set_error (surface, status);
+ goto unlock;
+ }
+ clone = image->base.backend->snapshot (&image->base);
+ _cairo_surface_release_source_image (snapshot->target, image, extra);
+
+done:
+ status = _cairo_surface_set_error (surface, clone->status);
+ snapshot->target = snapshot->clone = clone;
+ snapshot->base.type = clone->type;
+unlock:
+ CAIRO_MUTEX_UNLOCK (snapshot->mutex);
+}
+
+/**
+ * _cairo_surface_snapshot:
+ * @surface: a #cairo_surface_t
+ *
+ * Make an immutable reference to @surface. It is an error to call a
+ * surface-modifying function on the result of this function. The
+ * resulting 'snapshot' is a lazily copied-on-write surface i.e. it
+ * remains a reference to the original surface until that surface is
+ * written to again, at which time a copy is made of the original surface
+ * and the snapshot then points to that instead. Multiple snapshots of the
+ * same unmodified surface point to the same copy.
+ *
+ * The caller owns the return value and should call
+ * cairo_surface_destroy() when finished with it. This function will not
+ * return %NULL, but will return a nil surface instead.
+ *
+ * Return value: The snapshot surface. Note that the return surface
+ * may not necessarily be of the same type as @surface.
+ **/
+cairo_surface_t *
+_cairo_surface_snapshot (cairo_surface_t *surface)
+{
+ cairo_surface_snapshot_t *snapshot;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->unique_id));
+
+ if (unlikely (surface->status))
+ return _cairo_surface_create_in_error (surface->status);
+
+ if (unlikely (surface->finished))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ if (surface->snapshot_of != NULL)
+ return cairo_surface_reference (surface);
+
+ if (_cairo_surface_is_snapshot (surface))
+ return cairo_surface_reference (surface);
+
+ snapshot = (cairo_surface_snapshot_t *)
+ _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend);
+ if (snapshot != NULL)
+ return cairo_surface_reference (&snapshot->base);
+
+ snapshot = 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;
+
+ CAIRO_MUTEX_INIT (snapshot->mutex);
+ snapshot->target = surface;
+ snapshot->clone = NULL;
+
+ status = _cairo_surface_copy_mime_data (&snapshot->base, surface);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&snapshot->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ snapshot->base.device_transform = surface->device_transform;
+ snapshot->base.device_transform_inverse = surface->device_transform_inverse;
+
+ _cairo_surface_attach_snapshot (surface,
+ &snapshot->base,
+ _cairo_surface_snapshot_copy_on_write);
+
+ return &snapshot->base;
+}
diff --git a/src/cairo-surface-subsurface-inline.h b/src/cairo-surface-subsurface-inline.h
new file mode 100755
index 000000000..0cd09e63e
--- /dev/null
+++ b/src/cairo-surface-subsurface-inline.h
@@ -0,0 +1,72 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SUBSURFACE_INLINE_H
+#define CAIRO_SURFACE_SUBSURFACE_INLINE_H
+
+#include "cairo-surface-subsurface-private.h"
+
+static inline cairo_surface_t *
+_cairo_surface_subsurface_get_target (cairo_surface_t *surface)
+{
+ return ((cairo_surface_subsurface_t *) surface)->target;
+}
+
+static inline void
+_cairo_surface_subsurface_offset (cairo_surface_t *surface,
+ int *x, int *y)
+{
+ cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
+ *x += ss->extents.x;
+ *y += ss->extents.y;
+}
+
+static inline cairo_surface_t *
+_cairo_surface_subsurface_get_target_with_offset (cairo_surface_t *surface,
+ int *x, int *y)
+{
+ cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
+ *x += ss->extents.x;
+ *y += ss->extents.y;
+ return ss->target;
+}
+
+static inline cairo_bool_t
+_cairo_surface_is_subsurface (cairo_surface_t *surface)
+{
+ return surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE;
+}
+
+#endif /* CAIRO_SURFACE_SUBSURFACE_INLINE_H */
diff --git a/src/cairo-surface-subsurface-private.h b/src/cairo-surface-subsurface-private.h
new file mode 100755
index 000000000..89c5cc01b
--- /dev/null
+++ b/src/cairo-surface-subsurface-private.h
@@ -0,0 +1,55 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
+#define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+struct _cairo_surface_subsurface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t extents;
+
+ cairo_surface_t *target;
+ cairo_surface_t *snapshot;
+};
+
+cairo_private void
+_cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface,
+ cairo_surface_t *snapshot);
+
+#endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */
diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c
new file mode 100755
index 000000000..0b4915e7e
--- /dev/null
+++ b/src/cairo-surface-subsurface.c
@@ -0,0 +1,574 @@
+/* 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-clip-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+static const cairo_surface_backend_t _cairo_surface_subsurface_backend;
+
+static cairo_status_t
+_cairo_surface_subsurface_finish (void *abstract_surface)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+
+ cairo_surface_destroy (surface->target);
+ cairo_surface_destroy (surface->snapshot);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_create_similar (void *other,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_surface_subsurface_t *surface = other;
+
+ if (surface->target->backend->create_similar == NULL)
+ return NULL;
+
+ return surface->target->backend->create_similar (surface->target, content, width, height);
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_create_similar_image (void *other,
+ cairo_format_t format,
+ int width, int height)
+{
+ cairo_surface_subsurface_t *surface = other;
+
+ if (surface->target->backend->create_similar_image == NULL)
+ return NULL;
+
+ return surface->target->backend->create_similar_image (surface->target,
+ format,
+ width, height);
+}
+
+static cairo_image_surface_t *
+_cairo_surface_subsurface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t target_extents;
+
+ target_extents.x = extents->x + surface->extents.x;
+ target_extents.y = extents->y + surface->extents.y;
+ target_extents.width = extents->width;
+ target_extents.height = extents->height;
+
+ return _cairo_surface_map_to_image (surface->target, &target_extents);
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ return _cairo_surface_unmap_image (surface->target, image);
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t *target_clip;
+
+ target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+ status = _cairo_surface_offset_paint (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, target_clip);
+ _cairo_clip_destroy (target_clip);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t *target_clip;
+
+ target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+ status = _cairo_surface_offset_mask (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, mask, target_clip);
+ _cairo_clip_destroy (target_clip);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t *target_clip;
+
+ target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+ status = _cairo_surface_offset_fill (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, path, fill_rule, tolerance, antialias,
+ target_clip);
+ _cairo_clip_destroy (target_clip);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t *target_clip;
+
+ target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+ status = _cairo_surface_offset_stroke (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, path, stroke_style, ctm, ctm_inverse,
+ tolerance, antialias,
+ target_clip);
+ _cairo_clip_destroy (target_clip);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t *target_clip;
+
+ target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+ status = _cairo_surface_offset_glyphs (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source,
+ scaled_font, glyphs, num_glyphs,
+ target_clip);
+ _cairo_clip_destroy (target_clip);
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_subsurface_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ return _cairo_surface_flush (surface->target, flags);
+}
+
+static cairo_status_t
+_cairo_surface_subsurface_mark_dirty (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (surface->target->backend->mark_dirty_rectangle != NULL) {
+ cairo_rectangle_int_t rect, extents;
+
+ rect.x = x;
+ rect.y = y;
+ rect.width = width;
+ rect.height = height;
+
+ extents.x = extents.y = 0;
+ extents.width = surface->extents.width;
+ extents.height = surface->extents.height;
+
+ if (_cairo_rectangle_intersect (&rect, &extents)) {
+ status = surface->target->backend->mark_dirty_rectangle (surface->target,
+ rect.x + surface->extents.x,
+ rect.y + surface->extents.y,
+ rect.width, rect.height);
+ }
+ }
+
+ return status;
+}
+
+static cairo_bool_t
+_cairo_surface_subsurface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+
+ extents->x = 0;
+ extents->y = 0;
+ extents->width = surface->extents.width;
+ extents->height = surface->extents.height;
+
+ return TRUE;
+}
+
+static void
+_cairo_surface_subsurface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+
+ if (surface->target->backend->get_font_options != NULL)
+ surface->target->backend->get_font_options (surface->target, options);
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_surface_t *source;
+
+ source = _cairo_surface_get_source (surface->target, extents);
+ if (extents)
+ *extents = surface->extents;
+
+ return source;
+}
+
+static cairo_status_t
+_cairo_surface_subsurface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **extra_out)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *image;
+ cairo_status_t status;
+
+ image = _cairo_image_surface_create_with_content (surface->base.content,
+ surface->extents.width,
+ surface->extents.height);
+ if (unlikely (image->status)) {
+ status = image->status;
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, surface->target);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ surface->extents.x,
+ surface->extents.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (image,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ *image_out = (cairo_image_surface_t *)image;
+ *extra_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_surface_subsurface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *abstract_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_snapshot (void *abstract_surface)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *clone;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->target->unique_id));
+
+ clone = _cairo_surface_create_similar_scratch (surface->target,
+ surface->target->content,
+ surface->extents.width,
+ surface->extents.height);
+ if (unlikely (clone->status))
+ return clone;
+
+ _cairo_pattern_init_for_surface (&pattern, surface->target);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ surface->extents.x, surface->extents.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (clone,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (clone);
+ clone = _cairo_surface_create_in_error (status);
+ }
+
+ return clone;
+}
+
+static cairo_t *
+_cairo_surface_subsurface_create_context(void *target)
+{
+ cairo_surface_subsurface_t *surface = target;
+ return surface->target->backend->create_context (&surface->base);
+}
+
+static const cairo_surface_backend_t _cairo_surface_subsurface_backend = {
+ CAIRO_SURFACE_TYPE_SUBSURFACE,
+ _cairo_surface_subsurface_finish,
+
+ _cairo_surface_subsurface_create_context,
+
+ _cairo_surface_subsurface_create_similar,
+ _cairo_surface_subsurface_create_similar_image,
+ _cairo_surface_subsurface_map_to_image,
+ _cairo_surface_subsurface_unmap_image,
+
+ _cairo_surface_subsurface_source,
+ _cairo_surface_subsurface_acquire_source_image,
+ _cairo_surface_subsurface_release_source_image,
+ _cairo_surface_subsurface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_surface_subsurface_get_extents,
+ _cairo_surface_subsurface_get_font_options,
+
+ _cairo_surface_subsurface_flush,
+ _cairo_surface_subsurface_mark_dirty,
+
+ _cairo_surface_subsurface_paint,
+ _cairo_surface_subsurface_mask,
+ _cairo_surface_subsurface_stroke,
+ _cairo_surface_subsurface_fill,
+ NULL, /* fill/stroke */
+ _cairo_surface_subsurface_glyphs,
+};
+
+/**
+ * cairo_surface_create_for_rectangle:
+ * @target: an existing surface for which the sub-surface will point to
+ * @x: the x-origin of the sub-surface from the top-left of the target surface (in device-space units)
+ * @y: the y-origin of the sub-surface from the top-left of the target surface (in device-space units)
+ * @width: width of the sub-surface (in device-space units)
+ * @height: height of the sub-surface (in device-space units)
+ *
+ * Create a new surface that is a rectangle within the target surface.
+ * All operations drawn to this surface are then clipped and translated
+ * onto the target surface. Nothing drawn via this sub-surface outside of
+ * its bounds is drawn onto the target surface, making this a useful method
+ * for passing constrained child surfaces to library routines that draw
+ * directly onto the parent surface, i.e. with no further backend allocations,
+ * double buffering or copies.
+ *
+ * <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 (width < 0 || height < 0))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ if (unlikely (target->status))
+ return _cairo_surface_create_in_error (target->status);
+ if (unlikely (target->finished))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ surface = malloc (sizeof (cairo_surface_subsurface_t));
+ 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 ((surface->extents.width | surface->extents.height) < 0)
+ surface->extents.width = surface->extents.height = 0;
+
+ if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ /* Maintain subsurfaces as 1-depth */
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) target;
+ surface->extents.x += sub->extents.x;
+ surface->extents.y += sub->extents.y;
+ target = sub->target;
+ }
+
+ surface->target = cairo_surface_reference (target);
+ surface->base.type = surface->target->type;
+
+ surface->snapshot = NULL;
+
+ return &surface->base;
+}
+
+cairo_surface_t *
+_cairo_surface_create_for_rectangle_int (cairo_surface_t *target,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_subsurface_t *surface;
+
+ if (unlikely (target->status))
+ return _cairo_surface_create_in_error (target->status);
+ if (unlikely (target->finished))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ assert (target->backend->type != CAIRO_SURFACE_TYPE_SUBSURFACE);
+
+ surface = 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));
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_surface_subsurface_backend,
+ NULL, /* device */
+ target->content);
+
+ surface->extents = *extents;
+ surface->extents.x += target->device_transform.x0;
+ surface->extents.y += target->device_transform.y0;
+
+ surface->target = cairo_surface_reference (target);
+ surface->base.type = surface->target->type;
+
+ surface->snapshot = NULL;
+
+ return &surface->base;
+}
+/* XXX observe mark-dirty */
+
+static void
+_cairo_surface_subsurface_detach_snapshot (cairo_surface_t *surface)
+{
+ cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
+
+ TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, ss->target->unique_id));
+
+ cairo_surface_destroy (ss->snapshot);
+ ss->snapshot = NULL;
+}
+
+void
+_cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface,
+ cairo_surface_t *snapshot)
+{
+ cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
+
+ TRACE ((stderr, "%s: target=%d, snapshot=%d\n", __FUNCTION__,
+ ss->target->unique_id, snapshot->unique_id));
+
+ /* FIXME: attaching the subsurface as a snapshot to its target creates
+ * a reference cycle. Let's make this call as a no-op until that bug
+ * is fixed.
+ */
+ return;
+
+ if (ss->snapshot)
+ _cairo_surface_detach_snapshot (ss->snapshot);
+
+ ss->snapshot = cairo_surface_reference (snapshot);
+
+ _cairo_surface_attach_snapshot (ss->target, &ss->base,
+ _cairo_surface_subsurface_detach_snapshot);
+}
diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h
new file mode 100755
index 000000000..6529ebc11
--- /dev/null
+++ b/src/cairo-surface-wrapper-private.h
@@ -0,0 +1,193 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * 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 "cairoint.h"
+#include "cairo-types-private.h"
+#include "cairo-surface-backend-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_surface_wrapper {
+ cairo_surface_t *target;
+
+ cairo_matrix_t transform;
+
+ cairo_bool_t has_extents;
+ cairo_rectangle_int_t extents;
+ const cairo_clip_t *clip;
+
+ cairo_bool_t needs_transform;
+};
+
+cairo_private void
+_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper,
+ cairo_surface_t *target);
+
+cairo_private void
+_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper,
+ const cairo_matrix_t *transform);
+
+cairo_private void
+_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper);
+
+static inline cairo_bool_t
+_cairo_surface_wrapper_has_fill_stroke (cairo_surface_wrapper_t *wrapper)
+{
+ return wrapper->target->backend->fill_stroke != NULL;
+}
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+cairo_private void
+_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper,
+ cairo_image_surface_t *image,
+ void *image_extra);
+
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ const cairo_path_fixed_t*path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_surface_t *
+_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper,
+ cairo_content_t content,
+ int width,
+ int height);
+cairo_private cairo_bool_t
+_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
+ cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper,
+ cairo_font_options_t *options);
+
+cairo_private cairo_surface_t *
+_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper);
+
+cairo_private cairo_bool_t
+_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper);
+
+static inline cairo_bool_t
+_cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper)
+{
+ return wrapper->target != (cairo_surface_t *) 0;
+}
+
+cairo_private cairo_bool_t
+_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
+ cairo_rectangle_int_t *extents);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */
diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c
new file mode 100755
index 000000000..578e8e2be
--- /dev/null
+++ b/src/cairo-surface-wrapper.c
@@ -0,0 +1,686 @@
+/* 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-clip-inline.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-wrapper-private.h"
+
+/* A collection of routines to facilitate surface wrapping */
+
+static void
+_copy_transformed_pattern (cairo_pattern_t *pattern,
+ const cairo_pattern_t *original,
+ const cairo_matrix_t *ctm_inverse)
+{
+ _cairo_pattern_init_static_copy (pattern, original);
+
+ if (! _cairo_matrix_is_identity (ctm_inverse))
+ _cairo_pattern_transform (pattern, ctm_inverse);
+}
+
+cairo_status_t
+_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ return _cairo_surface_acquire_source_image (wrapper->target,
+ image_out, image_extra);
+}
+
+void
+_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ _cairo_surface_release_source_image (wrapper->target, image, image_extra);
+}
+
+static void
+_cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper,
+ cairo_matrix_t *m)
+{
+ cairo_matrix_init_identity (m);
+
+ if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
+ cairo_matrix_translate (m, -wrapper->extents.x, -wrapper->extents.y);
+
+ if (! _cairo_matrix_is_identity (&wrapper->transform))
+ cairo_matrix_multiply (m, &wrapper->transform, m);
+
+ if (! _cairo_matrix_is_identity (&wrapper->target->device_transform))
+ cairo_matrix_multiply (m, &wrapper->target->device_transform, m);
+}
+
+static void
+_cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper,
+ cairo_matrix_t *m)
+{
+ cairo_matrix_init_identity (m);
+
+ if (! _cairo_matrix_is_identity (&wrapper->target->device_transform_inverse))
+ cairo_matrix_multiply (m, &wrapper->target->device_transform_inverse, m);
+
+ if (! _cairo_matrix_is_identity (&wrapper->transform)) {
+ cairo_matrix_t inv;
+ cairo_status_t status;
+
+ inv = wrapper->transform;
+ status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ cairo_matrix_multiply (m, &inv, m);
+ }
+
+ if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
+ cairo_matrix_translate (m, wrapper->extents.x, wrapper->extents.y);
+}
+
+static cairo_clip_t *
+_cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper,
+ const cairo_clip_t *clip)
+{
+ cairo_clip_t *copy;
+
+ copy = _cairo_clip_copy (clip);
+ if (wrapper->has_extents) {
+ copy = _cairo_clip_intersect_rectangle (copy, &wrapper->extents);
+ }
+ copy = _cairo_clip_transform (copy, &wrapper->transform);
+ if (! _cairo_matrix_is_identity (&wrapper->target->device_transform))
+ copy = _cairo_clip_transform (copy, &wrapper->target->device_transform);
+ if (wrapper->clip)
+ copy = _cairo_clip_intersect_clip (copy, wrapper->clip);
+
+ return copy;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip;
+ cairo_pattern_union_t source_copy;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+ if (_cairo_clip_is_all_clipped (dev_clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (wrapper->needs_transform) {
+ cairo_matrix_t m;
+
+ _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+ status = cairo_matrix_invert (&m);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ }
+
+ status = _cairo_surface_paint (wrapper->target, op, source, dev_clip);
+
+ _cairo_clip_destroy (dev_clip);
+ return status;
+}
+
+
+cairo_status_t
+_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip;
+ cairo_pattern_union_t source_copy;
+ cairo_pattern_union_t mask_copy;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+ if (_cairo_clip_is_all_clipped (dev_clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (wrapper->needs_transform) {
+ cairo_matrix_t m;
+
+ _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+ status = cairo_matrix_invert (&m);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+
+ _copy_transformed_pattern (&mask_copy.base, mask, &m);
+ mask = &mask_copy.base;
+ }
+
+ status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip);
+
+ _cairo_clip_destroy (dev_clip);
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+ cairo_clip_t *dev_clip;
+ cairo_matrix_t dev_ctm = *ctm;
+ cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
+ cairo_pattern_union_t source_copy;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+ if (_cairo_clip_is_all_clipped (dev_clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (wrapper->needs_transform) {
+ cairo_matrix_t m;
+
+ _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+ status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+ if (unlikely (status))
+ goto FINISH;
+
+ _cairo_path_fixed_transform (&path_copy, &m);
+ dev_path = &path_copy;
+
+ cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+ status = cairo_matrix_invert (&m);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
+
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ }
+
+ status = _cairo_surface_stroke (wrapper->target, op, source,
+ dev_path, stroke_style,
+ &dev_ctm, &dev_ctm_inverse,
+ tolerance, antialias,
+ dev_clip);
+
+ FINISH:
+ if (dev_path != path)
+ _cairo_path_fixed_fini (dev_path);
+ _cairo_clip_destroy (dev_clip);
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ const cairo_path_fixed_t*path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path;
+ cairo_matrix_t dev_ctm = *stroke_ctm;
+ cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
+ cairo_clip_t *dev_clip;
+ cairo_pattern_union_t stroke_source_copy;
+ cairo_pattern_union_t fill_source_copy;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+ if (_cairo_clip_is_all_clipped (dev_clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (wrapper->needs_transform) {
+ cairo_matrix_t m;
+
+ _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+ status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+ if (unlikely (status))
+ goto FINISH;
+
+ _cairo_path_fixed_transform (&path_copy, &m);
+ dev_path = &path_copy;
+
+ cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+ status = cairo_matrix_invert (&m);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
+
+ _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m);
+ stroke_source = &stroke_source_copy.base;
+
+ _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m);
+ fill_source = &fill_source_copy.base;
+ }
+
+ status = _cairo_surface_fill_stroke (wrapper->target,
+ fill_op, fill_source, fill_rule,
+ fill_tolerance, fill_antialias,
+ dev_path,
+ stroke_op, stroke_source,
+ stroke_style,
+ &dev_ctm, &dev_ctm_inverse,
+ stroke_tolerance, stroke_antialias,
+ dev_clip);
+
+ FINISH:
+ if (dev_path != path)
+ _cairo_path_fixed_fini (dev_path);
+ _cairo_clip_destroy (dev_clip);
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+ cairo_pattern_union_t source_copy;
+ cairo_clip_t *dev_clip;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+ if (_cairo_clip_is_all_clipped (dev_clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (wrapper->needs_transform) {
+ cairo_matrix_t m;
+
+ _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+ status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+ if (unlikely (status))
+ goto FINISH;
+
+ _cairo_path_fixed_transform (&path_copy, &m);
+ dev_path = &path_copy;
+
+ status = cairo_matrix_invert (&m);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ }
+
+ status = _cairo_surface_fill (wrapper->target, op, source,
+ dev_path, fill_rule,
+ tolerance, antialias,
+ dev_clip);
+
+ FINISH:
+ if (dev_path != path)
+ _cairo_path_fixed_fini (dev_path);
+ _cairo_clip_destroy (dev_clip);
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t *dev_clip;
+ cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)];
+ cairo_glyph_t *dev_glyphs = stack_glyphs;
+ cairo_scaled_font_t *dev_scaled_font = scaled_font;
+ cairo_pattern_union_t source_copy;
+ cairo_font_options_t options;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+ if (_cairo_clip_is_all_clipped (dev_clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ cairo_surface_get_font_options (wrapper->target, &options);
+ cairo_font_options_merge (&options, &scaled_font->options);
+
+ if (wrapper->needs_transform) {
+ cairo_matrix_t m;
+ int i;
+
+ _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+ if (! _cairo_matrix_is_translation (&wrapper->transform)) {
+ cairo_matrix_t ctm;
+
+ /* XXX No device-transform? A bug in the tangle of layers? */
+ _cairo_matrix_multiply (&ctm,
+ &wrapper->transform,
+ &scaled_font->ctm);
+ dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face,
+ &scaled_font->font_matrix,
+ &ctm, &options);
+ }
+
+ if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
+ dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (dev_glyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FINISH;
+ }
+ }
+
+ for (i = 0; i < num_glyphs; i++) {
+ dev_glyphs[i] = glyphs[i];
+ cairo_matrix_transform_point (&m,
+ &dev_glyphs[i].x,
+ &dev_glyphs[i].y);
+ }
+
+ status = cairo_matrix_invert (&m);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ } else {
+ if (! cairo_font_options_equal (&options, &scaled_font->options)) {
+ dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face,
+ &scaled_font->font_matrix,
+ &scaled_font->ctm,
+ &options);
+ }
+
+ /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
+ * to modify the glyph array that's passed in. We must always
+ * copy the array before handing it to the backend.
+ */
+ if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
+ dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (dev_glyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FINISH;
+ }
+ }
+
+ memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+ }
+
+ status = _cairo_surface_show_text_glyphs (wrapper->target, op, source,
+ utf8, utf8_len,
+ dev_glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ dev_scaled_font,
+ dev_clip);
+ FINISH:
+ _cairo_clip_destroy (dev_clip);
+ if (dev_glyphs != stack_glyphs)
+ free (dev_glyphs);
+ if (dev_scaled_font != scaled_font)
+ cairo_scaled_font_destroy (dev_scaled_font);
+ return status;
+}
+
+cairo_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);
+ }
+}
+
+static cairo_bool_t
+_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper)
+{
+ return
+ (wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y)) ||
+ ! _cairo_matrix_is_identity (&wrapper->transform) ||
+ ! _cairo_matrix_is_identity (&wrapper->target->device_transform);
+}
+
+void
+_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper,
+ const cairo_rectangle_int_t *extents)
+{
+ if (! wrapper->has_extents) {
+ wrapper->extents = *extents;
+ wrapper->has_extents = TRUE;
+ } else
+ _cairo_rectangle_intersect (&wrapper->extents, extents);
+
+ wrapper->needs_transform =
+ _cairo_surface_wrapper_needs_device_transform (wrapper);
+}
+
+void
+_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper,
+ const cairo_matrix_t *transform)
+{
+ cairo_status_t status;
+
+ if (transform == NULL || _cairo_matrix_is_identity (transform)) {
+ cairo_matrix_init_identity (&wrapper->transform);
+
+ wrapper->needs_transform =
+ _cairo_surface_wrapper_needs_device_transform (wrapper);
+ } else {
+ wrapper->transform = *transform;
+ status = cairo_matrix_invert (&wrapper->transform);
+ /* should always be invertible unless given pathological input */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ wrapper->needs_transform = TRUE;
+ }
+}
+
+void
+_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper,
+ const cairo_clip_t *clip)
+{
+ wrapper->clip = clip;
+}
+
+void
+_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper,
+ cairo_font_options_t *options)
+{
+ cairo_surface_get_font_options (wrapper->target, options);
+}
+
+cairo_surface_t *
+_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper)
+{
+ if (wrapper->target->backend->snapshot)
+ return wrapper->target->backend->snapshot (wrapper->target);
+
+ return NULL;
+}
+
+cairo_bool_t
+_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper)
+{
+ return cairo_surface_has_show_text_glyphs (wrapper->target);
+}
+
+void
+_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper,
+ cairo_surface_t *target)
+{
+ wrapper->target = cairo_surface_reference (target);
+ cairo_matrix_init_identity (&wrapper->transform);
+ wrapper->has_extents = FALSE;
+ wrapper->extents.x = wrapper->extents.y = 0;
+ wrapper->clip = NULL;
+
+ wrapper->needs_transform = FALSE;
+ if (target) {
+ wrapper->needs_transform =
+ ! _cairo_matrix_is_identity (&target->device_transform);
+ }
+}
+
+void
+_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper)
+{
+ cairo_surface_destroy (wrapper->target);
+}
+
+cairo_bool_t
+_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_rectangle_int_t clip;
+ cairo_bool_t has_clip;
+
+ has_clip = _cairo_surface_get_extents (wrapper->target, &clip);
+ if (wrapper->clip) {
+ if (has_clip) {
+ if (! _cairo_rectangle_intersect (&clip,
+ _cairo_clip_get_extents (wrapper->clip)))
+ return FALSE;
+ } else {
+ has_clip = TRUE;
+ clip = *_cairo_clip_get_extents (wrapper->clip);
+ }
+ }
+
+ if (has_clip && wrapper->needs_transform) {
+ cairo_matrix_t m;
+ double x1, y1, x2, y2;
+
+ _cairo_surface_wrapper_get_inverse_transform (wrapper, &m);
+
+ x1 = clip.x;
+ y1 = clip.y;
+ x2 = clip.x + clip.width;
+ y2 = clip.y + clip.height;
+
+ _cairo_matrix_transform_bounding_box (&m, &x1, &y1, &x2, &y2, NULL);
+
+ clip.x = floor (x1);
+ clip.y = floor (y1);
+ clip.width = ceil (x2) - clip.x;
+ clip.height = ceil (y2) - clip.y;
+ }
+
+ if (has_clip) {
+ if (wrapper->has_extents) {
+ *extents = wrapper->extents;
+ return _cairo_rectangle_intersect (extents, &clip);
+ } else {
+ *extents = clip;
+ return TRUE;
+ }
+ } else if (wrapper->has_extents) {
+ *extents = wrapper->extents;
+ return TRUE;
+ } else {
+ _cairo_unbounded_rectangle_init (extents);
+ return TRUE;
+ }
+}
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
new file mode 100755
index 000000000..5c6969c8a
--- /dev/null
+++ b/src/cairo-surface.c
@@ -0,0 +1,2644 @@
+/* -*- 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-array-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-damage-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-inline.h"
+#include "cairo-tee-surface-private.h"
+
+/**
+ * SECTION:cairo-surface
+ * @Title: cairo_surface_t
+ * @Short_Description: Base class for surfaces
+ * @See_Also: #cairo_t, #cairo_pattern_t
+ *
+ * #cairo_surface_t is the abstract type representing all different drawing
+ * targets that cairo can render to. The actual drawings are
+ * performed using a cairo <firstterm>context</firstterm>.
+ *
+ * A cairo surface is created by using <firstterm>backend</firstterm>-specific
+ * constructors, typically of the form
+ * <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>.
+ *
+ * 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 */ \
+ 0, /* serial */ \
+ NULL, /* damage */ \
+ FALSE, /* _finishing */ \
+ FALSE, /* finished */ \
+ TRUE, /* is_clear */ \
+ FALSE, /* has_font_options */ \
+ FALSE, /* owns_device */ \
+ { 0, 0, 0, NULL, }, /* user_data */ \
+ { 0, 0, 0, NULL, }, /* mime_data */ \
+ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \
+ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \
+ { NULL, NULL }, /* device_transform_observers */ \
+ 0.0, /* x_resolution */ \
+ 0.0, /* y_resolution */ \
+ 0.0, /* x_fallback_resolution */ \
+ 0.0, /* y_fallback_resolution */ \
+ NULL, /* snapshot_of */ \
+ NULL, /* snapshot_detach */ \
+ { NULL, NULL }, /* snapshots */ \
+ { NULL, NULL }, /* snapshot */ \
+ { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \
+ CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \
+ CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \
+ CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \
+ CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \
+ CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \
+ } /* font_options */ \
+}
+
+/* XXX error object! */
+
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_FILE_NOT_FOUND, _cairo_surface_nil_file_not_found);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_file_error);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error);
+
+static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_UNSUPPORTED, _cairo_surface_nil_unsupported);
+static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_NOTHING_TO_DO, _cairo_surface_nil_nothing_to_do);
+
+static void _cairo_surface_finish_snapshots (cairo_surface_t *surface);
+static void _cairo_surface_finish (cairo_surface_t *surface);
+
+/**
+ * _cairo_surface_set_error:
+ * @surface: a surface
+ * @status: a status value indicating an error
+ *
+ * Atomically sets surface->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal
+ * status values.
+ *
+ * All assignments of an error status to surface->status should happen
+ * through _cairo_surface_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the
+ * nil objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ *
+ * Return value: the error status.
+ **/
+cairo_int_status_t
+_cairo_surface_set_error (cairo_surface_t *surface,
+ cairo_int_status_t status)
+{
+ /* NOTHING_TO_DO is magic. We use it to break out of the inner-most
+ * surface function, but anything higher just sees "success".
+ */
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ status = CAIRO_INT_STATUS_SUCCESS;
+
+ if (status == CAIRO_INT_STATUS_SUCCESS ||
+ status >= (int)CAIRO_INT_STATUS_LAST_STATUS)
+ return status;
+
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&surface->status, (cairo_status_t)status);
+
+ return _cairo_error (status);
+}
+
+/**
+ * cairo_surface_get_type:
+ * @surface: a #cairo_surface_t
+ *
+ * This function returns the type of the backend used to create
+ * a surface. See #cairo_surface_type_t for available types.
+ *
+ * Return value: The type of @surface.
+ *
+ * Since: 1.2
+ **/
+cairo_surface_type_t
+cairo_surface_get_type (cairo_surface_t *surface)
+{
+ /* We don't use surface->backend->type here so that some of the
+ * special "wrapper" surfaces such as cairo_paginated_surface_t
+ * can override surface->type with the type of the "child"
+ * surface. */
+ return surface->type;
+}
+
+/**
+ * cairo_surface_get_content:
+ * @surface: a #cairo_surface_t
+ *
+ * This function returns the content type of @surface which indicates
+ * whether the surface contains color and/or alpha information. See
+ * #cairo_content_t.
+ *
+ * Return value: The content type of @surface.
+ *
+ * Since: 1.2
+ **/
+cairo_content_t
+cairo_surface_get_content (cairo_surface_t *surface)
+{
+ return surface->content;
+}
+
+/**
+ * cairo_surface_status:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks whether an error has previously occurred for this
+ * surface.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER,
+ * %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR,
+ * %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or
+ * %CAIRO_STATUS_INVALID_VISUAL.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_surface_status (cairo_surface_t *surface)
+{
+ return surface->status;
+}
+slim_hidden_def (cairo_surface_status);
+
+static unsigned int
+_cairo_surface_allocate_unique_id (void)
+{
+ static cairo_atomic_int_t unique_id;
+
+#if CAIRO_NO_MUTEX
+ if (++unique_id == 0)
+ unique_id = 1;
+ return unique_id;
+#else
+ cairo_atomic_int_t old, id;
+
+ do {
+ old = _cairo_atomic_uint_get (&unique_id);
+ id = old + 1;
+ if (id == 0)
+ id = 1;
+ } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id));
+
+ return id;
+#endif
+}
+
+/**
+ * cairo_surface_get_device:
+ * @surface: a #cairo_surface_t
+ *
+ * This function returns the device for a @surface.
+ * See #cairo_device_t.
+ *
+ * Return value: The device for @surface or %NULL if the surface does
+ * not have an associated device.
+ *
+ * Since: 1.10
+ **/
+cairo_device_t *
+cairo_surface_get_device (cairo_surface_t *surface)
+{
+ if (unlikely (surface->status))
+ return _cairo_device_create_in_error (surface->status);
+
+ return surface->device;
+}
+
+static cairo_bool_t
+_cairo_surface_has_snapshots (cairo_surface_t *surface)
+{
+ return ! cairo_list_is_empty (&surface->snapshots);
+}
+
+static cairo_bool_t
+_cairo_surface_has_mime_data (cairo_surface_t *surface)
+{
+ return surface->mime_data.num_elements != 0;
+}
+
+static void
+_cairo_surface_detach_mime_data (cairo_surface_t *surface)
+{
+ if (! _cairo_surface_has_mime_data (surface))
+ return;
+
+ _cairo_user_data_array_fini (&surface->mime_data);
+ _cairo_user_data_array_init (&surface->mime_data);
+}
+
+static void
+_cairo_surface_detach_snapshots (cairo_surface_t *surface)
+{
+ while (_cairo_surface_has_snapshots (surface)) {
+ _cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots,
+ cairo_surface_t,
+ snapshot));
+ }
+}
+
+void
+_cairo_surface_detach_snapshot (cairo_surface_t *snapshot)
+{
+ assert (snapshot->snapshot_of != NULL);
+
+ snapshot->snapshot_of = NULL;
+ cairo_list_del (&snapshot->snapshot);
+
+ if (snapshot->snapshot_detach != NULL)
+ snapshot->snapshot_detach (snapshot);
+
+ cairo_surface_destroy (snapshot);
+}
+
+void
+_cairo_surface_attach_snapshot (cairo_surface_t *surface,
+ cairo_surface_t *snapshot,
+ cairo_surface_func_t detach_func)
+{
+ assert (surface != snapshot);
+ assert (snapshot->snapshot_of != surface);
+
+ cairo_surface_reference (snapshot);
+
+ if (snapshot->snapshot_of != NULL)
+ _cairo_surface_detach_snapshot (snapshot);
+
+ snapshot->snapshot_of = surface;
+ snapshot->snapshot_detach = detach_func;
+
+ cairo_list_add (&snapshot->snapshot, &surface->snapshots);
+
+ assert (_cairo_surface_has_snapshot (surface, snapshot->backend) == snapshot);
+}
+
+cairo_surface_t *
+_cairo_surface_has_snapshot (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend)
+{
+ cairo_surface_t *snapshot;
+
+ cairo_list_foreach_entry (snapshot, cairo_surface_t,
+ &surface->snapshots, snapshot)
+ {
+ if (snapshot->backend == backend)
+ return snapshot;
+ }
+
+ return NULL;
+}
+
+cairo_status_t
+_cairo_surface_begin_modification (cairo_surface_t *surface)
+{
+ assert (surface->status == CAIRO_STATUS_SUCCESS);
+ assert (! surface->finished);
+
+ return _cairo_surface_flush (surface, 1);
+}
+
+void
+_cairo_surface_init (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend,
+ cairo_device_t *device,
+ cairo_content_t content)
+{
+ CAIRO_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->_finishing = FALSE;
+ surface->is_clear = FALSE;
+ surface->serial = 0;
+ surface->damage = NULL;
+ surface->owns_device = (device != NULL);
+
+ _cairo_user_data_array_init (&surface->user_data);
+ _cairo_user_data_array_init (&surface->mime_data);
+
+ cairo_matrix_init_identity (&surface->device_transform);
+ cairo_matrix_init_identity (&surface->device_transform_inverse);
+ cairo_list_init (&surface->device_transform_observers);
+
+ surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
+ surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
+
+ surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
+ surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
+
+ cairo_list_init (&surface->snapshots);
+ surface->snapshot_of = NULL;
+
+ surface->has_font_options = FALSE;
+}
+
+static void
+_cairo_surface_copy_similar_properties (cairo_surface_t *surface,
+ cairo_surface_t *other)
+{
+ if (other->has_font_options || other->backend != surface->backend) {
+ cairo_font_options_t options;
+
+ cairo_surface_get_font_options (other, &options);
+ _cairo_surface_set_font_options (surface, &options);
+ }
+
+ 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);
+
+ surface = NULL;
+ if (other->backend->create_similar)
+ surface = other->backend->create_similar (other, content, width, height);
+ if (surface == NULL)
+ surface = cairo_surface_create_similar_image (other,
+ _cairo_format_from_content (content),
+ width, height);
+
+ if (unlikely (surface->status))
+ return surface;
+
+ _cairo_surface_copy_similar_properties (surface, other);
+
+ 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.)
+ *
+ * Use cairo_surface_create_similar_image() if you need an image surface
+ * which can be painted quickly to the target surface.
+ *
+ * Return value: a pointer to the newly allocated surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_surface_t *surface;
+
+ if (unlikely (other->status))
+ return _cairo_surface_create_in_error (other->status);
+ if (unlikely (other->finished))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+ if (unlikely (width < 0 || height < 0))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+ if (unlikely (! CAIRO_CONTENT_VALID (content)))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
+
+ surface = _cairo_surface_create_similar_solid (other,
+ content, width, height,
+ CAIRO_COLOR_TRANSPARENT);
+ assert (surface->is_clear);
+
+ return surface;
+}
+
+/**
+ * cairo_surface_create_similar_image:
+ * @other: an existing surface used to select the preference of the new surface
+ * @format: the format for the new surface
+ * @width: width of the new surface, (in device-space units)
+ * @height: height of the new surface (in device-space units)
+ *
+ * Create a new image surface that is as compatible as possible for uploading
+ * to and the use in conjunction with an existing surface. However, this surface
+ * can still be used like any normal image surface.
+ *
+ * Initially the surface contents are all 0 (transparent if contents
+ * have transparency, black otherwise.)
+ *
+ * Use cairo_surface_create_similar() if you don't need an image surface.
+ *
+ * Return value: a pointer to the newly allocated image surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_surface_create_similar_image (cairo_surface_t *other,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_surface_t *image;
+
+ if (unlikely (other->status))
+ return _cairo_surface_create_in_error (other->status);
+ if (unlikely (other->finished))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ if (unlikely (width < 0 || height < 0))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+ if (unlikely (! CAIRO_FORMAT_VALID (format)))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT);
+
+ image = NULL;
+ if (other->backend->create_similar_image)
+ image = other->backend->create_similar_image (other,
+ format, width, height);
+ if (image == NULL)
+ image = cairo_image_surface_create (format, width, height);
+
+ assert (image->is_clear);
+
+ return image;
+}
+slim_hidden_def (cairo_surface_create_similar_image);
+
+/**
+ * _cairo_surface_map_to_image:
+ * @surface: an existing surface used to extract the image from
+ * @extents: limit the extraction to an rectangular region
+ *
+ * Returns an image surface that is the most efficient mechanism for
+ * modifying the backing store of the target surface. The region
+ * retrieved is limited to @extents.
+ *
+ * Note, the use of the original surface as a target or source whilst
+ * it is mapped is undefined. The result of mapping the surface
+ * multiple times is undefined. Calling cairo_surface_destroy() or
+ * cairo_surface_finish() on the resulting image surface results in
+ * undefined behavior. Changing the device transform of the image
+ * surface or of @surface before the image surface is unmapped results
+ * in undefined behavior.
+ *
+ * Assumes that @surface is valid (CAIRO_STATUS_SUCCESS,
+ * non-finished).
+ *
+ * Return value: a pointer to the newly allocated image surface. The
+ * caller must use _cairo_surface_unmap_image() to destroy this image
+ * surface.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * The returned image might have a %CAIRO_FORMAT_INVALID format.
+ **/
+cairo_image_surface_t *
+_cairo_surface_map_to_image (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_image_surface_t *image = NULL;
+
+ assert (extents != NULL);
+
+ /* TODO: require map_to_image != NULL */
+ if (surface->backend->map_to_image)
+ image = surface->backend->map_to_image (surface, extents);
+
+ if (image == NULL)
+ image = _cairo_image_surface_clone_subimage (surface, extents);
+
+ return image;
+}
+
+/**
+ * _cairo_surface_unmap_image:
+ * @surface: the surface passed to _cairo_surface_map_to_image().
+ * @image: the currently mapped image
+ *
+ * Unmaps the image surface as returned from
+ * _cairo_surface_map_to_image().
+ *
+ * The content of the image will be uploaded to the target surface.
+ * Afterwards, the image is destroyed.
+ *
+ * Using an image surface which wasn't returned by
+ * _cairo_surface_map_to_image() results in undefined behavior.
+ *
+ * An image surface in error status can be passed to
+ * _cairo_surface_unmap_image().
+ *
+ * Return value: the unmap status.
+ *
+ * Even if the unmap status is not successful, @image is destroyed.
+ **/
+cairo_int_status_t
+_cairo_surface_unmap_image (cairo_surface_t *surface,
+ cairo_image_surface_t *image)
+{
+ cairo_surface_pattern_t pattern;
+ cairo_rectangle_int_t extents;
+ cairo_clip_t *clip;
+ cairo_int_status_t status;
+
+ /* map_to_image can return error surfaces */
+ if (unlikely (image->base.status)) {
+ status = image->base.status;
+ goto destroy;
+ }
+
+ /* If the image is untouched just skip the update */
+ if (image->base.serial == 0) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto destroy;
+ }
+
+ /* TODO: require unmap_image != NULL */
+ if (surface->backend->unmap_image &&
+ ! _cairo_image_surface_is_clone (image))
+ {
+ status = surface->backend->unmap_image (surface, image);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, &image->base);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+ /* We have to apply the translate from map_to_image's extents.x and .y */
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ image->base.device_transform.x0,
+ image->base.device_transform.y0);
+
+ /* And we also have to clip the operation to the image's extents */
+ extents.x = image->base.device_transform_inverse.x0;
+ extents.y = image->base.device_transform_inverse.y0;
+ extents.width = image->width;
+ extents.height = image->height;
+ clip = _cairo_clip_intersect_rectangle (NULL, &extents);
+
+ status = _cairo_surface_paint (surface,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base,
+ clip);
+
+ _cairo_pattern_fini (&pattern.base);
+ _cairo_clip_destroy (clip);
+
+destroy:
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return status;
+}
+
+/**
+ * cairo_surface_map_to_image:
+ * @surface: an existing surface used to extract the image from
+ * @extents: limit the extraction to an rectangular region
+ *
+ * Returns an image surface that is the most efficient mechanism for
+ * modifying the backing store of the target surface. The region retrieved
+ * may be limited to the @extents or %NULL for the whole surface
+ *
+ * Note, the use of the original surface as a target or source whilst
+ * it is mapped is undefined. The result of mapping the surface
+ * multiple times is undefined. Calling cairo_surface_destroy() or
+ * cairo_surface_finish() on the resulting image surface results in
+ * undefined behavior. Changing the device transform of the image
+ * surface or of @surface before the image surface is unmapped results
+ * in undefined behavior.
+ *
+ * Return value: a pointer to the newly allocated image surface. The caller
+ * must use cairo_surface_unmap_image() to destroy this image surface.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs. If the returned pointer does not have an
+ * error status, it is guaranteed to be an image surface whose format
+ * is not %CAIRO_FORMAT_INVALID.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_surface_map_to_image (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_rectangle_int_t rect;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return _cairo_surface_create_in_error (surface->status);
+ if (unlikely (surface->finished))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ if (extents == NULL) {
+ if (unlikely (! surface->backend->get_extents (surface, &rect)))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+ extents = &rect;
+ } else {
+ cairo_rectangle_int_t surface_extents;
+
+ /* If this surface is bounded, we can't map parts
+ * that are outside of it. */
+ if (likely (surface->backend->get_extents (surface, &surface_extents))) {
+ if (unlikely (! _cairo_rectangle_contains_rectangle (&surface_extents, extents)))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+ }
+ }
+
+ image = _cairo_surface_map_to_image (surface, extents);
+
+ status = image->base.status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ if (image->format == CAIRO_FORMAT_INVALID) {
+ cairo_surface_destroy (&image->base);
+ image = _cairo_image_surface_clone_subimage (surface, extents);
+ }
+
+ return &image->base;
+}
+
+/**
+ * cairo_surface_unmap_image:
+ * @surface: the surface passed to cairo_surface_map_to_image().
+ * @image: the currently mapped image
+ *
+ * Unmaps the image surface as returned from #cairo_surface_map_to_image().
+ *
+ * The content of the image will be uploaded to the target surface.
+ * Afterwards, the image is destroyed.
+ *
+ * Using an image surface which wasn't returned by cairo_surface_map_to_image()
+ * results in undefined behavior.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_surface_unmap_image (cairo_surface_t *surface,
+ cairo_surface_t *image)
+{
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (unlikely (surface->status)) {
+ status = surface->status;
+ goto error;
+ }
+ if (unlikely (surface->finished)) {
+ status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+ goto error;
+ }
+ if (unlikely (image->status)) {
+ status = image->status;
+ goto error;
+ }
+ if (unlikely (image->finished)) {
+ status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+ goto error;
+ }
+ if (unlikely (! _cairo_surface_is_image (image))) {
+ status = _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ goto error;
+ }
+
+ status = _cairo_surface_unmap_image (surface,
+ (cairo_image_surface_t *) image);
+ if (unlikely (status))
+ _cairo_surface_set_error (surface, status);
+
+ return;
+
+error:
+ _cairo_surface_set_error (surface, status);
+ cairo_surface_finish (image);
+ cairo_surface_destroy (image);
+}
+
+cairo_surface_t *
+_cairo_surface_create_similar_solid (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height,
+ const cairo_color_t *color)
+{
+ cairo_status_t status;
+ cairo_surface_t *surface;
+ cairo_solid_pattern_t pattern;
+
+ surface = _cairo_surface_create_similar_scratch (other, content,
+ width, height);
+ if (unlikely (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_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.
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_surface_reference (cairo_surface_t *surface)
+{
+ if (surface == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return surface;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
+
+ _cairo_reference_count_inc (&surface->ref_count);
+
+ return surface;
+}
+slim_hidden_def (cairo_surface_reference);
+
+/**
+ * cairo_surface_destroy:
+ * @surface: a #cairo_surface_t
+ *
+ * Decreases the reference count on @surface by one. If the result is
+ * zero, then @surface and all associated resources are freed. See
+ * cairo_surface_reference().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_surface_destroy (cairo_surface_t *surface)
+{
+ if (surface == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&surface->ref_count))
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (! surface->finished) {
+ _cairo_surface_finish_snapshots (surface);
+ /* We may have been referenced by a snapshot prior to have
+ * detaching it with the copy-on-write.
+ */
+ if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count))
+ return;
+
+ _cairo_surface_finish (surface);
+ }
+
+ if (surface->damage)
+ _cairo_damage_destroy (surface->damage);
+
+ _cairo_user_data_array_fini (&surface->user_data);
+ _cairo_user_data_array_fini (&surface->mime_data);
+
+ if (surface->owns_device)
+ cairo_device_destroy (surface->device);
+
+ assert (surface->snapshot_of == NULL);
+ assert (! _cairo_surface_has_snapshots (surface));
+ /* paranoid check that nobody took a reference whilst finishing */
+ assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
+
+ free (surface);
+}
+slim_hidden_def(cairo_surface_destroy);
+
+/**
+ * cairo_surface_get_reference_count:
+ * @surface: a #cairo_surface_t
+ *
+ * Returns the current reference count of @surface.
+ *
+ * Return value: the current reference count of @surface. If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_surface_get_reference_count (cairo_surface_t *surface)
+{
+ if (surface == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return 0;
+
+ return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count);
+}
+
+static void
+_cairo_surface_finish_snapshots (cairo_surface_t *surface)
+{
+ cairo_status_t status;
+
+ /* update the snapshots *before* we declare the surface as finished */
+ surface->_finishing = TRUE;
+ status = _cairo_surface_flush (surface, 0);
+ (void) status;
+}
+
+static void
+_cairo_surface_finish (cairo_surface_t *surface)
+{
+ cairo_status_t status;
+
+ surface->finished = TRUE;
+
+ /* call finish even if in error mode */
+ if (surface->backend->finish) {
+ status = surface->backend->finish (surface);
+ if (unlikely (status))
+ _cairo_surface_set_error (surface, status);
+ }
+
+ assert (surface->snapshot_of == NULL);
+ assert (!_cairo_surface_has_snapshots (surface));
+}
+
+/**
+ * cairo_surface_finish:
+ * @surface: the #cairo_surface_t to finish
+ *
+ * This function finishes the surface and drops all references to
+ * external resources. For example, for the Xlib backend it means
+ * that cairo will no longer access the drawable, which can be freed.
+ * After calling cairo_surface_finish() the only valid operations on a
+ * surface are getting and setting user, referencing and
+ * destroying, and flushing and finishing it.
+ * Further drawing to the surface will not affect the
+ * surface but will instead trigger a %CAIRO_STATUS_SURFACE_FINISHED
+ * error.
+ *
+ * When the last call to cairo_surface_destroy() decreases the
+ * reference count to zero, cairo will call cairo_surface_finish() if
+ * it hasn't been called already, before freeing the resources
+ * associated with the surface.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_surface_finish (cairo_surface_t *surface)
+{
+ if (surface == NULL)
+ return;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->finished)
+ return;
+
+ /* We have to be careful when decoupling potential reference cycles */
+ cairo_surface_reference (surface);
+
+ _cairo_surface_finish_snapshots (surface);
+ /* XXX need to block and wait for snapshot references */
+ _cairo_surface_finish (surface);
+
+ cairo_surface_destroy (surface);
+}
+slim_hidden_def (cairo_surface_finish);
+
+/**
+ * _cairo_surface_release_device_reference:
+ * @surface: a #cairo_surface_t
+ *
+ * This function makes @surface release the reference to its device. The
+ * function is intended to be used for avoiding cycling references for
+ * surfaces that are owned by their device, for example cache surfaces.
+ * Note that the @surface will still assume that the device is available.
+ * So it is the caller's responsibility to ensure the device stays around
+ * until the @surface is destroyed. Just calling cairo_surface_finish() is
+ * not enough.
+ **/
+void
+_cairo_surface_release_device_reference (cairo_surface_t *surface)
+{
+ assert (surface->owns_device);
+
+ cairo_device_destroy (surface->device);
+ surface->owns_device = FALSE;
+}
+
+/**
+ * cairo_surface_get_user_data:
+ * @surface: a #cairo_surface_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @surface using the specified
+ * key. If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.0
+ **/
+void *
+cairo_surface_get_user_data (cairo_surface_t *surface,
+ const cairo_user_data_key_t *key)
+{
+ /* Prevent reads of the array during teardown */
+ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
+ return NULL;
+
+ return _cairo_user_data_array_get_data (&surface->user_data, key);
+}
+
+/**
+ * cairo_surface_set_user_data:
+ * @surface: a #cairo_surface_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the surface
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * surface is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @surface. To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_surface_set_user_data (cairo_surface_t *surface,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy)
+{
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return surface->status;
+
+ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ return _cairo_user_data_array_set_data (&surface->user_data,
+ key, user_data, destroy);
+}
+
+/**
+ * cairo_surface_get_mime_data:
+ * @surface: a #cairo_surface_t
+ * @mime_type: the mime type of the image data
+ * @data: the image data to attached to the surface
+ * @length: the length of the image data
+ *
+ * Return mime data previously attached to @surface using the
+ * specified mime type. If no data has been attached with the given
+ * mime type, @data is set %NULL.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_surface_get_mime_data (cairo_surface_t *surface,
+ const char *mime_type,
+ const unsigned char **data,
+ unsigned long *length)
+{
+ cairo_user_data_slot_t *slots;
+ int i, num_slots;
+
+ *data = NULL;
+ *length = 0;
+
+ /* Prevent reads of the array during teardown */
+ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
+ return;
+
+ /* The number of mime-types attached to a surface is usually small,
+ * typically zero. Therefore it is quicker to do a strcmp() against
+ * each key than it is to intern the string (i.e. compute a hash,
+ * search the hash table, and do a final strcmp).
+ */
+ num_slots = surface->mime_data.num_elements;
+ slots = _cairo_array_index (&surface->mime_data, 0);
+ for (i = 0; i < num_slots; i++) {
+ if (slots[i].key != NULL && strcmp ((char *) slots[i].key, mime_type) == 0) {
+ cairo_mime_data_t *mime_data = slots[i].user_data;
+
+ *data = mime_data->data;
+ *length = mime_data->length;
+ return;
+ }
+ }
+}
+slim_hidden_def (cairo_surface_get_mime_data);
+
+static void
+_cairo_mime_data_destroy (void *ptr)
+{
+ cairo_mime_data_t *mime_data = ptr;
+
+ if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count))
+ return;
+
+ if (mime_data->destroy && mime_data->closure)
+ mime_data->destroy (mime_data->closure);
+
+ free (mime_data);
+}
+
+/**
+ * CAIRO_MIME_TYPE_JP2:
+ *
+ * The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1).
+ *
+ * Since: 1.10
+ **/
+
+/**
+ * CAIRO_MIME_TYPE_JPEG:
+ *
+ * The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1).
+ *
+ * Since: 1.10
+ **/
+
+/**
+ * CAIRO_MIME_TYPE_PNG:
+ *
+ * The Portable Network Graphics image file format (ISO/IEC 15948).
+ *
+ * Since: 1.10
+ **/
+
+/**
+ * CAIRO_MIME_TYPE_URI:
+ *
+ * URI for an image file (unofficial MIME type).
+ *
+ * Since: 1.10
+ **/
+
+/**
+ * CAIRO_MIME_TYPE_UNIQUE_ID:
+ *
+ * Unique identifier for a surface (cairo specific MIME type).
+ *
+ * Since: 1.12
+ **/
+
+/**
+ * cairo_surface_set_mime_data:
+ * @surface: a #cairo_surface_t
+ * @mime_type: the MIME type of the image data
+ * @data: the image data to attach to the surface
+ * @length: the length of the image data
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * surface is destroyed or when new image data is attached using the
+ * same mime type.
+ * @closure: the data to be passed to the @destroy notifier
+ *
+ * Attach an image in the format @mime_type to @surface. To remove
+ * the data from a surface, call this function with same mime type
+ * and %NULL for @data.
+ *
+ * The attached image (or filename) data can later be used by backends
+ * which support it (currently: PDF, PS, SVG and Win32 Printing
+ * surfaces) to emit this data instead of making a snapshot of the
+ * @surface. This approach tends to be faster and requires less
+ * memory and disk space.
+ *
+ * The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG,
+ * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI,
+ * %CAIRO_MIME_TYPE_UNIQUE_ID.
+ *
+ * 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.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_surface_set_mime_data (cairo_surface_t *surface,
+ const char *mime_type,
+ const unsigned char *data,
+ unsigned long length,
+ cairo_destroy_func_t destroy,
+ void *closure)
+{
+ cairo_status_t status;
+ cairo_mime_data_t *mime_data;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return surface->status;
+
+ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ if (unlikely (surface->status))
+ return surface->status;
+ if (unlikely (surface->finished))
+ return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ status = _cairo_intern_string (&mime_type, -1);
+ if (unlikely (status))
+ return _cairo_surface_set_error (surface, status);
+
+ if (data != NULL) {
+ mime_data = malloc (sizeof (cairo_mime_data_t));
+ if (unlikely (mime_data == NULL))
+ return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1);
+
+ mime_data->data = (unsigned char *) data;
+ mime_data->length = length;
+ mime_data->destroy = destroy;
+ mime_data->closure = closure;
+ } else
+ mime_data = NULL;
+
+ status = _cairo_user_data_array_set_data (&surface->mime_data,
+ (cairo_user_data_key_t *) mime_type,
+ mime_data,
+ _cairo_mime_data_destroy);
+ if (unlikely (status)) {
+ free (mime_data);
+
+ return _cairo_surface_set_error (surface, status);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_surface_set_mime_data);
+
+/**
+ * cairo_surface_supports_mime_type:
+ * @surface: a #cairo_surface_t
+ * @mime_type: the mime type
+ *
+ * Return whether @surface supports @mime_type.
+ *
+ * Return value: %TRUE if @surface supports
+ * @mime_type, %FALSE otherwise
+ *
+ * Since: 1.12
+ **/
+cairo_bool_t
+cairo_surface_supports_mime_type (cairo_surface_t *surface,
+ const char *mime_type)
+{
+ const char **types;
+
+ if (surface->backend->get_supported_mime_types) {
+ types = surface->backend->get_supported_mime_types (surface);
+ if (types) {
+ while (*types) {
+ if (strcmp (*types, mime_type) == 0)
+ return TRUE;
+ types++;
+ }
+ }
+ }
+
+ return FALSE;
+}
+slim_hidden_def (cairo_surface_supports_mime_type);
+
+static void
+_cairo_mime_data_reference (const void *key, void *elt, void *closure)
+{
+ cairo_mime_data_t *mime_data = elt;
+
+ _cairo_reference_count_inc (&mime_data->ref_count);
+}
+
+cairo_status_t
+_cairo_surface_copy_mime_data (cairo_surface_t *dst,
+ cairo_surface_t *src)
+{
+ cairo_status_t status;
+
+ if (dst->status)
+ return dst->status;
+
+ if (src->status)
+ return _cairo_surface_set_error (dst, src->status);
+
+ /* first copy the mime-data, discarding any already set on dst */
+ status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data);
+ if (unlikely (status))
+ return _cairo_surface_set_error (dst, status);
+
+ /* now increment the reference counters for the copies */
+ _cairo_user_data_array_foreach (&dst->mime_data,
+ _cairo_mime_data_reference,
+ NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_surface_set_font_options:
+ * @surface: a #cairo_surface_t
+ * @options: a #cairo_font_options_t object that contains the
+ * options to use for this surface instead of backend's default
+ * font options.
+ *
+ * Sets the default font rendering options for the surface.
+ * This is useful to correctly propagate default font options when
+ * falling back to an image surface in a backend implementation.
+ * This affects the options returned in cairo_surface_get_font_options().
+ *
+ * If @options is %NULL the surface options are reset to those of
+ * the backend default.
+ **/
+void
+_cairo_surface_set_font_options (cairo_surface_t *surface,
+ cairo_font_options_t *options)
+{
+ if (surface->status)
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (surface->finished) {
+ _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (options) {
+ surface->has_font_options = TRUE;
+ _cairo_font_options_init_copy (&surface->font_options, options);
+ } else {
+ surface->has_font_options = FALSE;
+ }
+}
+
+/**
+ * cairo_surface_get_font_options:
+ * @surface: a #cairo_surface_t
+ * @options: a #cairo_font_options_t object into which to store
+ * the retrieved options. All existing values are overwritten
+ *
+ * Retrieves the default font rendering options for the surface.
+ * This allows display surfaces to report the correct subpixel order
+ * for rendering on them, print surfaces to disable hinting of
+ * metrics and so forth. The result can then be used with
+ * cairo_scaled_font_create().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_surface_get_font_options (cairo_surface_t *surface,
+ cairo_font_options_t *options)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ if (surface->status) {
+ _cairo_font_options_init_default (options);
+ return;
+ }
+
+ if (! surface->has_font_options) {
+ surface->has_font_options = TRUE;
+
+ _cairo_font_options_init_default (&surface->font_options);
+
+ if (!surface->finished && surface->backend->get_font_options) {
+ surface->backend->get_font_options (surface, &surface->font_options);
+ }
+ }
+
+ _cairo_font_options_init_copy (options, &surface->font_options);
+}
+slim_hidden_def (cairo_surface_get_font_options);
+
+cairo_status_t
+_cairo_surface_flush (cairo_surface_t *surface, unsigned flags)
+{
+ /* update the current snapshots *before* the user updates the surface */
+ _cairo_surface_detach_snapshots (surface);
+ if (surface->snapshot_of != NULL)
+ _cairo_surface_detach_snapshot (surface);
+ _cairo_surface_detach_mime_data (surface);
+
+ return __cairo_surface_flush (surface, flags);
+}
+
+/**
+ * cairo_surface_flush:
+ * @surface: a #cairo_surface_t
+ *
+ * Do any pending drawing for the surface and also restore any
+ * temporary modifications cairo has made to the surface's
+ * state. This function must be called before switching from
+ * drawing on the surface with cairo to drawing on it directly
+ * with native APIs. If the surface doesn't support direct access,
+ * then this function does nothing.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_surface_flush (cairo_surface_t *surface)
+{
+ cairo_status_t status;
+
+ if (surface->status)
+ return;
+
+ if (surface->finished)
+ return;
+
+ status = _cairo_surface_flush (surface, 0);
+ if (unlikely (status))
+ _cairo_surface_set_error (surface, status);
+}
+slim_hidden_def (cairo_surface_flush);
+
+/**
+ * cairo_surface_mark_dirty:
+ * @surface: a #cairo_surface_t
+ *
+ * Tells cairo that drawing has been done to surface using means other
+ * than cairo, and that cairo should reread any cached areas. Note
+ * that you must call cairo_surface_flush() before doing such drawing.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_surface_mark_dirty (cairo_surface_t *surface)
+{
+ cairo_rectangle_int_t extents;
+
+ if (unlikely (surface->status))
+ return;
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ _cairo_surface_get_extents (surface, &extents);
+ cairo_surface_mark_dirty_rectangle (surface,
+ extents.x, extents.y,
+ extents.width, extents.height);
+}
+slim_hidden_def (cairo_surface_mark_dirty);
+
+/**
+ * cairo_surface_mark_dirty_rectangle:
+ * @surface: a #cairo_surface_t
+ * @x: X coordinate of dirty rectangle
+ * @y: Y coordinate of dirty rectangle
+ * @width: width of dirty rectangle
+ * @height: height of dirty rectangle
+ *
+ * Like cairo_surface_mark_dirty(), but drawing has been done only to
+ * the specified rectangle, so that cairo can retain cached contents
+ * for other parts of the surface.
+ *
+ * Any cached clip set on the surface will be reset by this function,
+ * to make sure that future cairo calls have the clip set that they
+ * expect.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ /* The application *should* have called cairo_surface_flush() before
+ * modifying the surface independently of cairo (and thus having to
+ * call mark_dirty()). */
+ assert (! _cairo_surface_has_snapshots (surface));
+ assert (! _cairo_surface_has_mime_data (surface));
+
+ surface->is_clear = FALSE;
+ surface->serial++;
+
+ if (surface->damage) {
+ cairo_box_t box;
+
+ box.p1.x = x;
+ box.p1.y = y;
+ box.p2.x = x + width;
+ box.p2.y = y + height;
+
+ surface->damage = _cairo_damage_add_box (surface->damage, &box);
+ }
+
+ if (surface->backend->mark_dirty_rectangle != NULL) {
+ /* XXX: FRAGILE: We're ignoring the scaling component of
+ * device_transform here. I don't know what the right thing to
+ * do would actually be if there were some scaling here, but
+ * we avoid this since device_transfom scaling is not exported
+ * publicly and mark_dirty is not used internally. */
+ status = surface->backend->mark_dirty_rectangle (surface,
+ x + surface->device_transform.x0,
+ y + surface->device_transform.y0,
+ width, height);
+
+ if (unlikely (status))
+ _cairo_surface_set_error (surface, status);
+ }
+}
+slim_hidden_def (cairo_surface_mark_dirty_rectangle);
+
+/**
+ * _cairo_surface_set_device_scale:
+ * @surface: a #cairo_surface_t
+ * @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 (unlikely (surface->status))
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (surface, status);
+ return;
+ }
+
+ surface->device_transform.xx = 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.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_surface_set_device_offset (cairo_surface_t *surface,
+ double x_offset,
+ double y_offset)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (surface, status);
+ return;
+ }
+
+ surface->device_transform.x0 = x_offset;
+ surface->device_transform.y0 = y_offset;
+
+ surface->device_transform_inverse = surface->device_transform;
+ status = cairo_matrix_invert (&surface->device_transform_inverse);
+ /* should always be invertible unless given pathological input */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _cairo_observers_notify (&surface->device_transform_observers, surface);
+}
+slim_hidden_def (cairo_surface_set_device_offset);
+
+/**
+ * cairo_surface_get_device_offset:
+ * @surface: a #cairo_surface_t
+ * @x_offset: the offset in the X direction, in device units
+ * @y_offset: the offset in the Y direction, in device units
+ *
+ * This function returns the previous device offset set by
+ * cairo_surface_set_device_offset().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_surface_get_device_offset (cairo_surface_t *surface,
+ double *x_offset,
+ double *y_offset)
+{
+ if (x_offset)
+ *x_offset = surface->device_transform.x0;
+ if (y_offset)
+ *y_offset = surface->device_transform.y0;
+}
+slim_hidden_def (cairo_surface_get_device_offset);
+
+/**
+ * cairo_surface_set_fallback_resolution:
+ * @surface: a #cairo_surface_t
+ * @x_pixels_per_inch: horizontal setting for pixels per inch
+ * @y_pixels_per_inch: vertical setting for pixels per inch
+ *
+ * Set the horizontal and vertical resolution for image fallbacks.
+ *
+ * When certain operations aren't supported natively by a backend,
+ * cairo will fallback by rendering operations to an image and then
+ * overlaying that image onto the output. For backends that are
+ * natively vector-oriented, this function can be used to set the
+ * resolution used for these image fallbacks, (larger values will
+ * result in more detailed images, but also larger file sizes).
+ *
+ * Some examples of natively vector-oriented backends are the ps, pdf,
+ * and svg backends.
+ *
+ * For backends that are natively raster-oriented, image fallbacks are
+ * still possible, but they are always performed at the native
+ * device resolution. So this function has no effect on those
+ * backends.
+ *
+ * Note: The fallback resolution only takes effect at the time of
+ * completing a page (with cairo_show_page() or cairo_copy_page()) so
+ * there is currently no way to have more than one fallback resolution
+ * in effect on a single page.
+ *
+ * The default fallback 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 (unlikely (surface->status))
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) {
+ /* XXX Could delay raising the error until we fallback, but throwing
+ * the error here means that we can catch the real culprit.
+ */
+ _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX);
+ return;
+ }
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (surface, status);
+ return;
+ }
+
+ surface->x_fallback_resolution = x_pixels_per_inch;
+ surface->y_fallback_resolution = y_pixels_per_inch;
+}
+slim_hidden_def (cairo_surface_set_fallback_resolution);
+
+/**
+ * cairo_surface_get_fallback_resolution:
+ * @surface: a #cairo_surface_t
+ * @x_pixels_per_inch: horizontal pixels per inch
+ * @y_pixels_per_inch: vertical pixels per inch
+ *
+ * This function returns the previous fallback resolution set by
+ * cairo_surface_set_fallback_resolution(), or default fallback
+ * resolution if never set.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_surface_get_fallback_resolution (cairo_surface_t *surface,
+ double *x_pixels_per_inch,
+ double *y_pixels_per_inch)
+{
+ if (x_pixels_per_inch)
+ *x_pixels_per_inch = surface->x_fallback_resolution;
+ if (y_pixels_per_inch)
+ *y_pixels_per_inch = surface->y_fallback_resolution;
+}
+
+cairo_bool_t
+_cairo_surface_has_device_transform (cairo_surface_t *surface)
+{
+ return ! _cairo_matrix_is_identity (&surface->device_transform);
+}
+
+/**
+ * _cairo_surface_acquire_source_image:
+ * @surface: a #cairo_surface_t
+ * @image_out: location to store a pointer to an image surface that
+ * has identical contents to @surface. This surface could be @surface
+ * itself, a surface held internal to @surface, or it could be a new
+ * surface with a copy of the relevant portion of @surface.
+ * @image_extra: location to store image specific backend data
+ *
+ * Gets an image surface to use when drawing as a fallback when drawing with
+ * @surface as a source. _cairo_surface_release_source_image() must be called
+ * when finished.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if an image was stored in @image_out.
+ * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified
+ * surface. Or %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_status_t
+_cairo_surface_acquire_source_image (cairo_surface_t *surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ assert (!surface->finished);
+
+ if (surface->backend->acquire_source_image == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = surface->backend->acquire_source_image (surface,
+ image_out, image_extra);
+ if (unlikely (status))
+ return _cairo_surface_set_error (surface, status);
+
+ _cairo_debug_check_image_surface_is_defined (&(*image_out)->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_default_acquire_source_image (void *_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_surface_t *surface = _surface;
+ cairo_rectangle_int_t extents;
+
+ if (unlikely (! surface->backend->get_extents (surface, &extents)))
+ return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+
+ *image_out = _cairo_surface_map_to_image (surface, &extents);
+ *image_extra = NULL;
+ return (*image_out)->base.status;
+}
+
+/**
+ * _cairo_surface_release_source_image:
+ * @surface: a #cairo_surface_t
+ * @image_extra: same as return from the matching _cairo_surface_acquire_source_image()
+ *
+ * Releases any resources obtained with _cairo_surface_acquire_source_image()
+ **/
+void
+_cairo_surface_release_source_image (cairo_surface_t *surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ assert (!surface->finished);
+
+ if (surface->backend->release_source_image)
+ surface->backend->release_source_image (surface, image, image_extra);
+}
+
+void
+_cairo_surface_default_release_source_image (void *surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_status_t ignored;
+
+ ignored = _cairo_surface_unmap_image (surface, image);
+ (void)ignored;
+}
+
+
+cairo_surface_t *
+_cairo_surface_get_source (cairo_surface_t *surface,
+ cairo_rectangle_int_t *extents)
+{
+ assert (surface->backend->source);
+ return surface->backend->source (surface, extents);
+}
+
+cairo_surface_t *
+_cairo_surface_default_source (void *surface,
+ cairo_rectangle_int_t *extents)
+{
+ if (extents)
+ _cairo_surface_get_extents(surface, extents);
+ return surface;
+}
+
+static cairo_status_t
+_pattern_has_error (const cairo_pattern_t *pattern)
+{
+ const cairo_surface_pattern_t *spattern;
+
+ if (unlikely (pattern->status))
+ return pattern->status;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_STATUS_SUCCESS;
+
+ spattern = (const cairo_surface_pattern_t *) pattern;
+ if (unlikely (spattern->surface->status))
+ return spattern->surface->status;
+
+ if (unlikely (spattern->surface->finished))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+nothing_to_do (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source)
+{
+ if (_cairo_pattern_is_clear (source)) {
+ if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
+ return TRUE;
+
+ if (op == CAIRO_OPERATOR_SOURCE)
+ op = CAIRO_OPERATOR_CLEAR;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
+ return TRUE;
+
+ if (op == CAIRO_OPERATOR_ATOP && (surface->content & CAIRO_CONTENT_COLOR) ==0)
+ return TRUE;
+
+ return FALSE;
+}
+
+cairo_status_t
+_cairo_surface_paint (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ if (nothing_to_do (surface, op, source))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status))
+ return status;
+
+ status = surface->backend->paint (surface, op, source, clip);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
+ surface->serial++;
+ }
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_mask (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* If the mask is blank, this is just an expensive no-op */
+ if (_cairo_pattern_is_clear (mask) &&
+ _cairo_operator_bounded_by_mask (op))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ status = _pattern_has_error (mask);
+ if (unlikely (status))
+ return status;
+
+ if (nothing_to_do (surface, op, source))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status))
+ return status;
+
+ status = surface->backend->mask (surface, op, source, mask, clip);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ surface->is_clear = FALSE;
+ surface->serial++;
+ }
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_fill_stroke (cairo_surface_t *surface,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->is_clear &&
+ fill_op == CAIRO_OPERATOR_CLEAR &&
+ stroke_op == CAIRO_OPERATOR_CLEAR)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _pattern_has_error (fill_source);
+ if (unlikely (status))
+ return status;
+
+ status = _pattern_has_error (stroke_source);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status))
+ return status;
+
+ if (surface->backend->fill_stroke) {
+ cairo_matrix_t dev_ctm = *stroke_ctm;
+ cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
+
+ status = surface->backend->fill_stroke (surface,
+ fill_op, fill_source, fill_rule,
+ fill_tolerance, fill_antialias,
+ path,
+ stroke_op, stroke_source,
+ stroke_style,
+ &dev_ctm, &dev_ctm_inverse,
+ stroke_tolerance, stroke_antialias,
+ clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto FINISH;
+ }
+
+ status = _cairo_surface_fill (surface, fill_op, fill_source, path,
+ fill_rule, fill_tolerance, fill_antialias,
+ clip);
+ if (unlikely (status))
+ goto FINISH;
+
+ status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path,
+ stroke_style, stroke_ctm, stroke_ctm_inverse,
+ stroke_tolerance, stroke_antialias,
+ clip);
+ if (unlikely (status))
+ goto FINISH;
+
+ FINISH:
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ surface->is_clear = FALSE;
+ surface->serial++;
+ }
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_stroke (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ if (nothing_to_do (surface, op, source))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status))
+ return status;
+
+ status = surface->backend->stroke (surface, op, source,
+ path, stroke_style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ surface->is_clear = FALSE;
+ surface->serial++;
+ }
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_fill (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ if (nothing_to_do (surface, op, source))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status))
+ return status;
+
+ status = surface->backend->fill (surface, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ surface->is_clear = FALSE;
+ surface->serial++;
+ }
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+/**
+ * cairo_surface_copy_page:
+ * @surface: a #cairo_surface_t
+ *
+ * Emits the current page for backends that support multiple pages,
+ * but doesn't clear it, so that the contents of the current page will
+ * be retained for the next page. Use cairo_surface_show_page() if you
+ * want to get an empty page after the emission.
+ *
+ * There is a convenience function for this that takes a #cairo_t,
+ * namely cairo_copy_page().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_surface_copy_page (cairo_surface_t *surface)
+{
+ if (unlikely (surface->status))
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+ return;
+ }
+
+ /* It's fine if some backends don't implement copy_page */
+ if (surface->backend->copy_page == NULL)
+ return;
+
+ _cairo_surface_set_error (surface, surface->backend->copy_page (surface));
+}
+slim_hidden_def (cairo_surface_copy_page);
+
+/**
+ * cairo_surface_show_page:
+ * @surface: a #cairo_Surface_t
+ *
+ * Emits and clears the current page for backends that support multiple
+ * pages. Use cairo_surface_copy_page() if you don't want to clear the page.
+ *
+ * There is a convenience function for this that takes a #cairo_t,
+ * namely cairo_show_page().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_surface_show_page (cairo_surface_t *surface)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return;
+
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+ return;
+ }
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (surface, status);
+ return;
+ }
+
+ /* It's fine if some backends don't implement show_page */
+ if (surface->backend->show_page == NULL)
+ return;
+
+ _cairo_surface_set_error (surface, surface->backend->show_page (surface));
+}
+slim_hidden_def (cairo_surface_show_page);
+
+/**
+ * _cairo_surface_get_extents:
+ * @surface: the #cairo_surface_t to fetch extents for
+ *
+ * This function returns a bounding box for the surface. The surface
+ * bounds are defined as a region beyond which no rendering will
+ * possibly be recorded, in other words, it is the maximum extent of
+ * potentially usable coordinates.
+ *
+ * For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface
+ * might be conceived as unbounded, but we force the user to provide a
+ * maximum size at the time of surface_create. So get_extents uses
+ * that size.
+ *
+ * Note: The coordinates returned are in "backend" space rather than
+ * "surface" space. That is, they are relative to the true (0,0)
+ * origin rather than the device_transform origin. This might seem a
+ * bit inconsistent with other #cairo_surface_t interfaces, but all
+ * current callers are within the surface layer where backend space is
+ * desired.
+ *
+ * This behavior would have to be changed is we ever exported a public
+ * variant of this function.
+ **/
+cairo_bool_t
+_cairo_surface_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t bounded;
+
+ bounded = FALSE;
+ if (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)
+{
+ if (unlikely (surface->status))
+ return FALSE;
+
+ if (unlikely (surface->finished)) {
+ _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+ return FALSE;
+ }
+
+ if (surface->backend->has_show_text_glyphs)
+ return surface->backend->has_show_text_glyphs (surface);
+ else
+ return surface->backend->show_text_glyphs != NULL;
+}
+slim_hidden_def (cairo_surface_has_show_text_glyphs);
+
+/* Note: the backends may modify the contents of the glyph array as long as
+ * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to
+ * avoid copying the array again and again, and edit it in-place.
+ * Backends are in fact free to use the array as a generic buffer as they
+ * see fit.
+ *
+ * For show_glyphs backend method, and NOT for show_text_glyphs method,
+ * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify
+ * that they have successfully rendered some of the glyphs (from the beginning
+ * of the array), but not all. If they don't touch remaining_glyphs, it
+ * defaults to all glyphs.
+ *
+ * See commits 5a9642c5746fd677aed35ce620ce90b1029b1a0c and
+ * 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale.
+ */
+cairo_status_t
+_cairo_surface_show_text_glyphs (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_scaled_font_t *dev_scaled_font = scaled_font;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (num_glyphs == 0 && utf8_len == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ if (nothing_to_do (surface, op, source))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_begin_modification (surface);
+ if (unlikely (status))
+ return status;
+
+ 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)
+ {
+ status = surface->backend->show_glyphs (surface, op,
+ source,
+ glyphs, num_glyphs,
+ dev_scaled_font,
+ clip);
+ }
+ } else {
+ /* A mere show_glyphs call. Try show_glyphs backend method first */
+ if (surface->backend->show_glyphs != NULL) {
+ status = surface->backend->show_glyphs (surface, op,
+ source,
+ glyphs, num_glyphs,
+ dev_scaled_font,
+ clip);
+ } else if (surface->backend->show_text_glyphs != NULL) {
+ /* Intentionally only try show_text_glyphs method for show_glyphs
+ * calls if backend does not have show_glyphs. If backend has
+ * both methods implemented, we don't fallback from show_glyphs to
+ * show_text_glyphs, and hence the backend can assume in its
+ * show_text_glyphs call that clusters is not NULL (which also
+ * implies that UTF-8 is not NULL, unless the text is
+ * zero-length).
+ */
+ status = surface->backend->show_text_glyphs (surface, op,
+ source,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters, cluster_flags,
+ dev_scaled_font,
+ clip);
+ }
+ }
+
+ if (dev_scaled_font != scaled_font)
+ cairo_scaled_font_destroy (dev_scaled_font);
+
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ surface->is_clear = FALSE;
+ surface->serial++;
+ }
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+/**
+ * _cairo_surface_set_resolution:
+ * @surface: the surface
+ * @x_res: x resolution, in dpi
+ * @y_res: y resolution, in dpi
+ *
+ * Set the actual surface resolution of @surface to the given x and y DPI.
+ * Mainly used for correctly computing the scale factor when fallback
+ * rendering needs to take place in the paginated surface.
+ **/
+void
+_cairo_surface_set_resolution (cairo_surface_t *surface,
+ double x_res,
+ double y_res)
+{
+ if (surface->status)
+ return;
+
+ surface->x_resolution = x_res;
+ surface->y_resolution = y_res;
+}
+
+cairo_surface_t *
+_cairo_surface_create_in_error (cairo_status_t status)
+{
+ assert (status < CAIRO_STATUS_LAST_STATUS);
+ switch (status) {
+ case CAIRO_STATUS_NO_MEMORY:
+ return (cairo_surface_t *) &_cairo_surface_nil;
+ case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
+ return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch;
+ case CAIRO_STATUS_INVALID_STATUS:
+ return (cairo_surface_t *) &_cairo_surface_nil_invalid_status;
+ case CAIRO_STATUS_INVALID_CONTENT:
+ return (cairo_surface_t *) &_cairo_surface_nil_invalid_content;
+ case CAIRO_STATUS_INVALID_FORMAT:
+ return (cairo_surface_t *) &_cairo_surface_nil_invalid_format;
+ case CAIRO_STATUS_INVALID_VISUAL:
+ return (cairo_surface_t *) &_cairo_surface_nil_invalid_visual;
+ case CAIRO_STATUS_READ_ERROR:
+ return (cairo_surface_t *) &_cairo_surface_nil_read_error;
+ case CAIRO_STATUS_WRITE_ERROR:
+ return (cairo_surface_t *) &_cairo_surface_nil_write_error;
+ case CAIRO_STATUS_FILE_NOT_FOUND:
+ return (cairo_surface_t *) &_cairo_surface_nil_file_not_found;
+ case CAIRO_STATUS_TEMP_FILE_ERROR:
+ return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error;
+ case CAIRO_STATUS_INVALID_STRIDE:
+ return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride;
+ case CAIRO_STATUS_INVALID_SIZE:
+ return (cairo_surface_t *) &_cairo_surface_nil_invalid_size;
+ case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
+ return (cairo_surface_t *) &_cairo_surface_nil_device_type_mismatch;
+ case CAIRO_STATUS_DEVICE_ERROR:
+ return (cairo_surface_t *) &_cairo_surface_nil_device_error;
+ case CAIRO_STATUS_SUCCESS:
+ case CAIRO_STATUS_LAST_STATUS:
+ ASSERT_NOT_REACHED;
+ /* fall-through */
+ case CAIRO_STATUS_INVALID_RESTORE:
+ case CAIRO_STATUS_INVALID_POP_GROUP:
+ case CAIRO_STATUS_NO_CURRENT_POINT:
+ case CAIRO_STATUS_INVALID_MATRIX:
+ case CAIRO_STATUS_NULL_POINTER:
+ case CAIRO_STATUS_INVALID_STRING:
+ case CAIRO_STATUS_INVALID_PATH_DATA:
+ case CAIRO_STATUS_SURFACE_FINISHED:
+ case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
+ case CAIRO_STATUS_INVALID_DASH:
+ case CAIRO_STATUS_INVALID_DSC_COMMENT:
+ case CAIRO_STATUS_INVALID_INDEX:
+ case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
+ case CAIRO_STATUS_FONT_TYPE_MISMATCH:
+ case CAIRO_STATUS_USER_FONT_IMMUTABLE:
+ case CAIRO_STATUS_USER_FONT_ERROR:
+ case CAIRO_STATUS_NEGATIVE_COUNT:
+ case CAIRO_STATUS_INVALID_CLUSTERS:
+ case CAIRO_STATUS_INVALID_SLANT:
+ case CAIRO_STATUS_INVALID_WEIGHT:
+ case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+ case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+ case CAIRO_STATUS_DEVICE_FINISHED:
+ default:
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_surface_t *) &_cairo_surface_nil;
+ }
+}
+
+cairo_surface_t *
+_cairo_int_surface_create_in_error (cairo_int_status_t status)
+{
+ if (status < CAIRO_INT_STATUS_LAST_STATUS)
+ return _cairo_surface_create_in_error (status);
+
+ switch ((int)status) {
+ case CAIRO_INT_STATUS_UNSUPPORTED:
+ return (cairo_surface_t *) &_cairo_surface_nil_unsupported;
+ case CAIRO_INT_STATUS_NOTHING_TO_DO:
+ return (cairo_surface_t *) &_cairo_surface_nil_nothing_to_do;
+ default:
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_surface_t *) &_cairo_surface_nil;
+ }
+}
+
+/* LocalWords: rasterized
+ */
diff --git a/src/cairo-svg-surface-private.h b/src/cairo-svg-surface-private.h
new file mode 100755
index 000000000..ddbf464b1
--- /dev/null
+++ b/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/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
new file mode 100755
index 000000000..e2d99499d
--- /dev/null
+++ b/src/cairo-svg-surface.c
@@ -0,0 +1,2876 @@
+/* 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-array-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-info-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-svg-surface-private.h"
+
+/**
+ * SECTION:cairo-svg
+ * @Title: SVG Surfaces
+ * @Short_Description: Rendering SVG documents
+ * @See_Also: #cairo_surface_t
+ *
+ * The SVG surface is used to render cairo graphics to
+ * SVG files and is a multi-page vector surface backend.
+ **/
+
+/**
+ * CAIRO_HAS_SVG_SURFACE:
+ *
+ * Defined if the SVG surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.2
+ **/
+
+typedef struct cairo_svg_page cairo_svg_page_t;
+
+static const int invalid_pattern_id = -1;
+
+static const cairo_svg_version_t _cairo_svg_versions[] =
+{
+ CAIRO_SVG_VERSION_1_1,
+ CAIRO_SVG_VERSION_1_2
+};
+
+#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
+
+static const char *_cairo_svg_supported_mime_types[] =
+{
+ CAIRO_MIME_TYPE_JPEG,
+ CAIRO_MIME_TYPE_PNG,
+ CAIRO_MIME_TYPE_URI,
+ NULL
+};
+
+static void
+_cairo_svg_surface_emit_path (cairo_output_stream_t *output,
+ const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm_inverse);
+
+static cairo_bool_t
+_cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
+{
+ return version > CAIRO_SVG_VERSION_1_1;
+}
+
+static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
+{
+ "SVG 1.1",
+ "SVG 1.2"
+};
+
+static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
+{
+ "1.1",
+ "1.2"
+};
+
+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 = NULL;
+ 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);
+ cairo_surface_destroy (paginated);
+
+ 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)
+{
+ cairo_svg_page_t page;
+ cairo_output_stream_t *stream;
+ cairo_int_status_t status;
+ unsigned int i;
+
+ stream = _cairo_memory_stream_create ();
+ if (_cairo_output_stream_get_status (stream)) {
+ status = _cairo_output_stream_destroy (stream);
+ return NULL;
+ }
+
+ page.surface_id = surface->base.unique_id;
+ page.clip_level = surface->clip_level;
+ page.xml_node = surface->xml_node;
+
+ if (_cairo_array_append (&surface->page_set, &page)) {
+ status = _cairo_output_stream_destroy (stream);
+ return NULL;
+ }
+
+ surface->xml_node = stream;
+ surface->clip_level = 0;
+ for (i = 0; i < page.clip_level; i++)
+ _cairo_output_stream_printf (page.xml_node, "</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,
+ const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm_inverse)
+{
+ cairo_status_t status;
+ svg_path_info_t info;
+
+ _cairo_output_stream_printf (output, "d=\"");
+
+ info.output = output;
+ info.ctm_inverse = ctm_inverse;
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_svg_path_move_to,
+ _cairo_svg_path_line_to,
+ _cairo_svg_path_curve_to,
+ _cairo_svg_path_close_path,
+ &info);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _cairo_output_stream_printf (output, "\"");
+}
+
+static cairo_int_status_t
+_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
+ cairo_scaled_font_t *scaled_font,
+ unsigned long glyph_index)
+{
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_int_status_t status;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS|
+ CAIRO_SCALED_GLYPH_INFO_PATH,
+ &scaled_glyph);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (document->xml_node_glyphs,
+ "<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)) {
+ cairo_surface_destroy (&image->base);
+ 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_int_status_t
+_cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
+ cairo_scaled_font_t *scaled_font,
+ unsigned long scaled_font_glyph_index,
+ unsigned int font_id,
+ unsigned int subset_glyph_index)
+{
+ cairo_int_status_t status;
+
+ _cairo_output_stream_printf (document->xml_node_glyphs,
+ "<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_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_svg_document_t *document = closure;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+ unsigned int i;
+
+ _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
+ for (i = 0; i < font_subset->num_glyphs; i++) {
+ status = _cairo_svg_document_emit_glyph (document,
+ font_subset->scaled_font,
+ font_subset->glyphs[i],
+ font_subset->font_id, i);
+ if (unlikely (status))
+ break;
+ }
+ _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
+{
+ cairo_status_t status;
+
+ status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
+ _cairo_svg_document_emit_font_subset,
+ document);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
+ _cairo_svg_document_emit_font_subset,
+ document);
+
+ FAIL:
+ _cairo_scaled_font_subsets_destroy (document->font_subsets);
+ document->font_subsets = NULL;
+
+ return status;
+}
+
+static char const *
+_cairo_svg_surface_operators[] = {
+ "clear",
+
+ "src", "src-over", "src-in",
+ "src-out", "src-atop",
+
+ "dst", "dst-over", "dst-in",
+ "dst-out", "dst-atop",
+
+ "xor", "plus",
+ "color-dodge", /* FIXME: saturate ? */
+
+ "multiply", "screen", "overlay",
+ "darken", "lighten",
+ "color-dodge", "color-burn",
+ "hard-light", "soft-light",
+ "difference", "exclusion"
+};
+
+static cairo_bool_t
+_cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
+ cairo_operator_t op)
+{
+ /* guard against newly added operators */
+ if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* allow operators being NULL if they are unsupported */
+ if (_cairo_svg_surface_operators[op] == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ cairo_svg_document_t *document = surface->document;
+
+ if (surface->force_fallbacks &&
+ surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* SVG doesn't support extend reflect for image pattern */
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ pattern->extend == CAIRO_EXTEND_REFLECT)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
+ return _cairo_svg_surface_analyze_operator (surface, op);
+
+ if (op == CAIRO_OPERATOR_OVER)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* The SOURCE operator is only supported if there is nothing
+ * painted underneath. */
+ if (op == CAIRO_OPERATOR_SOURCE)
+ return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_svg_surface_finish (void *abstract_surface)
+{
+ cairo_status_t status, status2;
+ cairo_svg_surface_t *surface = abstract_surface;
+ cairo_svg_document_t *document = surface->document;
+ cairo_svg_page_t *page;
+ unsigned int i;
+
+ if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
+ status = _cairo_svg_document_finish (document);
+ else
+ status = CAIRO_STATUS_SUCCESS;
+
+ if (surface->xml_node != NULL) {
+ status2 = _cairo_output_stream_destroy (surface->xml_node);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+ }
+
+ for (i = 0; i < surface->page_set.num_elements; i++) {
+ page = _cairo_array_index (&surface->page_set, i);
+ status2 = _cairo_output_stream_destroy (page->xml_node);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+ }
+ _cairo_array_fini (&surface->page_set);
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ 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_int_status_t status;
+ base64_write_closure_t info;
+
+ status = _cairo_surface_base64_encode_jpeg (surface, output);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_surface_base64_encode_png (surface, output);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ info.output = output;
+ info.in_mem = 0;
+ info.trailing = 0;
+
+ _cairo_output_stream_printf (info.output, "data:image/png;base64,");
+
+ status = cairo_surface_write_to_png_stream (surface, base64_write_func,
+ (void *) &info);
+
+ if (unlikely (status))
+ return status;
+
+ if (info.in_mem > 0) {
+ memset (info.src + info.in_mem, 0, 3 - info.in_mem);
+ info.trailing = 3 - info.in_mem;
+ info.in_mem = 3;
+ status = base64_write_func (&info, NULL, 0);
+ }
+
+ return status;
+}
+
+static void
+_cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
+ cairo_svg_surface_t *surface,
+ cairo_operator_t op)
+{
+ if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
+ op != CAIRO_OPERATOR_OVER) {
+ _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
+ if (!_cairo_operator_bounded_by_source (op))
+ _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
+ }
+}
+
+static void
+_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
+ cairo_svg_surface_t *surface,
+ cairo_operator_t op)
+{
+ if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
+ op != CAIRO_OPERATOR_OVER) {
+ _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
+ if (!_cairo_operator_bounded_by_source (op))
+ _cairo_output_stream_printf (output, "clip-to-self:true;");
+ }
+}
+
+/**
+ * _cairo_svg_surface_emit_attr_value:
+ *
+ * Write the value to output the stream as a sequence of characters,
+ * while escaping those which have special meaning in the XML
+ * attribute's value context: &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 = NULL;
+ 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->base.content,
+ source->extents_pixels.width,
+ source->extents_pixels.height);
+ if (unlikely (paginated_surface->status)) {
+ status = paginated_surface->status;
+ cairo_surface_destroy (paginated_surface);
+ return 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->base.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;
+ _cairo_color_init_rgba ((cairo_color_t *) &offset_color_start, 0, 0, 0, 0);
+
+ 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;
+ cairo_matrix_t p2u;
+ cairo_status_t status;
+
+ p2u = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&p2u);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<linearGradient id=\"linear%d\" "
+ "gradientUnits=\"userSpaceOnUse\" "
+ "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
+ document->linear_pattern_id,
+ pattern->pd1.x, pattern->pd1.y,
+ pattern->pd2.x, pattern->pd2.y);
+
+ _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
+ _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
+ _cairo_output_stream_printf (document->xml_node_defs, ">\n");
+
+ status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
+ &pattern->base, 0.0,
+ FALSE, FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "</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_circle_double_t *c0, *c1;
+
+ extend = pattern->base.base.extend;
+
+ if (pattern->cd1.radius < pattern->cd2.radius) {
+ c0 = &pattern->cd1;
+ c1 = &pattern->cd2;
+ reverse_stops = FALSE;
+ } else {
+ c0 = &pattern->cd2;
+ c1 = &pattern->cd1;
+ reverse_stops = TRUE;
+ }
+
+ x0 = c0->center.x;
+ y0 = c0->center.y;
+ r0 = c0->radius;
+ x1 = c1->center.x;
+ y1 = c1->center.y;
+ r1 = c1->radius;
+
+ p2u = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&p2u);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ if (r0 == r1) {
+ unsigned int n_stops = pattern->base.n_stops;
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<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);
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ ASSERT_NOT_REACHED;
+ }
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
+ cairo_svg_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_fill_rule_t fill_rule,
+ const cairo_matrix_t *parent_matrix)
+{
+ _cairo_output_stream_printf (output,
+ "fill-rule:%s;",
+ fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
+ "evenodd" : "nonzero");
+ _cairo_svg_surface_emit_operator_for_style (output, surface, op);
+ return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
+ cairo_svg_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *parent_matrix)
+{
+ cairo_status_t status;
+ const char *line_cap, *line_join;
+ unsigned int i;
+
+ switch (stroke_style->line_cap) {
+ case CAIRO_LINE_CAP_BUTT:
+ line_cap = "butt";
+ break;
+ case CAIRO_LINE_CAP_ROUND:
+ line_cap = "round";
+ break;
+ case CAIRO_LINE_CAP_SQUARE:
+ line_cap = "square";
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ switch (stroke_style->line_join) {
+ case CAIRO_LINE_JOIN_MITER:
+ line_join = "miter";
+ break;
+ case CAIRO_LINE_JOIN_ROUND:
+ line_join = "round";
+ break;
+ case CAIRO_LINE_JOIN_BEVEL:
+ line_join = "bevel";
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_output_stream_printf (output,
+ "stroke-width:%f;"
+ "stroke-linecap:%s;"
+ "stroke-linejoin:%s;",
+ stroke_style->line_width,
+ line_cap,
+ line_join);
+
+ status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
+ if (unlikely (status))
+ return status;
+
+ _cairo_svg_surface_emit_operator_for_style (output, surface, op);
+
+ if (stroke_style->num_dashes > 0) {
+ _cairo_output_stream_printf (output, "stroke-dasharray:");
+ for (i = 0; i < stroke_style->num_dashes; i++) {
+ _cairo_output_stream_printf (output, "%f",
+ stroke_style->dash[i]);
+ if (i + 1 < stroke_style->num_dashes)
+ _cairo_output_stream_printf (output, ",");
+ else
+ _cairo_output_stream_printf (output, ";");
+ }
+ if (stroke_style->dash_offset != 0.0) {
+ _cairo_output_stream_printf (output,
+ "stroke-dashoffset:%f;",
+ stroke_style->dash_offset);
+ }
+ }
+
+ _cairo_output_stream_printf (output,
+ "stroke-miterlimit:%f;",
+ stroke_style->miter_limit);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_fill_stroke (void *abstract_surface,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ const cairo_path_fixed_t*path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_svg_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->xml_node, "<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,
+ const cairo_path_fixed_t*path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_svg_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_svg_surface_analyze_operation (surface, op, source);
+
+ assert (_cairo_svg_surface_operation_supported (surface, op, source));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->xml_node, "<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,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_svg_surface_t *surface = abstract_surface;
+
+ /* Emulation of clear and source operators, when no clipping region
+ * is defined. We just delete existing content of surface root node,
+ * and exit early if operator is clear.
+ */
+ if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
+ clip == NULL)
+ {
+ switch (surface->paginated_mode) {
+ case CAIRO_PAGINATED_MODE_FALLBACK:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PAGINATED_MODE_ANALYZE:
+ return CAIRO_STATUS_SUCCESS;
+
+ case CAIRO_PAGINATED_MODE_RENDER:
+ status = _cairo_output_stream_destroy (surface->xml_node);
+ if (unlikely (status)) {
+ surface->xml_node = NULL;
+ return status;
+ }
+
+ surface->xml_node = _cairo_memory_stream_create ();
+ if (_cairo_output_stream_get_status (surface->xml_node)) {
+ status = _cairo_output_stream_destroy (surface->xml_node);
+ surface->xml_node = NULL;
+ return status;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ if (surface->content == CAIRO_CONTENT_COLOR) {
+ _cairo_output_stream_printf (surface->xml_node,
+ "<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,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_svg_surface_t *surface = abstract_surface;
+ cairo_svg_document_t *document = surface->document;
+ cairo_output_stream_t *mask_stream;
+ char buffer[64];
+ cairo_bool_t discard_filter = FALSE;
+ unsigned int mask_id;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ cairo_status_t source_status, mask_status;
+
+ source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
+ if (_cairo_status_is_error (source_status))
+ return source_status;
+
+ if (mask->has_component_alpha) {
+ mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
+ if (_cairo_status_is_error (mask_status))
+ return mask_status;
+ }
+
+ return _cairo_analysis_surface_merge_status (source_status,
+ mask_status);
+ }
+
+ assert (_cairo_svg_surface_operation_supported (surface, op, source));
+ assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
+ cairo_content_t content = surface_pattern->surface->content;
+ if (content == CAIRO_CONTENT_ALPHA)
+ discard_filter = TRUE;
+ }
+
+ if (!discard_filter)
+ _cairo_svg_surface_emit_alpha_filter (document);
+
+ /* _cairo_svg_surface_emit_paint() will output a pattern definition to
+ * document->xml_node_defs so we need to write the mask element to
+ * a temporary stream and then copy that to xml_node_defs. */
+ mask_stream = _cairo_memory_stream_create ();
+ if (_cairo_output_stream_get_status (mask_stream))
+ return _cairo_output_stream_destroy (mask_stream);
+
+ mask_id = _cairo_svg_document_allocate_mask_id (document);
+
+ _cairo_output_stream_printf (mask_stream,
+ "<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);
+ (void) ignore;
+ return status;
+ }
+
+ _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,
+ const cairo_path_fixed_t*path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_svg_surface_t *surface = abstract_dst;
+ cairo_status_t status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_svg_surface_analyze_operation (surface, op, source);
+
+ assert (_cairo_svg_surface_operation_supported (surface, op, source));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->xml_node, "<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,
+ const cairo_clip_t *clip)
+{
+ cairo_svg_surface_t *surface = abstract_surface;
+ cairo_svg_document_t *document = surface->document;
+ cairo_path_fixed_t path;
+ cairo_int_status_t status;
+ cairo_scaled_font_subsets_glyph_t subset_glyph;
+ int i;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_svg_surface_analyze_operation (surface, op, pattern);
+
+ assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
+
+ if (num_glyphs <= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ /* FIXME it's probably possible to apply a pattern of a gradient to
+ * a group of symbols, but I don't know how yet. Gradients or patterns
+ * are translated by x and y properties of use element. */
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ goto FALLBACK;
+
+ _cairo_output_stream_printf (surface->xml_node, "<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);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+
+static const char **
+_cairo_svg_surface_get_supported_mime_types (void *abstract_surface)
+{
+ return _cairo_svg_supported_mime_types;
+}
+
+static const cairo_surface_backend_t cairo_svg_surface_backend = {
+ CAIRO_SURFACE_TYPE_SVG,
+ _cairo_svg_surface_finish,
+
+ _cairo_default_context_create,
+
+ NULL, /* create_similar: handled by wrapper */
+ NULL, /* create_similar_image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ _cairo_svg_surface_copy_page,
+ _cairo_svg_surface_show_page,
+
+ _cairo_svg_surface_get_extents,
+ _cairo_svg_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark dirty rectangle */
+
+ _cairo_svg_surface_paint,
+ _cairo_svg_surface_mask,
+ _cairo_svg_surface_stroke,
+ _cairo_svg_surface_fill,
+ _cairo_svg_surface_fill_stroke,
+ _cairo_svg_surface_show_glyphs,
+ NULL, /* has_show_text_glyphs */
+ NULL, /* show_text_glyphs */
+ _cairo_svg_surface_get_supported_mime_types,
+};
+
+static cairo_status_t
+_cairo_svg_document_create (cairo_output_stream_t *output_stream,
+ double width,
+ double height,
+ cairo_svg_version_t version,
+ cairo_svg_document_t **document_out)
+{
+ cairo_svg_document_t *document;
+ cairo_status_t status, status_ignored;
+
+ if (output_stream->status)
+ return output_stream->status;
+
+ document = 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_INT_STATUS_SUCCESS;
+}
+
+static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
+ NULL /*_cairo_svg_surface_start_page*/,
+ _cairo_svg_surface_set_paginated_mode,
+ NULL, /* _cairo_svg_surface_set_bounding_box */
+ NULL, /* _cairo_svg_surface_set_fallback_images_required */
+ _cairo_svg_surface_supports_fine_grained_fallbacks,
+
+};
diff --git a/src/cairo-svg.h b/src/cairo-svg.h
new file mode 100755
index 000000000..592c645f6
--- /dev/null
+++ b/src/cairo-svg.h
@@ -0,0 +1,84 @@
+/* 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. (Since 1.2)
+ * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification. (Since 1.2)
+ *
+ * #cairo_svg_version_t is used to describe the version number of the SVG
+ * specification that a generated SVG file will conform to.
+ *
+ * Since: 1.2
+ **/
+typedef enum _cairo_svg_version {
+ CAIRO_SVG_VERSION_1_1,
+ CAIRO_SVG_VERSION_1_2
+} cairo_svg_version_t;
+
+cairo_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/src/cairo-tee-surface-private.h b/src/cairo-tee-surface-private.h
new file mode 100755
index 000000000..a83cfc959
--- /dev/null
+++ b/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/src/cairo-tee-surface.c b/src/cairo-tee-surface.c
new file mode 100755
index 000000000..294e5f162
--- /dev/null
+++ b/src/cairo-tee-surface.c
@@ -0,0 +1,602 @@
+/* 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-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-tee-surface-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-surface-wrapper-private.h"
+#include "cairo-array-private.h"
+#include "cairo-image-surface-inline.h"
+
+typedef struct _cairo_tee_surface {
+ cairo_surface_t base;
+
+ cairo_surface_wrapper_t master;
+ cairo_array_t slaves;
+} cairo_tee_surface_t;
+
+slim_hidden_proto (cairo_tee_surface_create);
+slim_hidden_proto (cairo_tee_surface_add);
+
+static cairo_surface_t *
+_cairo_tee_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+
+ cairo_tee_surface_t *other = abstract_surface;
+ cairo_surface_t *similar;
+ cairo_surface_t *surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+
+ similar = _cairo_surface_wrapper_create_similar (&other->master,
+ content, width, height);
+ surface = cairo_tee_surface_create (similar);
+ cairo_surface_destroy (similar);
+ if (unlikely (surface->status))
+ return surface;
+
+ num_slaves = _cairo_array_num_elements (&other->slaves);
+ slaves = _cairo_array_index (&other->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+
+ similar = _cairo_surface_wrapper_create_similar (&slaves[n],
+ content,
+ width, height);
+ cairo_tee_surface_add (surface, similar);
+ cairo_surface_destroy (similar);
+ }
+
+ if (unlikely (surface->status)) {
+ cairo_status_t status = surface->status;
+ cairo_surface_destroy (surface);
+ surface = _cairo_surface_create_in_error (status);
+ }
+
+ return surface;
+}
+
+static cairo_status_t
+_cairo_tee_surface_finish (void *abstract_surface)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+
+ _cairo_surface_wrapper_fini (&surface->master);
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++)
+ _cairo_surface_wrapper_fini (&slaves[n]);
+
+ _cairo_array_fini (&surface->slaves);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_tee_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ return _cairo_surface_get_source (surface->master.target, extents);
+}
+
+static cairo_status_t
+_cairo_tee_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int num_slaves, n;
+
+ /* we prefer to use a real image surface if available */
+ if (_cairo_surface_is_image (surface->master.target)) {
+ return _cairo_surface_wrapper_acquire_source_image (&surface->master,
+ image_out, image_extra);
+ }
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ if (_cairo_surface_is_image (slaves[n].target)) {
+ return _cairo_surface_wrapper_acquire_source_image (&slaves[n],
+ image_out,
+ image_extra);
+ }
+ }
+
+ return _cairo_surface_wrapper_acquire_source_image (&surface->master,
+ image_out, image_extra);
+}
+
+static void
+_cairo_tee_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+
+ _cairo_surface_wrapper_release_source_image (&surface->master,
+ image, image_extra);
+}
+
+static cairo_surface_t *
+_cairo_tee_surface_snapshot (void *abstract_surface)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int num_slaves, n;
+
+ /* we prefer to use a recording surface for our snapshots */
+ if (_cairo_surface_is_recording (surface->master.target))
+ return _cairo_surface_wrapper_snapshot (&surface->master);
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ if (_cairo_surface_is_recording (slaves[n].target))
+ return _cairo_surface_wrapper_snapshot (&slaves[n]);
+ }
+
+ return _cairo_surface_wrapper_snapshot (&surface->master);
+}
+
+static cairo_bool_t
+_cairo_tee_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_wrapper_get_extents (&surface->master, rectangle);
+}
+
+static void
+_cairo_tee_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+
+ _cairo_surface_wrapper_get_font_options (&surface->master, options);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_int_status_t status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_surface_wrapper_paint (&surface->master, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ cairo_int_status_t status;
+ int n, num_slaves;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ status = _cairo_surface_wrapper_mask (&slaves[n],
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_surface_wrapper_mask (&surface->master,
+ op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ cairo_int_status_t status;
+ int n, num_slaves;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ status = _cairo_surface_wrapper_stroke (&slaves[n],
+ op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_surface_wrapper_stroke (&surface->master,
+ op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ cairo_int_status_t status;
+ int n, num_slaves;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ status = _cairo_surface_wrapper_fill (&slaves[n],
+ op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_surface_wrapper_fill (&surface->master,
+ op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_bool_t
+_cairo_tee_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ cairo_int_status_t status;
+ int n, num_slaves;
+ cairo_glyph_t *glyphs_copy;
+
+ /* XXX: This copying is ugly. */
+ glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (glyphs_copy == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+ status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op,
+ source,
+ utf8, utf8_len,
+ glyphs_copy, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+
+ memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+ status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op,
+ source,
+ utf8, utf8_len,
+ glyphs_copy, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
+CLEANUP:
+ free (glyphs_copy);
+ return status;
+}
+
+static const cairo_surface_backend_t cairo_tee_surface_backend = {
+ CAIRO_SURFACE_TYPE_TEE,
+ _cairo_tee_surface_finish,
+
+ _cairo_default_context_create, /* XXX */
+
+ _cairo_tee_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_tee_surface_source,
+ _cairo_tee_surface_acquire_source_image,
+ _cairo_tee_surface_release_source_image,
+ _cairo_tee_surface_snapshot,
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_tee_surface_get_extents,
+ _cairo_tee_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_tee_surface_paint,
+ _cairo_tee_surface_mask,
+ _cairo_tee_surface_stroke,
+ _cairo_tee_surface_fill,
+ NULL, /* fill_stroke */
+
+ NULL, /* show_glyphs */
+
+ _cairo_tee_surface_has_show_text_glyphs,
+ _cairo_tee_surface_show_text_glyphs
+};
+
+cairo_surface_t *
+cairo_tee_surface_create (cairo_surface_t *master)
+{
+ cairo_tee_surface_t *surface;
+
+ if (unlikely (master->status))
+ return _cairo_surface_create_in_error (master->status);
+
+ surface = 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);
+
+ _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t));
+
+ return &surface->base;
+}
+slim_hidden_def (cairo_tee_surface_create);
+
+void
+cairo_tee_surface_add (cairo_surface_t *abstract_surface,
+ cairo_surface_t *target)
+{
+ cairo_tee_surface_t *surface;
+ cairo_surface_wrapper_t slave;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (abstract_surface->backend != &cairo_tee_surface_backend) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (unlikely (target->status)) {
+ status = _cairo_surface_set_error (abstract_surface, target->status);
+ return;
+ }
+
+ surface = (cairo_tee_surface_t *) abstract_surface;
+
+ _cairo_surface_wrapper_init (&slave, target);
+ status = _cairo_array_append (&surface->slaves, &slave);
+ if (unlikely (status)) {
+ _cairo_surface_wrapper_fini (&slave);
+ status = _cairo_surface_set_error (&surface->base, status);
+ }
+}
+slim_hidden_def (cairo_tee_surface_add);
+
+void
+cairo_tee_surface_remove (cairo_surface_t *abstract_surface,
+ cairo_surface_t *target)
+{
+ cairo_tee_surface_t *surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (abstract_surface->backend != &cairo_tee_surface_backend) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ surface = (cairo_tee_surface_t *) abstract_surface;
+ if (target == surface->master.target) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_INDEX));
+ return;
+ }
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ if (slaves[n].target == target)
+ break;
+ }
+
+ if (n == num_slaves) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_INDEX));
+ return;
+ }
+
+ _cairo_surface_wrapper_fini (&slaves[n]);
+ for (n++; n < num_slaves; n++)
+ slaves[n-1] = slaves[n];
+ surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */
+}
+
+cairo_surface_t *
+cairo_tee_surface_index (cairo_surface_t *abstract_surface,
+ unsigned int index)
+{
+ cairo_tee_surface_t *surface;
+
+ if (unlikely (abstract_surface->status))
+ return _cairo_surface_create_in_error (abstract_surface->status);
+ if (unlikely (abstract_surface->finished))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ if (abstract_surface->backend != &cairo_tee_surface_backend)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+ surface = (cairo_tee_surface_t *) abstract_surface;
+ if (index == 0) {
+ return surface->master.target;
+ } else {
+ cairo_surface_wrapper_t *slave;
+
+ index--;
+
+ if (index >= _cairo_array_num_elements (&surface->slaves))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
+
+ slave = _cairo_array_index (&surface->slaves, index);
+ return slave->target;
+ }
+}
+
+cairo_surface_t *
+_cairo_tee_surface_find_match (void *abstract_surface,
+ const cairo_surface_backend_t *backend,
+ cairo_content_t content)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int num_slaves, n;
+
+ /* exact match first */
+ if (surface->master.target->backend == backend &&
+ surface->master.target->content == content)
+ {
+ return surface->master.target;
+ }
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ if (slaves[n].target->backend == backend &&
+ slaves[n].target->content == content)
+ {
+ return slaves[n].target;
+ }
+ }
+
+ /* matching backend? */
+ if (surface->master.target->backend == backend)
+ return surface->master.target;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ if (slaves[n].target->backend == backend)
+ return slaves[n].target;
+ }
+
+ return NULL;
+}
diff --git a/src/cairo-tee.h b/src/cairo-tee.h
new file mode 100755
index 000000000..9125a3a4a
--- /dev/null
+++ b/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,
+ unsigned int index);
+
+CAIRO_END_DECLS
+
+#else /*CAIRO_HAS_TEE_SURFACE*/
+# error Cairo was not compiled with support for the TEE backend
+#endif /*CAIRO_HAS_TEE_SURFACE*/
+
+#endif /*CAIRO_TEE_H*/
diff --git a/src/cairo-tg-allocator-private.h b/src/cairo-tg-allocator-private.h
new file mode 100755
index 000000000..f62b2e211
--- /dev/null
+++ b/src/cairo-tg-allocator-private.h
@@ -0,0 +1,134 @@
+#ifndef CAIRO_TG_ALLOCATOR_H
+#define CAIRO_TG_ALLOCATOR_H
+
+#include "cairoint.h"
+
+typedef struct _cairo_tg_mem_chunk cairo_tg_mem_chunk_t;
+
+struct _cairo_tg_mem_chunk
+{
+ cairo_tg_mem_chunk_t *next;
+ uint8_t *buffer;
+ int chunk_size;
+ int remaining_size;
+};
+
+typedef struct _cairo_tg_mono_allocator
+{
+ cairo_tg_mem_chunk_t *chunk_head;
+ int chunk_size;
+} cairo_tg_mono_allocator_t;
+
+static inline cairo_tg_mem_chunk_t *
+_cairo_tg_mem_chunk_create (int chunk_size)
+{
+ cairo_tg_mem_chunk_t *chunk;
+
+ chunk = (cairo_tg_mem_chunk_t *) malloc (sizeof (cairo_tg_mem_chunk_t) + chunk_size);
+
+ if (chunk)
+ {
+ chunk->next = NULL;
+ chunk->buffer = (uint8_t *) chunk + sizeof (cairo_tg_mem_chunk_t);
+ chunk->chunk_size = chunk_size;
+ chunk->remaining_size = chunk_size;
+ }
+
+ return chunk;
+}
+
+static inline void
+_cairo_tg_mem_chunk_destroy (cairo_tg_mem_chunk_t *chunk)
+{
+ free (chunk);
+}
+
+static inline cairo_status_t
+_cairo_tg_mono_allocator_init (cairo_tg_mono_allocator_t *allocator, int chunk_size)
+{
+ cairo_tg_mem_chunk_t *chunk;
+
+ chunk = _cairo_tg_mem_chunk_create (chunk_size);
+
+ if (! chunk)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ allocator->chunk_size = chunk_size;
+ allocator->chunk_head = chunk;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_cairo_tg_mono_allocator_fini (cairo_tg_mono_allocator_t *allocator)
+{
+ cairo_tg_mem_chunk_t *chunk = allocator->chunk_head, *next;
+
+ while (chunk != NULL)
+ {
+ next = chunk->next;
+ _cairo_tg_mem_chunk_destroy (chunk);
+ chunk = next;
+ }
+
+ allocator->chunk_head = NULL;
+}
+
+static inline void *
+_cairo_tg_mono_allocator_alloc (cairo_tg_mono_allocator_t *allocator, int size)
+{
+ cairo_tg_mem_chunk_t *chunk = allocator->chunk_head;
+ int chunk_size;
+
+ if (chunk && chunk->remaining_size >= size)
+ {
+ void *buffer = (void*)(chunk->buffer + chunk->chunk_size - chunk->remaining_size);
+ chunk->remaining_size -= size;
+ return buffer;
+ }
+
+ chunk_size = MAX (allocator->chunk_size, size);
+
+ chunk = _cairo_tg_mem_chunk_create (chunk_size);
+
+ if (chunk == NULL)
+ return NULL;
+
+ chunk->next = allocator->chunk_head;
+ chunk->buffer = (uint8_t *) chunk + sizeof (cairo_tg_mem_chunk_t);
+ chunk->chunk_size = chunk_size;
+ chunk->remaining_size = chunk_size - size;
+
+ allocator->chunk_head = chunk;
+
+ return (void *) chunk->buffer;
+}
+
+static inline void
+_cairo_tg_mono_allocator_reset (cairo_tg_mono_allocator_t *allocator)
+{
+ cairo_tg_mem_chunk_t *chunk = allocator->chunk_head, *next;
+ cairo_tg_mem_chunk_t *stock = NULL;
+
+ while (chunk != NULL)
+ {
+ next = chunk->next;
+
+ if (stock)
+ _cairo_tg_mem_chunk_destroy (chunk);
+ else
+ stock = chunk;
+
+ chunk = next;
+ }
+
+ if (stock)
+ {
+ stock->next = NULL;
+ stock->remaining_size = stock->chunk_size;
+ }
+
+ allocator->chunk_head = stock;
+}
+
+#endif /* CAIRO_TG_ALLOCATOR_H */
diff --git a/src/cairo-tg-composite-extents-private.h b/src/cairo-tg-composite-extents-private.h
new file mode 100755
index 000000000..22de139bf
--- /dev/null
+++ b/src/cairo-tg-composite-extents-private.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright © 2012 SCore 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.
+ *
+ * Author: Taekyun Kim (podain77@gmail.com)
+ */
+
+#ifndef CAIRO_TG_COMPOSITE_EXTENTS_PRIVATE_H
+#define CAIRO_TG_COMPOSITE_EXTENTS_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-pattern-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-surface-private.h"
+
+static inline void
+_cairo_tg_approximate_paint_extents (cairo_rectangle_int_t *extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ *extents = * (_cairo_clip_get_extents (clip));
+}
+
+static inline void
+_cairo_tg_approximate_mask_extents (cairo_rectangle_int_t *extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ *extents = * (_cairo_clip_get_extents (clip));
+}
+
+static inline void
+_cairo_tg_approximate_stroke_extents (cairo_rectangle_int_t *extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t rect;
+
+ *extents = * (_cairo_clip_get_extents (clip));
+
+ if (_cairo_operator_bounded_by_either (op))
+ {
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &rect);
+ _cairo_rectangle_intersect (extents, &rect);
+ }
+}
+
+static inline void
+_cairo_tg_approximate_fill_extents (cairo_rectangle_int_t *extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t rect;
+
+ *extents = * (_cairo_clip_get_extents (clip));
+
+ if (_cairo_operator_bounded_by_either (op))
+ {
+ _cairo_path_fixed_approximate_fill_extents (path, &rect);
+ _cairo_rectangle_intersect (extents, &rect);
+ }
+}
+
+static inline void
+_cairo_tg_approximate_glyphs_extents (cairo_rectangle_int_t *extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t rect;
+
+ *extents = * (_cairo_clip_get_extents (clip));
+
+ if (_cairo_operator_bounded_by_either (op))
+ {
+ if (_cairo_scaled_font_glyph_approximate_extents (scaled_font, glyphs, num_glyphs, &rect))
+ _cairo_rectangle_intersect (extents, &rect);
+ }
+}
+
+#endif /* CAIRO_TG_COMPOSITE_EXTENTS_PRIVATE_H */
diff --git a/src/cairo-tg-journal-private.h b/src/cairo-tg-journal-private.h
new file mode 100755
index 000000000..694f77cfd
--- /dev/null
+++ b/src/cairo-tg-journal-private.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright © 2012 SCore 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.
+ *
+ * Author: Taekyun Kim (podain77@gmail.com)
+ */
+
+#ifndef CAIRO_TG_JOURNAL_PRIVATE_H
+#define CAIRO_TG_JOURNAL_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-pattern-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-list-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-tg-allocator-private.h"
+#include "cairo-mutex-private.h"
+
+typedef enum
+{
+ CAIRO_TG_JOURNAL_ENTRY_PAINT,
+ CAIRO_TG_JOURNAL_ENTRY_MASK,
+ CAIRO_TG_JOURNAL_ENTRY_FILL,
+ CAIRO_TG_JOURNAL_ENTRY_STROKE,
+ CAIRO_TG_JOURNAL_ENTRY_GLYPHS,
+} cairo_tg_journal_entry_type_t;
+
+typedef struct _cairo_tg_journal_entry cairo_tg_journal_entry_t;
+
+struct _cairo_tg_journal_entry
+{
+ cairo_list_t link;
+ cairo_tg_journal_entry_type_t type;
+
+ cairo_rectangle_int_t extents;
+
+ cairo_operator_t op;
+ cairo_pattern_union_t source;
+ cairo_clip_t *clip;
+};
+
+typedef struct _cairo_tg_journal_entry_paint
+{
+ cairo_tg_journal_entry_t base;
+} cairo_tg_journal_entry_paint_t;
+
+typedef struct _cairo_tg_journal_entry_mask
+{
+ cairo_tg_journal_entry_t base;
+
+ cairo_pattern_union_t mask;
+} cairo_tg_journal_entry_mask_t;
+
+typedef struct _cairo_tg_journal_entry_stroke
+{
+ cairo_tg_journal_entry_t base;
+
+ 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_tg_journal_entry_stroke_t;
+
+typedef struct _cairo_tg_journal_entry_fill
+{
+ cairo_tg_journal_entry_t base;
+
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ double tolerance;
+ cairo_antialias_t antialias;
+} cairo_tg_journal_entry_fill_t;
+
+typedef struct _cairo_tg_journal_entry_glyphs
+{
+ cairo_tg_journal_entry_t base;
+
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+ cairo_scaled_font_t *scaled_font;
+} cairo_tg_journal_entry_glyphs_t;
+
+typedef struct _cairo_tg_journal
+{
+ cairo_rectangle_int_t extents;
+ cairo_list_t entry_list;
+ int num_entries;
+ cairo_tg_mono_allocator_t allocator;
+ cairo_mutex_t mutex;
+} cairo_tg_journal_t;
+
+typedef struct _cairo_tg_journal_replay_funcs
+{
+ cairo_int_status_t
+ (*paint) (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+ cairo_int_status_t
+ (*mask) (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+ cairo_int_status_t
+ (*stroke) (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+ cairo_int_status_t
+ (*fill) (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+ cairo_int_status_t
+ (*glyphs) (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+} cairo_tg_journal_replay_funcs_t;
+
+cairo_int_status_t
+_cairo_tg_journal_init (cairo_tg_journal_t *journal);
+
+void
+_cairo_tg_journal_fini (cairo_tg_journal_t *journal);
+
+void
+_cairo_tg_journal_lock (cairo_tg_journal_t *journal);
+
+void
+_cairo_tg_journal_unlock (cairo_tg_journal_t *journal);
+
+cairo_int_status_t
+_cairo_tg_journal_log_paint (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_int_status_t
+_cairo_tg_journal_log_mask (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_int_status_t
+_cairo_tg_journal_log_stroke (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_int_status_t
+_cairo_tg_journal_log_fill (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_int_status_t
+_cairo_tg_journal_log_glyphs (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+void
+_cairo_tg_journal_clear (cairo_tg_journal_t *journal);
+
+cairo_int_status_t
+_cairo_tg_journal_replay (const cairo_tg_journal_t *journal,
+ void *closure,
+ const cairo_rectangle_int_t *extents,
+ const cairo_tg_journal_replay_funcs_t *funcs);
+
+#endif /* CAIRO_TG_JOURNAL_PRIVATE_H */
diff --git a/src/cairo-tg-journal.c b/src/cairo-tg-journal.c
new file mode 100755
index 000000000..1d3902134
--- /dev/null
+++ b/src/cairo-tg-journal.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright © 2012 SCore 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.
+ *
+ * Author: Taekyun Kim (podain77@gmail.com)
+ */
+
+#include "cairo-tg.h"
+#include "cairo-tg-private.h"
+#include "cairo-tg-journal-private.h"
+#include "cairo-tg-allocator-private.h"
+#include "cairo-tg-composite-extents-private.h"
+
+static inline cairo_int_status_t
+_cairo_tg_journal_pattern_snapshot (cairo_pattern_t *dst,
+ const cairo_pattern_t *src)
+{
+ return _cairo_pattern_init_snapshot (dst, src);
+}
+
+/* Allocator for various types of journal entries. */
+static cairo_tg_journal_entry_t *
+_cairo_tg_journal_entry_alloc (cairo_tg_mono_allocator_t *allocator,
+ cairo_tg_journal_entry_type_t type)
+{
+ cairo_tg_journal_entry_t *entry = NULL;
+
+ switch (type)
+ {
+ case CAIRO_TG_JOURNAL_ENTRY_PAINT:
+ entry = _cairo_tg_mono_allocator_alloc (allocator,
+ sizeof (cairo_tg_journal_entry_paint_t));
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_MASK:
+ entry = _cairo_tg_mono_allocator_alloc (allocator,
+ sizeof (cairo_tg_journal_entry_mask_t));
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_STROKE:
+ entry = _cairo_tg_mono_allocator_alloc (allocator,
+ sizeof (cairo_tg_journal_entry_stroke_t));
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_FILL:
+ entry = _cairo_tg_mono_allocator_alloc (allocator,
+ sizeof (cairo_tg_journal_entry_fill_t));
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_GLYPHS:
+ entry = _cairo_tg_mono_allocator_alloc (allocator,
+ sizeof (cairo_tg_journal_entry_glyphs_t));
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ return NULL;
+ }
+
+ /* One should not change the type of an entry.
+ * It is determined at the moment of allocation. */
+ entry->type = type;
+
+ return entry;
+}
+
+static void
+_cairo_tg_journal_entry_fini (cairo_tg_journal_entry_t *entry)
+{
+ /* common part. */
+ _cairo_pattern_fini (&entry->source.base);
+
+ if (entry->clip)
+ _cairo_clip_destroy (entry->clip);
+
+ /* For each entry types... */
+ switch (entry->type)
+ {
+ case CAIRO_TG_JOURNAL_ENTRY_PAINT:
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_MASK:
+ {
+ cairo_tg_journal_entry_mask_t *entry_mask =
+ (cairo_tg_journal_entry_mask_t *) entry;
+
+ _cairo_pattern_fini (&entry_mask->mask.base);
+ }
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_STROKE:
+ {
+ cairo_tg_journal_entry_stroke_t *entry_stroke =
+ (cairo_tg_journal_entry_stroke_t *) entry;
+
+ _cairo_path_fixed_fini (&entry_stroke->path);
+ _cairo_stroke_style_fini (&entry_stroke->style);
+ }
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_FILL:
+ {
+ cairo_tg_journal_entry_fill_t *entry_fill =
+ (cairo_tg_journal_entry_fill_t *) entry;
+
+ _cairo_path_fixed_fini (&entry_fill->path);
+ }
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_GLYPHS:
+ {
+ cairo_tg_journal_entry_glyphs_t *entry_glyphs =
+ (cairo_tg_journal_entry_glyphs_t *) entry;
+
+ free (entry_glyphs->glyphs);
+ cairo_scaled_font_destroy (entry_glyphs->scaled_font);
+ }
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+}
+
+cairo_int_status_t
+_cairo_tg_journal_init (cairo_tg_journal_t *journal)
+{
+ cairo_int_status_t status;
+
+ CAIRO_MUTEX_INIT (journal->mutex);
+ journal->num_entries = 0;
+ cairo_list_init (&journal->entry_list);
+ journal->extents = _cairo_empty_rectangle;
+
+ status = _cairo_tg_mono_allocator_init (&journal->allocator, 4096 -
+ sizeof (cairo_tg_mem_chunk_t));
+
+ return status;
+}
+
+void
+_cairo_tg_journal_fini (cairo_tg_journal_t *journal)
+{
+ cairo_tg_journal_entry_t *entry;
+ cairo_tg_journal_entry_t *next;
+
+ CAIRO_MUTEX_FINI (journal->mutex);
+ cairo_list_foreach_entry_safe (entry, next, cairo_tg_journal_entry_t,
+ &journal->entry_list, link)
+ {
+ _cairo_tg_journal_entry_fini (entry);
+ }
+
+ _cairo_tg_mono_allocator_fini (&journal->allocator);
+}
+
+void
+_cairo_tg_journal_lock (cairo_tg_journal_t *journal)
+{
+ CAIRO_MUTEX_LOCK (journal->mutex);
+}
+
+void
+_cairo_tg_journal_unlock (cairo_tg_journal_t *journal)
+{
+ CAIRO_MUTEX_UNLOCK (journal->mutex);
+}
+
+cairo_int_status_t
+_cairo_tg_journal_log_paint (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_tg_journal_entry_paint_t *entry;
+
+ entry = (cairo_tg_journal_entry_paint_t *)
+ _cairo_tg_journal_entry_alloc (&journal->allocator, CAIRO_TG_JOURNAL_ENTRY_PAINT);
+
+ if (unlikely (entry == NULL))
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ status = _cairo_tg_journal_pattern_snapshot (&entry->base.source.base, source);
+
+ if (unlikely (status))
+ return status;
+
+ entry->base.op = op;
+ entry->base.clip = _cairo_clip_copy (clip);
+ _cairo_tg_approximate_paint_extents (&entry->base.extents, op, source, clip);
+ _cairo_rectangle_union (&journal->extents, &entry->base.extents);
+
+ cairo_list_add_tail (&entry->base.link, &journal->entry_list);
+ journal->num_entries++;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_tg_journal_log_mask (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_tg_journal_entry_mask_t *entry;
+
+ entry = (cairo_tg_journal_entry_mask_t *)
+ _cairo_tg_journal_entry_alloc (&journal->allocator, CAIRO_TG_JOURNAL_ENTRY_MASK);
+
+ if (unlikely (entry == NULL))
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ status = _cairo_tg_journal_pattern_snapshot (&entry->base.source.base, source);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tg_journal_pattern_snapshot (&entry->mask.base, mask);
+
+ if (unlikely (status))
+ {
+ _cairo_pattern_fini (&entry->base.source.base);
+ return status;
+ }
+
+ entry->base.op = op;
+ entry->base.clip = _cairo_clip_copy (clip);
+ _cairo_tg_approximate_mask_extents (&entry->base.extents, op, source, mask, clip);
+ _cairo_rectangle_union (&journal->extents, &entry->base.extents);
+
+ cairo_list_add_tail (&entry->base.link, &journal->entry_list);
+ journal->num_entries++;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_tg_journal_log_stroke (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_tg_journal_entry_stroke_t *entry;
+
+ entry = (cairo_tg_journal_entry_stroke_t *)
+ _cairo_tg_journal_entry_alloc (&journal->allocator, CAIRO_TG_JOURNAL_ENTRY_STROKE);
+
+ if (unlikely (entry == NULL))
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ status = _cairo_tg_journal_pattern_snapshot (&entry->base.source.base, source);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_init_copy (&entry->path, path);
+
+ if (unlikely (status))
+ {
+ _cairo_pattern_fini (&entry->base.source.base);
+ return status;
+ }
+
+ status = _cairo_stroke_style_init_copy (&entry->style, style);
+
+ if (unlikely (status))
+ {
+ _cairo_path_fixed_fini (&entry->path);
+ _cairo_pattern_fini (&entry->base.source.base);
+ return status;
+ }
+
+ entry->base.op = op;
+ entry->base.clip = _cairo_clip_copy (clip);
+ entry->ctm = *ctm;
+ entry->ctm_inverse = *ctm_inverse;
+ entry->tolerance = tolerance;
+ entry->antialias = antialias;
+ _cairo_tg_approximate_stroke_extents (&entry->base.extents, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+ _cairo_rectangle_union (&journal->extents, &entry->base.extents);
+
+ cairo_list_add_tail (&entry->base.link, &journal->entry_list);
+ journal->num_entries++;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_tg_journal_log_fill (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_tg_journal_entry_fill_t *entry;
+
+ entry = (cairo_tg_journal_entry_fill_t *)
+ _cairo_tg_journal_entry_alloc (&journal->allocator, CAIRO_TG_JOURNAL_ENTRY_FILL);
+
+ if (unlikely (entry == NULL))
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ status = _cairo_tg_journal_pattern_snapshot (&entry->base.source.base, source);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_init_copy (&entry->path, path);
+
+ if (unlikely (status))
+ {
+ _cairo_pattern_fini (&entry->base.source.base);
+ return status;
+ }
+
+ entry->base.op = op;
+ entry->base.clip = _cairo_clip_copy (clip);
+ entry->fill_rule = fill_rule;
+ entry->tolerance = tolerance;
+ entry->antialias = antialias;
+ _cairo_tg_approximate_fill_extents (&entry->base.extents, op, source,
+ path, fill_rule, tolerance, antialias, clip);
+ _cairo_rectangle_union (&journal->extents, &entry->base.extents);
+
+ cairo_list_add_tail (&entry->base.link, &journal->entry_list);
+ journal->num_entries++;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_tg_journal_log_glyphs (cairo_tg_journal_t *journal,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+ cairo_tg_journal_entry_glyphs_t *entry;
+
+ entry = (cairo_tg_journal_entry_glyphs_t *)
+ _cairo_tg_journal_entry_alloc (&journal->allocator, CAIRO_TG_JOURNAL_ENTRY_GLYPHS);
+
+ if (unlikely (entry == NULL))
+ return CAIRO_INT_STATUS_NO_MEMORY;
+
+ status = _cairo_tg_journal_pattern_snapshot (&entry->base.source.base, source);
+
+ if (unlikely (status))
+ return status;
+
+ entry->scaled_font = cairo_scaled_font_reference (scaled_font);
+
+ if (unlikely (entry->scaled_font == NULL))
+ {
+ _cairo_pattern_fini (&entry->base.source.base);
+ return CAIRO_INT_STATUS_NO_MEMORY;
+ }
+
+ if (num_glyphs)
+ {
+ entry->glyphs = malloc (sizeof (cairo_glyph_t) * num_glyphs);
+
+ if (unlikely (entry->glyphs == NULL))
+ {
+ cairo_scaled_font_destroy (entry->scaled_font);
+ _cairo_pattern_fini (&entry->base.source.base);
+ return CAIRO_INT_STATUS_NO_MEMORY;
+ }
+
+ memcpy (entry->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+ }
+ else
+ {
+ entry->glyphs = NULL;
+ }
+
+ entry->num_glyphs = num_glyphs;
+ entry->base.op = op;
+ entry->base.clip = _cairo_clip_copy (clip);
+ _cairo_tg_approximate_glyphs_extents (&entry->base.extents, op, source,
+ glyphs, num_glyphs, scaled_font, clip);
+ _cairo_rectangle_union (&journal->extents, &entry->base.extents);
+
+ cairo_list_add_tail (&entry->base.link, &journal->entry_list);
+ journal->num_entries++;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+void
+_cairo_tg_journal_clear (cairo_tg_journal_t *journal)
+{
+ cairo_tg_journal_entry_t *entry;
+ cairo_tg_journal_entry_t *next;
+
+ cairo_list_foreach_entry_safe (entry, next, cairo_tg_journal_entry_t,
+ &journal->entry_list, link)
+ {
+ _cairo_tg_journal_entry_fini (entry);
+ }
+
+ journal->num_entries = 0;
+ cairo_list_init (&journal->entry_list);
+ _cairo_tg_mono_allocator_reset(&journal->allocator);
+ journal->extents = _cairo_empty_rectangle;
+}
+
+cairo_int_status_t
+_cairo_tg_journal_replay (const cairo_tg_journal_t *journal,
+ void *closure,
+ const cairo_rectangle_int_t *extents,
+ const cairo_tg_journal_replay_funcs_t *funcs)
+{
+ const cairo_tg_journal_entry_t *entry;
+ const cairo_tg_journal_entry_t *next;
+ cairo_int_status_t status;
+
+ cairo_list_foreach_entry_safe (entry, next, cairo_tg_journal_entry_t,
+ &journal->entry_list, link)
+ {
+ if (extents && ! _cairo_rectangle_intersects (extents, &entry->extents))
+ {
+ continue;
+ }
+
+ switch (entry->type)
+ {
+ case CAIRO_TG_JOURNAL_ENTRY_PAINT:
+ {
+ cairo_tg_journal_entry_paint_t *e =
+ (cairo_tg_journal_entry_paint_t *) entry;
+
+ status = funcs->paint (closure, e->base.op, &e->base.source.base,
+ e->base.clip);
+ }
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_MASK:
+ {
+ cairo_tg_journal_entry_mask_t *e =
+ (cairo_tg_journal_entry_mask_t *) entry;
+
+ status = funcs->mask (closure, e->base.op, &e->base.source.base,
+ &e->mask.base, e->base.clip);
+ }
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_STROKE:
+ {
+ cairo_tg_journal_entry_stroke_t *e =
+ (cairo_tg_journal_entry_stroke_t *) entry;
+
+ status = funcs->stroke (closure, e->base.op, &e->base.source.base,
+ &e->path, &e->style, &e->ctm, &e->ctm_inverse,
+ e->tolerance, e->antialias, e->base.clip);
+ }
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_FILL:
+ {
+ cairo_tg_journal_entry_fill_t *e =
+ (cairo_tg_journal_entry_fill_t *) entry;
+
+ status = funcs->fill (closure, e->base.op, &e->base.source.base,
+ &e->path, e->fill_rule,
+ e->tolerance, e->antialias, e->base.clip);
+ }
+ break;
+ case CAIRO_TG_JOURNAL_ENTRY_GLYPHS:
+ {
+ cairo_tg_journal_entry_glyphs_t *e =
+ (cairo_tg_journal_entry_glyphs_t *) entry;
+
+ status = funcs->glyphs (closure, e->base.op, &e->base.source.base,
+ e->glyphs, e->num_glyphs,
+ e->scaled_font, e->base.clip);
+ }
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+
+ if (unlikely (status) && status != CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ assert (0);
+ return status;
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
diff --git a/src/cairo-tg-private.h b/src/cairo-tg-private.h
new file mode 100755
index 000000000..6e2464ea4
--- /dev/null
+++ b/src/cairo-tg-private.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2012 SCore 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.
+ *
+ * Author: Taekyun Kim (podain77@gmail.com)
+ */
+
+#ifndef CAIRO_TG_PRIVATE_H
+#define CAIRO_TG_PRIVATE_H
+
+#include "cairo-default-context-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-tg-journal-private.h"
+#include <pixman.h>
+
+#define CAIRO_TG_NUM_MAX_TILES 8
+
+typedef struct _cairo_tg_surface
+{
+ cairo_surface_t base;
+
+ cairo_format_t format;
+ pixman_format_code_t pixman_format;
+ unsigned char *data;
+ int width;
+ int height;
+ int stride;
+ int bpp;
+
+ cairo_surface_t *image_surface;
+ cairo_surface_t *tile_surfaces[CAIRO_TG_NUM_MAX_TILES];
+ cairo_tg_journal_t journal;
+} cairo_tg_surface_t;
+
+#endif /* CAIRO_TG_PRIVATE_H */
diff --git a/src/cairo-tg-surface.c b/src/cairo-tg-surface.c
new file mode 100755
index 000000000..c32799fa4
--- /dev/null
+++ b/src/cairo-tg-surface.c
@@ -0,0 +1,1372 @@
+/*
+ * Copyright © 2012 SCore 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.
+ *
+ * Author: Taekyun Kim (podain77@gmail.com)
+ */
+
+#include "cairoint.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-tg.h"
+#include "cairo-tg-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-surface-subsurface-inline.h"
+#include "cairo-compositor-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-recording-surface-inline.h"
+
+#if CAIRO_HAS_OPENMP
+#include <omp.h>
+#endif
+
+#define CAIRO_TG_THREAD_POOL_BUSY_WAIT
+#define CAIRO_TG_NUM_MIN_ENTRIES_FOR_PARALLEL_FLUSH 2
+
+static inline int
+get_num_cpu_cores (void)
+{
+ static int num_cpu_cores = 0;
+
+ if (num_cpu_cores == 0)
+ {
+#if CAIRO_HAS_OPENMP
+ num_cpu_cores = omp_get_num_procs ();
+#elif defined (__WIN32)
+ SYSTEM_INFO sysinfo;
+
+ GetSystemInfo (&sysinfo);
+ num_cpu_cores = sysinfo.dwNumberOfProcessors;
+#elif defined (__linux__)
+ cpu_set_t cs;
+ int i;
+
+ CPU_ZERO (&cs);
+
+ if (sched_getaffinity (0, sizeof (cs), &cs) != 0)
+ num_cpu_cores = 1;
+
+ for (i = 0; i < 8; i++)
+ {
+ if (CPU_ISSET (i, &cs))
+ num_cpu_cores++;
+ }
+#else
+ num_cpu_cores = 1;
+#endif
+ }
+
+ return num_cpu_cores;
+}
+
+static inline int
+get_bpp_for_format (cairo_format_t format)
+{
+ switch (format)
+ {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_RGB30:
+ 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 inline cairo_bool_t
+_cairo_surface_is_tg(const cairo_surface_t *surface)
+{
+ return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_TG;
+}
+
+static inline cairo_bool_t
+_cairo_tg_surface_is_size_valid (int width, int height)
+{
+ if (width < 0 || height < 0)
+ return FALSE;
+
+ /* TODO: Check for upper limit of surface size. */
+
+ return TRUE;
+}
+
+static inline cairo_bool_t
+_cairo_pattern_is_self_copy (cairo_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ if (unlikely (surface == NULL))
+ return FALSE;
+
+ if (unlikely (pattern == NULL))
+ return FALSE;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE )
+ {
+ cairo_surface_t *pattern_surface =
+ ((cairo_surface_pattern_t *) pattern)->surface;
+
+ while (_cairo_surface_is_subsurface (pattern_surface))
+ {
+ pattern_surface =
+ _cairo_surface_subsurface_get_target (pattern_surface);
+ }
+
+ return pattern_surface == surface;
+ }
+
+ return FALSE;
+}
+
+static inline cairo_bool_t
+_cairo_pattern_is_recording (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return FALSE;
+
+ surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+ return _cairo_surface_is_recording (surface);
+}
+
+static inline cairo_bool_t
+_cairo_tg_surface_owns_data (cairo_tg_surface_t *surface)
+{
+ return ((cairo_image_surface_t *) surface->image_surface)->owns_data;
+}
+
+static inline cairo_int_status_t
+_cairo_tg_image_surface_paint (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = (cairo_image_surface_t *) closure;
+ cairo_int_status_t status;
+
+ status = _cairo_surface_begin_modification (&surface->base);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_compositor_paint (surface->compositor, &surface->base,
+ op, source, clip);
+
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ surface->base.is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
+ surface->base.serial++;
+ }
+
+ return status;
+}
+
+static inline cairo_int_status_t
+_cairo_tg_image_surface_mask (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = (cairo_image_surface_t *) closure;
+ cairo_int_status_t status;
+
+ status = _cairo_surface_begin_modification (&surface->base);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_compositor_mask (surface->compositor, &surface->base,
+ op, source, mask, clip);
+
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ surface->base.is_clear = FALSE;
+ surface->base.serial++;
+ }
+
+ return status;
+}
+
+static inline cairo_int_status_t
+_cairo_tg_image_surface_stroke (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = (cairo_image_surface_t *) closure;
+ cairo_int_status_t status;
+
+ status = _cairo_surface_begin_modification (&surface->base);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_compositor_stroke (surface->compositor, &surface->base,
+ op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ surface->base.is_clear = FALSE;
+ surface->base.serial++;
+ }
+
+ return status;
+}
+
+
+static inline cairo_int_status_t
+_cairo_tg_image_surface_fill (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = (cairo_image_surface_t *) closure;
+ cairo_int_status_t status;
+
+ status = _cairo_surface_begin_modification (&surface->base);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_compositor_fill (surface->compositor, &surface->base,
+ op, source, path,
+ fill_rule, tolerance, antialias, clip);
+
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ surface->base.is_clear = FALSE;
+ surface->base.serial++;
+ }
+
+ return status;
+}
+
+static inline cairo_int_status_t
+_cairo_tg_image_surface_glyphs (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = (cairo_image_surface_t *) closure;
+ cairo_int_status_t status;
+
+ status = _cairo_surface_begin_modification (&surface->base);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_compositor_glyphs (surface->compositor, &surface->base,
+ op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ surface->base.is_clear = FALSE;
+ surface->base.serial++;
+ }
+
+ return status;
+}
+
+const cairo_tg_journal_replay_funcs_t replay_funcs_image_fallback =
+{
+ _cairo_tg_image_surface_paint,
+ _cairo_tg_image_surface_mask,
+ _cairo_tg_image_surface_stroke,
+ _cairo_tg_image_surface_fill,
+ _cairo_tg_image_surface_glyphs,
+};
+
+typedef struct _cairo_tg_surface_tile
+{
+ cairo_surface_t *surface;
+ cairo_rectangle_int_t tile_rect;
+} cairo_tg_surface_tile_t;
+
+static inline int
+_cairo_tg_surface_tiles_init (cairo_tg_surface_t *surface,
+ const cairo_rectangle_int_t *extents,
+ int num_tiles,
+ cairo_tg_surface_tile_t *tiles)
+{
+ int tile_height;
+ int i;
+
+ if (extents->height <= 0)
+ return 0;
+
+ if (extents->height <= num_tiles)
+ num_tiles = extents->height;
+
+ tile_height = extents->height / num_tiles;
+
+ for (i = 0; i < num_tiles; i++)
+ {
+ tiles[i].surface = surface->tile_surfaces[i];
+ tiles[i].tile_rect.x = extents->x;
+ tiles[i].tile_rect.y = extents->y + i * tile_height;
+ tiles[i].tile_rect.width = extents->width;
+ tiles[i].tile_rect.height = tile_height;
+ }
+
+ tiles[num_tiles - 1].tile_rect.height = extents->height - i * (num_tiles - 1);
+
+ return num_tiles;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_tile_paint (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure;
+ cairo_clip_t *tile_clip;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect);
+
+ if (! _cairo_clip_is_all_clipped (tile_clip))
+ status = _cairo_tg_image_surface_paint (tile->surface, op, source, tile_clip);
+
+ _cairo_clip_destroy (tile_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_tile_mask (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure;
+ cairo_clip_t *tile_clip;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect);
+
+ if (! _cairo_clip_is_all_clipped (tile_clip))
+ {
+ status = _cairo_tg_image_surface_mask (tile->surface, op, source,
+ mask, tile_clip);
+ }
+
+ _cairo_clip_destroy (tile_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_tile_stroke (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure;
+ cairo_clip_t *tile_clip;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect);
+
+ if (! _cairo_clip_is_all_clipped (tile_clip))
+ {
+ status = _cairo_tg_image_surface_stroke (tile->surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, tile_clip);
+ }
+
+ _cairo_clip_destroy (tile_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_tile_fill (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure;
+ cairo_clip_t *tile_clip;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect);
+
+ if (! _cairo_clip_is_all_clipped (tile_clip))
+ {
+ status = _cairo_tg_image_surface_fill (tile->surface, op, source,
+ path, fill_rule, tolerance,
+ antialias, tile_clip);
+ }
+
+ _cairo_clip_destroy (tile_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_tile_glyphs (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure;
+ cairo_clip_t *tile_clip;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect);
+
+ if (! _cairo_clip_is_all_clipped (tile_clip))
+ {
+ status = _cairo_tg_image_surface_glyphs (tile->surface, op, source,
+ glyphs, num_glyphs, scaled_font,
+ tile_clip);
+ }
+
+ _cairo_clip_destroy (tile_clip);
+
+ return status;
+}
+
+const cairo_tg_journal_replay_funcs_t replay_funcs_tile =
+{
+ _cairo_tg_surface_tile_paint,
+ _cairo_tg_surface_tile_mask,
+ _cairo_tg_surface_tile_stroke,
+ _cairo_tg_surface_tile_fill,
+ _cairo_tg_surface_tile_glyphs,
+};
+
+#if ! CAIRO_HAS_OPENMP
+#define CAIRO_TG_NUM_MAX_WORKERS CAIRO_TG_NUM_MAX_TILES
+
+typedef enum _cairo_tg_worker_status
+{
+ CAIRO_TG_WORKER_STATUS_IDLE, /* can transit to either OCCUPIED or KILLED */
+ CAIRO_TG_WORKER_STATUS_TO_DO, /* only can transit to IDLE state */
+ CAIRO_TG_WORKER_STATUS_KILLED, /* worker will be no longer valid */
+} cairo_tg_worker_status_t;
+
+typedef struct _cairo_tg_worker
+{
+ cairo_tg_journal_t *journal;
+ cairo_tg_surface_tile_t *tile;
+
+ pthread_t thread;
+ pthread_mutex_t lock;
+ pthread_cond_t cond_wake_up;
+ cairo_tg_worker_status_t status;
+
+#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT
+ pthread_spinlock_t spinlock;
+#else
+ pthread_cond_t cond_done;
+#endif
+} cairo_tg_worker_t;
+
+cairo_tg_worker_t workers[CAIRO_TG_NUM_MAX_WORKERS];
+
+pthread_mutex_t workers_lock;
+cairo_bool_t workers_occupied;
+
+static void *
+_cairo_tg_worker_mainloop (void *arg)
+{
+ cairo_tg_worker_t *worker = (cairo_tg_worker_t *) arg;
+
+ while (1)
+ {
+ pthread_mutex_lock (&worker->lock);
+
+ while (worker->status == CAIRO_TG_WORKER_STATUS_IDLE)
+ pthread_cond_wait (&worker->cond_wake_up, &worker->lock);
+
+ /* Here, worker is kicked off to do some action. */
+
+ if (worker->status == CAIRO_TG_WORKER_STATUS_KILLED)
+ {
+ /* Worker is killed, so release mutex and exit. */
+ pthread_mutex_unlock (&worker->lock);
+ pthread_exit (NULL);
+ }
+
+ assert (worker->status == CAIRO_TG_WORKER_STATUS_TO_DO);
+
+ _cairo_tg_journal_replay (worker->journal, (void *)worker->tile,
+ &worker->tile->tile_rect, &replay_funcs_tile);
+
+ worker->status = CAIRO_TG_WORKER_STATUS_IDLE;
+
+#ifndef CAIRO_TG_THREAD_POOL_BUSY_WAIT
+ pthread_cond_signal (&worker->cond_done);
+#endif
+
+ pthread_mutex_unlock (&worker->lock);
+ }
+
+ return NULL;
+}
+
+static void
+_cairo_tg_workers_init (void)
+{
+ int i;
+
+ for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++)
+ {
+ workers[i].status = CAIRO_TG_WORKER_STATUS_IDLE;
+
+ pthread_mutex_init (&workers[i].lock, NULL);
+ pthread_cond_init (&workers[i].cond_wake_up, NULL);
+
+#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT
+ pthread_spin_init (&workers[i].spinlock, 0);
+#else
+ pthread_cond_init (&workers[i].cond_done, NULL);
+#endif
+
+ pthread_create (&workers[i].thread, NULL, _cairo_tg_worker_mainloop, (void *) &workers[i]);
+ }
+
+ pthread_mutex_init (&workers_lock, NULL);
+ workers_occupied = FALSE;
+}
+
+static void
+_cairo_tg_workers_fini (void)
+{
+ int i;
+
+ for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++)
+ {
+ pthread_mutex_lock (&workers[i].lock);
+
+ workers[i].status = CAIRO_TG_WORKER_STATUS_KILLED;
+ pthread_cond_signal (&workers[i].cond_wake_up);
+ pthread_mutex_unlock (&workers[i].lock);
+ }
+
+ for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++)
+ pthread_join (workers[i].thread, NULL);
+
+ for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++)
+ {
+ pthread_mutex_destroy (&workers[i].lock);
+ pthread_cond_destroy (&workers[i].cond_wake_up);
+
+#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT
+ pthread_spin_destroy (&workers[i].spinlock);
+#else
+ pthread_cond_destroy (&workers[i].cond_done);
+#endif
+ }
+}
+
+static void __attribute__((constructor))
+_cairo_tg_constructor (void)
+{
+ pthread_atfork (NULL, NULL, _cairo_tg_workers_init);
+ _cairo_tg_workers_init ();
+}
+
+static void __attribute__((destructor))
+_cairo_tg_destructor (void)
+{
+ _cairo_tg_workers_fini ();
+}
+
+#endif /* ! CAIRO_HAS_OPENMP */
+
+static void
+_cairo_tg_surface_prepare_flush_parallel (cairo_tg_surface_t *surface)
+{
+ const cairo_tg_journal_entry_t *entry;
+ const cairo_tg_journal_entry_t *next;
+
+ cairo_list_foreach_entry_safe (entry, next, cairo_tg_journal_entry_t,
+ &surface->journal.entry_list, link)
+ {
+ if (entry->source.base.type == CAIRO_PATTERN_TYPE_SURFACE)
+ {
+ cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) (&entry->source.base);
+ cairo_surface_flush (pattern->surface);
+ }
+
+ if (entry->type == CAIRO_TG_JOURNAL_ENTRY_MASK)
+ {
+ cairo_tg_journal_entry_mask_t *e =
+ (cairo_tg_journal_entry_mask_t *) entry;
+
+ if (e->mask.base.type == CAIRO_PATTERN_TYPE_SURFACE)
+ {
+ cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) (&e->mask.base);
+ cairo_surface_flush (pattern->surface);
+ }
+ }
+ }
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_flush_parallel (cairo_tg_surface_t *surface)
+{
+ int num_tiles, i;
+ cairo_tg_surface_tile_t tiles[CAIRO_TG_NUM_MAX_TILES];
+ cairo_rectangle_int_t extents;
+
+ if (surface->journal.num_entries < CAIRO_TG_NUM_MIN_ENTRIES_FOR_PARALLEL_FLUSH)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_tg_surface_prepare_flush_parallel (surface);
+
+ extents.x = 0;
+ extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ _cairo_rectangle_intersect (&extents, &surface->journal.extents);
+
+ num_tiles = get_num_cpu_cores ();
+
+#if ! CAIRO_HAS_OPENMP
+ if (num_tiles > CAIRO_TG_NUM_MAX_WORKERS)
+ num_tiles = CAIRO_TG_NUM_MAX_WORKERS;
+#endif
+
+ num_tiles = _cairo_tg_surface_tiles_init (surface, &extents, num_tiles, &tiles[0]);
+
+#if CAIRO_HAS_OPENMP
+ #pragma omp parallel for
+ for (i = 0; i < num_tiles; i++)
+ {
+ _cairo_tg_journal_replay (&surface->journal, (void *) &tiles[i],
+ &tiles[i].tile_rect, &replay_funcs_tile);
+ }
+#else
+ pthread_mutex_lock (&workers_lock);
+
+ if (workers_occupied)
+ {
+ pthread_mutex_unlock (&workers_lock);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ workers_occupied = TRUE;
+ pthread_mutex_unlock (&workers_lock);
+
+ /* Kick workers to start. */
+ for (i = 0; i < num_tiles - 1; i++)
+ {
+ pthread_mutex_lock (&workers[i].lock);
+
+ workers[i].status = CAIRO_TG_WORKER_STATUS_TO_DO;
+ workers[i].journal = &surface->journal;
+ workers[i].tile = &tiles[i];
+
+ pthread_cond_signal (&workers[i].cond_wake_up);
+ pthread_mutex_unlock (&workers[i].lock);
+ }
+
+ _cairo_tg_journal_replay (&surface->journal, &tiles[num_tiles - 1],
+ &tiles[num_tiles - 1].tile_rect, &replay_funcs_tile);
+
+ /* Wait for workers to finish. */
+ for (i = 0; i < num_tiles - 1; i++)
+ {
+#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT
+ pthread_spin_lock (&workers[i].spinlock);
+
+ while (workers[i].status == CAIRO_TG_WORKER_STATUS_TO_DO)
+ {
+ pthread_spin_unlock (&workers[i].spinlock);
+ pthread_spin_lock (&workers[i].spinlock);
+ }
+
+ pthread_spin_unlock (&workers[i].spinlock);
+#else
+ pthread_mutex_lock (&workers[i].lock);
+
+ while (workers[i].status == CAIRO_TG_WORKER_STATUS_TO_DO)
+ pthread_cond_wait (&workers[i].cond_done, &workers[i].lock);
+
+ pthread_mutex_unlock (&workers[i].lock);
+#endif
+ }
+
+ /* Release thread pool. */
+ pthread_mutex_lock (&workers_lock);
+ workers_occupied = FALSE;
+ pthread_mutex_unlock (&workers_lock);
+#endif
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tg_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_tg_journal_lock (&surface->journal);
+
+ if (surface->journal.num_entries)
+ {
+ status = _cairo_tg_surface_flush_parallel (surface);
+
+ if (status)
+ {
+ status = _cairo_tg_journal_replay (&surface->journal,
+ (void *) surface->image_surface,
+ NULL, &replay_funcs_image_fallback);
+ }
+
+ _cairo_tg_journal_clear (&surface->journal);
+ }
+
+ _cairo_tg_journal_unlock (&surface->journal);
+
+ return status;
+}
+
+static cairo_image_surface_t *
+_cairo_tg_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_tg_surface_t *other = abstract_surface;
+ cairo_surface_t *surface;
+ uint8_t *buffer;
+
+ _cairo_tg_surface_flush (other, 0);
+
+ buffer = other->data;
+ buffer += extents->y * other->stride;
+ buffer += extents->x * other->bpp / 8;
+
+ surface =
+ _cairo_image_surface_create_with_pixman_format (buffer,
+ other->pixman_format,
+ extents->width,
+ extents->height,
+ other->stride);
+
+ if (unlikely (surface == NULL))
+ return NULL;
+
+ cairo_surface_set_device_offset (surface, -extents->x, extents->y);
+
+ return (cairo_image_surface_t *) surface;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_tg_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+
+ extents->x = 0;
+ extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_pattern_is_self_copy (&surface->base, source) &&
+ ! _cairo_pattern_is_recording (source))
+ status = _cairo_tg_journal_log_paint (&surface->journal, op, source, clip);
+
+ if (status)
+ {
+ status = _cairo_tg_surface_flush (surface, 0);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tg_image_surface_paint (surface->image_surface, op, source, clip);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_pattern_is_self_copy (&surface->base, source) &&
+ ! _cairo_pattern_is_self_copy (&surface->base, mask) &&
+ ! _cairo_pattern_is_recording (source))
+ status = _cairo_tg_journal_log_mask (&surface->journal, op, source, mask, clip);
+
+ if (status)
+ {
+ status = _cairo_tg_surface_flush (surface, 0);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tg_image_surface_mask (surface->image_surface, op, source,
+ mask, clip);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_pattern_is_self_copy (&surface->base, source) &&
+ ! _cairo_pattern_is_recording (source))
+ {
+ status = _cairo_tg_journal_log_stroke (&surface->journal, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+ }
+
+ if (status)
+ {
+ status = _cairo_tg_surface_flush (surface, 0);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tg_image_surface_stroke (surface->image_surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_pattern_is_self_copy (&surface->base, source) &&
+ ! _cairo_pattern_is_recording (source))
+ {
+ status = _cairo_tg_journal_log_fill (&surface->journal, op, source,
+ path, fill_rule, tolerance, antialias, clip);
+ }
+
+ if (status)
+ {
+ status = _cairo_tg_surface_flush (surface, 0);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tg_image_surface_fill (surface->image_surface, op, source,
+ path, fill_rule, tolerance, antialias, clip);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_pattern_is_self_copy (&surface->base, source) &&
+ ! _cairo_pattern_is_recording (source))
+ {
+ status = _cairo_tg_journal_log_glyphs (&surface->journal, op, source,
+ glyphs, num_glyphs, scaled_font, clip);
+ }
+
+ if (status)
+ {
+ status = _cairo_tg_surface_flush (surface, 0);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tg_image_surface_glyphs (surface->image_surface, op, source,
+ glyphs, num_glyphs, scaled_font, clip);
+ }
+
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_tg_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_tg_surface_t *other = abstract_other;
+
+ if (! _cairo_tg_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_tg_surface_create (other->format, width, height);
+
+ return cairo_tg_surface_create (_cairo_format_from_content (content), width, height);
+}
+
+static cairo_surface_t *
+_cairo_tg_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+
+ if (extents)
+ {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+static cairo_status_t
+_cairo_tg_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+
+ _cairo_tg_surface_flush (surface, 0);
+
+ *image_out = (cairo_image_surface_t *) surface->image_surface;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_tg_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ /* Do nothing */
+}
+
+static cairo_surface_t *
+_cairo_tg_surface_snapshot (void *abstract_surface)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+ cairo_tg_surface_t *clone;
+
+ _cairo_tg_surface_flush (surface, 0);
+
+ if (_cairo_tg_surface_owns_data (surface) && surface->base._finishing)
+ {
+ return cairo_tg_surface_create_for_data (surface->data, surface->format,
+ surface->width, surface->height,
+ surface->stride);
+ }
+
+ clone = (cairo_tg_surface_t *)
+ cairo_tg_surface_create (surface->format, surface->width, surface->height);
+
+ if (unlikely (clone->base.status))
+ return &clone->base;
+
+ if (surface->stride == clone->stride)
+ {
+ memcpy (clone->data, surface->data, clone->stride * clone->height);
+ }
+ else
+ {
+ unsigned char *dst = clone->data;
+ unsigned char *src = surface->data;
+ int i;
+ int stride = clone->stride < surface->stride ? clone->stride : surface->stride;
+
+ for (i = 0; i < clone->height; i++)
+ {
+ memcpy (dst, src, stride);
+ dst += clone->stride;
+ src += surface->stride;
+ }
+ }
+
+ clone->base.is_clear = FALSE;
+
+ return &clone->base;
+}
+
+static cairo_int_status_t
+_cairo_tg_surface_init_tile_surfaces (cairo_tg_surface_t *surface)
+{
+ int i;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ memset (&surface->tile_surfaces[0], 0x00,
+ sizeof (cairo_surface_t *) * CAIRO_TG_NUM_MAX_TILES);
+
+ for (i = 0; i < CAIRO_TG_NUM_MAX_TILES; i++)
+ {
+ surface->tile_surfaces[i] = cairo_image_surface_create_for_data (surface->data,
+ surface->format,
+ surface->width,
+ surface->height,
+ surface->stride);
+
+ if (surface->tile_surfaces[i] == NULL)
+ {
+ status = CAIRO_INT_STATUS_NO_MEMORY;
+ break;
+ }
+ }
+
+ if (unlikely (status))
+ {
+ for (i = 0; i < CAIRO_TG_NUM_MAX_TILES; i++)
+ {
+ if (surface->tile_surfaces[i])
+ cairo_surface_destroy (surface->tile_surfaces[i]);
+ else
+ break;
+ }
+ }
+
+ return status;
+}
+
+static void
+_cairo_tg_surface_fini_tile_surfaces (cairo_tg_surface_t *surface)
+{
+ int i;
+
+ for (i = 0; i < CAIRO_TG_NUM_MAX_TILES; i++)
+ {
+ if (surface->tile_surfaces[i])
+ cairo_surface_destroy (surface->tile_surfaces[i]);
+ else
+ break;
+ }
+}
+
+static cairo_status_t
+_cairo_tg_surface_finish (void *abstract_surface)
+{
+ cairo_tg_surface_t *surface = abstract_surface;
+
+ _cairo_tg_surface_flush (surface, 0);
+ _cairo_tg_journal_fini (&surface->journal);
+ _cairo_tg_surface_fini_tile_surfaces (surface);
+ cairo_surface_destroy (surface->image_surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t _cairo_tg_surface_backend =
+{
+ CAIRO_SURFACE_TYPE_TG,
+ _cairo_tg_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_tg_surface_create_similar,
+ NULL, /* create_similar image */
+ _cairo_tg_surface_map_to_image,
+ _cairo_tg_surface_unmap_image,
+
+ _cairo_tg_surface_source,
+ _cairo_tg_surface_acquire_source_image,
+ _cairo_tg_surface_release_source_image,
+ _cairo_tg_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_tg_surface_get_extents,
+ NULL, /* get_font_options */
+
+ _cairo_tg_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_tg_surface_paint,
+ _cairo_tg_surface_mask,
+ _cairo_tg_surface_stroke,
+ _cairo_tg_surface_fill,
+ NULL, /* fill_stroke */
+ _cairo_tg_surface_glyphs,
+};
+
+cairo_surface_t *
+cairo_tg_surface_create (cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_tg_surface_t *surface;
+ cairo_surface_t *image_surface;
+
+ image_surface = cairo_image_surface_create (format, width, height);
+
+ if (unlikely (image_surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface = malloc (sizeof (cairo_tg_surface_t));
+
+ if (unlikely (surface == NULL))
+ {
+ cairo_surface_destroy (image_surface);
+
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_tg_surface_backend,
+ NULL, image_surface->content);
+
+ surface->format = format;
+ surface->pixman_format = ((cairo_image_surface_t *) image_surface)->pixman_format;
+ surface->data = (unsigned char *) cairo_image_surface_get_data (image_surface);
+ surface->width = width;
+ surface->height = height;
+ surface->stride = cairo_image_surface_get_stride (image_surface);
+ surface->bpp = get_bpp_for_format (format);
+ surface->image_surface = image_surface;
+ surface->base.is_clear = image_surface->is_clear;
+
+ _cairo_tg_journal_init (&surface->journal);
+
+ if (_cairo_tg_surface_init_tile_surfaces (surface))
+ {
+ cairo_surface_destroy (image_surface);
+ _cairo_tg_journal_fini (&surface->journal);
+
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ return &surface->base;
+}
+
+cairo_surface_t *
+cairo_tg_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride)
+{
+ cairo_tg_surface_t *surface;
+ cairo_surface_t *image_surface;
+
+ image_surface = cairo_image_surface_create_for_data (data, format, width, height, stride);
+
+ if (unlikely (image_surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface = malloc (sizeof (cairo_tg_surface_t));
+
+ if (unlikely (surface == NULL))
+ {
+ cairo_surface_destroy (image_surface);
+
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_tg_surface_backend,
+ NULL, image_surface->content);
+
+ surface->format = format;
+ surface->pixman_format = ((cairo_image_surface_t *) image_surface)->pixman_format;
+ surface->data = (unsigned char *) cairo_image_surface_get_data (image_surface);
+ surface->width = width;
+ surface->height = height;
+ surface->stride = cairo_image_surface_get_stride (image_surface);
+ surface->bpp = get_bpp_for_format (format);
+ surface->image_surface = image_surface;
+ surface->base.is_clear = image_surface->is_clear;
+
+ _cairo_tg_journal_init (&surface->journal);
+
+ if (_cairo_tg_surface_init_tile_surfaces (surface))
+ {
+ cairo_surface_destroy (image_surface);
+ _cairo_tg_journal_fini (&surface->journal);
+
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ return &surface->base;
+}
+
+unsigned char *
+cairo_tg_surface_get_data (cairo_surface_t *surface)
+{
+ cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface;
+
+ if (! _cairo_surface_is_tg (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return tg_surface->data;
+}
+
+cairo_format_t
+cairo_tg_surface_get_format (cairo_surface_t *surface)
+{
+ cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface;
+
+ if (! _cairo_surface_is_tg (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return CAIRO_FORMAT_INVALID;
+ }
+
+ return tg_surface->format;
+}
+
+int
+cairo_tg_surface_get_width (cairo_surface_t *surface)
+{
+ cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface;
+
+ if (! _cairo_surface_is_tg (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return tg_surface->width;
+}
+
+int
+cairo_tg_surface_get_height (cairo_surface_t *surface)
+{
+ cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface;
+
+ if (! _cairo_surface_is_tg (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return tg_surface->height;
+}
+
+int
+cairo_tg_surface_get_stride (cairo_surface_t *surface)
+{
+ cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface;
+
+ if (! _cairo_surface_is_tg (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return tg_surface->stride;
+}
diff --git a/src/cairo-tg.h b/src/cairo-tg.h
new file mode 100755
index 000000000..97be60153
--- /dev/null
+++ b/src/cairo-tg.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2012 SCore 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.
+ *
+ * Author: Taekyun Kim (podain77@gmail.com)
+ */
+
+#ifndef CAIRO_TG_H
+#define CAIRO_TG_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_TG_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_tg_surface_create (cairo_format_t format,
+ int width,
+ int height);
+
+cairo_public cairo_surface_t *
+cairo_tg_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride);
+
+cairo_public unsigned char *
+cairo_tg_surface_get_data (cairo_surface_t *surface);
+
+cairo_public cairo_format_t
+cairo_tg_surface_get_format (cairo_surface_t *surface);
+
+cairo_public int
+cairo_tg_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_tg_surface_get_height (cairo_surface_t *surface);
+
+cairo_public int
+cairo_tg_surface_get_stride (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_HAS_TG_SURFACE */
+
+#endif /* CAIRO_TG_H */
diff --git a/src/cairo-thread-local-private.h b/src/cairo-thread-local-private.h
new file mode 100755
index 000000000..994c049c7
--- /dev/null
+++ b/src/cairo-thread-local-private.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright © 2012 SCore 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.
+ *
+ * Author: Taekyun Kim (podain77@gmail.com)
+ */
+
+#ifndef CAIRO_THREAD_LOCAL_PRIVATE_H
+#define CAIRO_THREAD_LOCAL_PRIVATE_H
+
+#if CAIRO_HAS_TLS || CAIRO_HAS_PTHREAD_SETSPECIFIC
+#define CAIRO_HAS_THREAD_LOCAL 1
+#endif
+
+#if CAIRO_HAS_THREAD_LOCAL
+#if defined(TLS)
+
+# define CAIRO_DEFINE_THREAD_LOCAL(type, name) \
+ static TLS type name
+
+# define CAIRO_GET_THREAD_LOCAL(name) \
+ (&name)
+
+#elif defined(__MINGW32__)
+
+# define _NO_W32_PSEUDO_MODIFIERS
+# include <windows.h>
+
+# define CAIRO_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 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 CAIRO_GET_THREAD_LOCAL(name) \
+ tls_ ## name ## _get ()
+
+#elif defined(_MSC_VER)
+
+# define CAIRO_DEFINE_THREAD_LOCAL(type, name) \
+ static __declspec(thread) type name
+
+# define CAIRO_GET_THREAD_LOCAL(name) \
+ (&name)
+
+#elif defined(CAIRO_HAS_PTHREAD_SETSPECIFIC)
+
+#include <pthread.h>
+
+# define CAIRO_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 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 CAIRO_GET_THREAD_LOCAL(name) \
+ tls_ ## name ## _get ()
+
+#else
+
+# error "Unknown thread local support for this system."
+
+#endif
+#endif /* CAIRO_HAS_THREAD_LOCAL */
+
+#endif /* CAIRO_THREAD_LOCAL_PRIVATE_H */
diff --git a/src/cairo-time-private.h b/src/cairo-time-private.h
new file mode 100755
index 000000000..06dc912b4
--- /dev/null
+++ b/src/cairo-time-private.h
@@ -0,0 +1,94 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (C) 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Andrea Canciani <ranma42@gmail.com>
+ *
+ */
+
+#ifndef CAIRO_TIME_PRIVATE_H
+#define CAIRO_TIME_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-wideint-private.h"
+
+/* Make the base type signed for easier arithmetic */
+typedef cairo_int64_t cairo_time_t;
+
+#define _cairo_time_add _cairo_int64_add
+#define _cairo_time_sub _cairo_int64_sub
+#define _cairo_time_gt _cairo_int64_gt
+#define _cairo_time_lt _cairo_int64_lt
+
+#define _cairo_time_to_double _cairo_int64_to_double
+#define _cairo_time_from_double _cairo_double_to_int64
+
+cairo_private int
+_cairo_time_cmp (const void *a,
+ const void *b);
+
+cairo_private double
+_cairo_time_to_s (cairo_time_t t);
+
+cairo_private cairo_time_t
+_cairo_time_from_s (double t);
+
+cairo_private cairo_time_t
+_cairo_time_get (void);
+
+static cairo_always_inline cairo_time_t
+_cairo_time_get_delta (cairo_time_t t)
+{
+ cairo_time_t now;
+
+ now = _cairo_time_get ();
+
+ return _cairo_time_sub (now, t);
+}
+
+static cairo_always_inline double
+_cairo_time_to_ns (cairo_time_t t)
+{
+ return 1.e9 * _cairo_time_to_s (t);
+}
+
+static cairo_always_inline cairo_time_t
+_cairo_time_max (cairo_time_t a, cairo_time_t b)
+{
+ if (_cairo_int64_gt (a, b))
+ return a;
+ else
+ return b;
+}
+
+static cairo_always_inline cairo_time_t
+_cairo_time_min (cairo_time_t a, cairo_time_t b)
+{
+ if (_cairo_int64_lt (a, b))
+ return a;
+ else
+ return b;
+}
+
+#endif
diff --git a/src/cairo-time.c b/src/cairo-time.c
new file mode 100755
index 000000000..a0003fbfc
--- /dev/null
+++ b/src/cairo-time.c
@@ -0,0 +1,225 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2007 Netlabs
+ * Copyright (c) 2006 Mozilla Corporation
+ * Copyright (c) 2006 Red Hat, Inc.
+ * Copyright (c) 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Peter Weilbacher <mozilla@weilbacher.org>
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ * Carl Worth <cworth@cworth.org>
+ * Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-time-private.h"
+
+#if HAVE_CLOCK_GETTIME
+#if defined(CLOCK_MONOTONIC_RAW)
+#define CAIRO_CLOCK CLOCK_MONOTONIC_RAW
+#elif defined(CLOCK_MONOTONIC)
+#define CAIRO_CLOCK CLOCK_MONOTONIC
+#endif
+#endif
+
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+ mach_timebase_info_data_t freq;
+
+ mach_timebase_info (&freq);
+
+ return 1000000000. * freq.denom / freq.numer;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+ return mach_absolute_time ();
+}
+
+#elif defined(__OS2__)
+#define INCL_BASE
+#include <os2.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+ ULONG freq;
+
+ DosTmrQueryFreq (&freq);
+
+ return freq;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+ QWORD t;
+ cairo_int64_t r;
+
+ DosTmrQueryTime (&t);
+
+ r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.ulHi), 32);
+ r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.ulLo));
+
+ return r;
+}
+
+#elif _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+ LARGE_INTEGER freq;
+
+ QueryPerformanceFrequency (&freq);
+
+ return freq.QuadPart;
+}
+
+#ifndef HAVE_UINT64_T
+static cairo_always_inline cairo_time_t
+_cairo_time_from_large_integer (LARGE_INTEGER t)
+{
+ cairo_int64_t r;
+
+ r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.HighPart), 32);
+ r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.LowPart));
+
+ return r;
+}
+#else
+static cairo_always_inline cairo_time_t
+_cairo_time_from_large_integer (LARGE_INTEGER t)
+{
+ return t.QuadPart;
+}
+#endif
+
+cairo_time_t
+_cairo_time_get (void)
+{
+ LARGE_INTEGER t;
+
+ QueryPerformanceCounter (&t);
+
+ return _cairo_time_from_large_integer(t);
+}
+
+#elif defined(CAIRO_CLOCK)
+#include <time.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+ return 1000000000;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+ struct timespec t;
+ cairo_time_t r;
+
+ clock_gettime (CAIRO_CLOCK, &t);
+
+ r = _cairo_double_to_int64 (_cairo_time_1s ());
+ r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec));
+ r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_nsec));
+
+ return r;
+}
+
+#else
+#include <sys/time.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+ return 1000000;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+ struct timeval t;
+ cairo_time_t r;
+
+ gettimeofday (&t, NULL);
+
+ r = _cairo_double_to_int64 (_cairo_time_1s ());
+ r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec));
+ r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_usec));
+
+ return r;
+}
+
+#endif
+
+int
+_cairo_time_cmp (const void *a,
+ const void *b)
+{
+ const cairo_time_t *ta = a, *tb = b;
+ return _cairo_int64_cmp (*ta, *tb);
+}
+
+static double
+_cairo_time_ticks_per_sec (void)
+{
+ static double ticks = 0;
+
+ if (unlikely (ticks == 0))
+ ticks = _cairo_time_1s ();
+
+ return ticks;
+}
+
+static double
+_cairo_time_s_per_tick (void)
+{
+ static double s = 0;
+
+ if (unlikely (s == 0))
+ s = 1. / _cairo_time_ticks_per_sec ();
+
+ return s;
+}
+
+double
+_cairo_time_to_s (cairo_time_t t)
+{
+ return _cairo_int64_to_double (t) * _cairo_time_s_per_tick ();
+}
+
+cairo_time_t
+_cairo_time_from_s (double t)
+{
+ return _cairo_double_to_int64 (t * _cairo_time_ticks_per_sec ());
+}
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
new file mode 100755
index 000000000..89ef20f09
--- /dev/null
+++ b/src/cairo-tor-scan-converter.c
@@ -0,0 +1,1854 @@
+/* -*- 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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.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;
+
+/*-------------------------------------------------------------------------
+ * 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);
+
+/* 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. */
+
+/*-------------------------------------------------------------------------
+ * 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. */
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1)
+#elif GRID_XY == 255
+# define GRID_AREA_TO_ALPHA(c) (c)
+#elif GRID_XY == 64
+# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9)
+#else
+# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+ int32_t quo;
+ int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+ /* # bytes used in this chunk. */
+ size_t size;
+
+ /* # bytes total in this chunk */
+ size_t capacity;
+
+ /* Pointer to the previous chunk or %NULL if this is the sentinel
+ * chunk in the pool header. */
+ struct _pool_chunk *prev_chunk;
+
+ /* Actual data starts here. Well aligned for pointers. */
+};
+
+/* A memory pool. This is supposed to be embedded on the stack or
+ * within some other structure. It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+ /* Chunk we're allocating from. */
+ struct _pool_chunk *current;
+
+ jmp_buf *jmp;
+
+ /* Free list of previously allocated chunks. All have >= default
+ * capacity. */
+ struct _pool_chunk *first_free;
+
+ /* The default capacity of a chunk. */
+ size_t default_capacity;
+
+ /* Header for the sentinel chunk. Directly following the pool
+ * struct should be some space for embedded elements from which
+ * the sentinel chunk allocates from. */
+ struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+ /* Next in y-bucket or active list. */
+ struct edge *next, *prev;
+
+ /* Number of subsample rows remaining to scan convert of this
+ * edge. */
+ grid_scaled_y_t height_left;
+
+ /* Original sign of the edge: +1 for downwards, -1 for upwards
+ * edges. */
+ int dir;
+ int vertical;
+
+ /* Current x coordinate while the edge is on the active
+ * list. Initialised to the x coordinate of the top of the
+ * edge. The quotient is in grid_scaled_x_t units and the
+ * remainder is mod dy in grid_scaled_y_t units.*/
+ struct quorem x;
+
+ /* Advance of the current x when moving down a subsample line. */
+ struct quorem dxdy;
+
+ /* 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;
+};
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+ /* The vertical clip extents. */
+ grid_scaled_y_t ymin, ymax;
+
+ /* Array of edges all starting in the same bucket. An edge is put
+ * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+ * it is added to the polygon. */
+ struct edge **y_buckets;
+ struct edge *y_buckets_embedded[64];
+
+ struct {
+ struct pool base[1];
+ struct edge embedded[32];
+ } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel. It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one. The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * | | |
+ * | | |
+ * |_______________________|_______________________|
+ * | \...................|.......................|\
+ * | \..................|.......................| |
+ * | \.................|.......................| |
+ * | \....covered.....|.......................| |
+ * | \....area.......|.......................| } covered height
+ * | \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * | area \............|.......................| |
+ * |___________\...........|.......................|/
+ * | | |
+ * | | |
+ * | | |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead. The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge. As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign. When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+ struct cell *next;
+ int x;
+ int16_t uncovered_area;
+ int16_t covered_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x. It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+ /* Sentinel nodes */
+ struct cell head, tail;
+
+ /* Cursor state for iterating through the cell list. */
+ struct cell *cursor, *rewind;
+
+ /* Cells in the cell list are owned by the cell list and are
+ * allocated from this pool. */
+ struct {
+ struct pool base[1];
+ struct cell embedded[32];
+ } cell_pool;
+};
+
+struct cell_pair {
+ struct cell *cell1;
+ struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+ /* Leftmost edge on the current scan line. */
+ struct edge head, tail;
+
+ /* A lower bound on the height of the active edges is used to
+ * estimate how soon some active edge ends. We can't advance the
+ * scan conversion by a full pixel row if an edge ends somewhere
+ * within it. */
+ grid_scaled_y_t min_height;
+ int is_vertical;
+};
+
+struct glitter_scan_converter {
+ struct polygon polygon[1];
+ struct active_list active[1];
+ struct cell_list coverages[1];
+
+ cairo_half_open_span_t *spans;
+ cairo_half_open_span_t spans_embedded[64];
+
+ /* Clip box. */
+ grid_scaled_x_t xmin, xmax;
+ grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+ struct quorem qr;
+ qr.quo = a/b;
+ qr.rem = a%b;
+ if ((a^b)<0 && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+ struct quorem qr;
+ long long xa = (long long)x*a;
+ qr.quo = xa/b;
+ qr.rem = xa%b;
+ if ((xa>=0) != (b>=0) && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+ struct _pool_chunk *p,
+ struct _pool_chunk *prev_chunk,
+ size_t capacity)
+{
+ p->prev_chunk = prev_chunk;
+ p->size = 0;
+ p->capacity = capacity;
+ return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+ struct _pool_chunk *p;
+
+ p = malloc(size + sizeof(struct _pool_chunk));
+ if (unlikely (NULL == p))
+ longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+ jmp_buf *jmp,
+ size_t default_capacity,
+ size_t embedded_capacity)
+{
+ pool->jmp = jmp;
+ pool->current = pool->sentinel;
+ pool->first_free = NULL;
+ pool->default_capacity = default_capacity;
+ _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+ struct _pool_chunk *p = pool->current;
+ do {
+ while (NULL != p) {
+ struct _pool_chunk *prev = p->prev_chunk;
+ if (p != pool->sentinel)
+ free(p);
+ p = prev;
+ }
+ p = pool->first_free;
+ pool->first_free = NULL;
+ } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+ struct pool *pool,
+ size_t size)
+{
+ struct _pool_chunk *chunk;
+ void *obj;
+ size_t capacity;
+
+ /* If the allocation is smaller than the default chunk size then
+ * try getting a chunk off the free list. Force alloc of a new
+ * chunk for large requests. */
+ capacity = size;
+ chunk = NULL;
+ if (size < pool->default_capacity) {
+ capacity = pool->default_capacity;
+ chunk = pool->first_free;
+ if (chunk) {
+ pool->first_free = chunk->prev_chunk;
+ _pool_chunk_init(chunk, pool->current, chunk->capacity);
+ }
+ }
+
+ if (NULL == chunk)
+ chunk = _pool_chunk_create (pool, capacity);
+ pool->current = chunk;
+
+ obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+}
+
+/* Allocate size bytes from the pool. The first allocated address
+ * returned from a pool is aligned to sizeof(void*). Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated. Returns the address of a new memory area or %NULL on
+ * allocation failures. The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+ struct _pool_chunk *chunk = pool->current;
+
+ if (size <= chunk->capacity - chunk->size) {
+ void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+ } else {
+ return _pool_alloc_from_new_chunk(pool, size);
+ }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+ /* Transfer all used chunks to the chunk free list. */
+ struct _pool_chunk *chunk = pool->current;
+ if (chunk != pool->sentinel) {
+ while (chunk->prev_chunk != pool->sentinel) {
+ chunk = chunk->prev_chunk;
+ }
+ chunk->prev_chunk = pool->first_free;
+ pool->first_free = pool->current;
+ }
+ /* Reset the sentinel as the current chunk. */
+ pool->current = pool->sentinel;
+ pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning. After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+ cells->cursor = &cells->head;
+}
+
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+ if (x < cells->cursor->x) {
+ cells->cursor = cells->rewind;
+ if (x < cells->cursor->x)
+ cells->cursor = &cells->head;
+ }
+}
+
+inline static void
+cell_list_set_rewind (struct cell_list *cells)
+{
+ cells->rewind = cells->cursor;
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+ pool_init(cells->cell_pool.base, jmp,
+ 256*sizeof(struct cell),
+ sizeof(cells->cell_pool.embedded));
+ cells->tail.next = NULL;
+ cells->tail.x = INT_MAX;
+ cells->head.x = INT_MIN;
+ cells->head.next = &cells->tail;
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+ pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list. This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+ cell_list_rewind (cells);
+ cells->head.next = &cells->tail;
+ pool_reset (cells->cell_pool.base);
+}
+
+inline static struct cell *
+cell_list_alloc (struct cell_list *cells,
+ struct cell *tail,
+ int x)
+{
+ struct cell *cell;
+
+ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+ cell->next = tail->next;
+ tail->next = cell;
+ cell->x = x;
+ *(uint32_t *)&cell->uncovered_area = 0;
+
+ return cell;
+}
+
+/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
+ * needed to be allocated but couldn't be. Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+
+ if (tail->x == x)
+ return tail;
+
+ while (1) {
+ UNROLL3({
+ if (tail->next->x > x)
+ break;
+ tail = tail->next;
+ });
+ }
+
+ if (tail->x != x)
+ tail = cell_list_alloc (cells, tail, x);
+ return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2. This is exactly equivalent
+ * to
+ *
+ * pair.cell1 = cell_list_find(cells, x1);
+ * pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+ struct cell_pair pair;
+
+ pair.cell1 = cells->cursor;
+ while (1) {
+ UNROLL3({
+ if (pair.cell1->next->x > x1)
+ break;
+ pair.cell1 = pair.cell1->next;
+ });
+ }
+ if (pair.cell1->x != x1)
+ pair.cell1 = cell_list_alloc (cells, pair.cell1, x1);
+
+ pair.cell2 = pair.cell1;
+ while (1) {
+ UNROLL3({
+ if (pair.cell2->next->x > x2)
+ break;
+ pair.cell2 = pair.cell2->next;
+ });
+ }
+ if (pair.cell2->x != x2)
+ pair.cell2 = cell_list_alloc (cells, pair.cell2, x2);
+
+ cells->cursor = pair.cell2;
+ return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+ grid_scaled_x_t x1,
+ grid_scaled_x_t x2)
+{
+ int ix1, fx1;
+ int ix2, fx2;
+
+ if (x1 == x2)
+ return;
+
+ GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+ if (ix1 != ix2) {
+ struct cell_pair p;
+ p = cell_list_find_pair(cells, ix1, ix2);
+ p.cell1->uncovered_area += 2*fx1;
+ ++p.cell1->covered_height;
+ p.cell2->uncovered_area -= 2*fx2;
+ --p.cell2->covered_height;
+ } else {
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->uncovered_area += 2*(fx1-fx2);
+ }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change. In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.) */
+static void
+cell_list_render_edge(struct cell_list *cells,
+ struct edge *edge,
+ int sign)
+{
+ grid_scaled_y_t y1, y2, dy;
+ grid_scaled_x_t dx;
+ int ix1, ix2;
+ grid_scaled_x_t fx1, fx2;
+
+ struct quorem x1 = edge->x;
+ struct quorem x2 = x1;
+
+ if (! edge->vertical) {
+ x2.quo += edge->dxdy_full.quo;
+ x2.rem += edge->dxdy_full.rem;
+ if (x2.rem >= 0) {
+ ++x2.quo;
+ x2.rem -= edge->dy;
+ }
+
+ edge->x = x2;
+ }
+
+ GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+ /* Edge is entirely within a column? */
+ if (ix1 == ix2) {
+ /* We always know that ix1 is >= the cell list cursor in this
+ * case due to the no-intersections precondition. */
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->covered_height += sign*GRID_Y;
+ cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+ return;
+ }
+
+ /* Orient the edge left-to-right. */
+ dx = x2.quo - x1.quo;
+ if (dx >= 0) {
+ y1 = 0;
+ y2 = GRID_Y;
+ } else {
+ int tmp;
+ tmp = ix1; ix1 = ix2; ix2 = tmp;
+ tmp = fx1; fx1 = fx2; fx2 = tmp;
+ dx = -dx;
+ sign = -sign;
+ y1 = GRID_Y;
+ y2 = 0;
+ }
+ dy = y2 - y1;
+
+ /* Add coverage for all pixels [ix1,ix2] on this row crossed
+ * by the edge. */
+ {
+ struct cell_pair pair;
+ struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+ /* When rendering a previous edge on the active list we may
+ * advance the cell list cursor past the leftmost pixel of the
+ * current edge even though the two edges don't intersect.
+ * e.g. consider two edges going down and rightwards:
+ *
+ * --\_+---\_+-----+-----+----
+ * \_ \_ | |
+ * | \_ | \_ | |
+ * | \_| \_| |
+ * | \_ \_ |
+ * ----+-----+-\---+-\---+----
+ *
+ * The left edge touches cells past the starting cell of the
+ * right edge. Fortunately such cases are rare.
+ *
+ * The rewinding is never necessary if the current edge stays
+ * within a single column because we've checked before calling
+ * this function that the active list order won't change. */
+ cell_list_maybe_rewind(cells, ix1);
+
+ pair = cell_list_find_pair(cells, ix1, ix1+1);
+ pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+ pair.cell1->covered_height += sign*y.quo;
+ y.quo += y1;
+
+ if (ix1+1 < ix2) {
+ struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+ struct cell *cell = pair.cell2;
+
+ ++ix1;
+ do {
+ grid_scaled_y_t y_skip = dydx_full.quo;
+ y.rem += dydx_full.rem;
+ if (y.rem >= dx) {
+ ++y_skip;
+ y.rem -= dx;
+ }
+
+ y.quo += y_skip;
+
+ y_skip *= sign;
+ cell->uncovered_area += y_skip*GRID_X;
+ cell->covered_height += y_skip;
+
+ ++ix1;
+ cell = cell_list_find(cells, ix1);
+ } while (ix1 != ix2);
+
+ pair.cell2 = cell;
+ }
+ pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+ pair.cell2->covered_height += sign*(y2 - y.quo);
+ }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+ polygon->ymin = polygon->ymax = 0;
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ pool_init (polygon->edge_pool.base, jmp,
+ 8192 - sizeof (struct _pool_chunk),
+ sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static glitter_status_t
+polygon_reset (struct polygon *polygon,
+ grid_scaled_y_t ymin,
+ grid_scaled_y_t ymax)
+{
+ unsigned h = ymax - ymin;
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
+
+ pool_reset(polygon->edge_pool.base);
+
+ if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
+ goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+ polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+ sizeof (struct edge *));
+ if (unlikely (NULL == polygon->y_buckets))
+ goto bail_no_mem;
+ }
+ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
+ return GLITTER_STATUS_SUCCESS;
+
+bail_no_mem:
+ polygon->ymin = 0;
+ polygon->ymax = 0;
+ return GLITTER_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
+ struct edge *e)
+{
+ unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+ struct edge **ptail = &polygon->y_buckets[ix];
+ e->next = *ptail;
+ *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+ const cairo_edge_t *edge)
+{
+ struct edge *e;
+ grid_scaled_x_t dx;
+ grid_scaled_y_t dy;
+ grid_scaled_y_t ytop, ybot;
+ grid_scaled_y_t ymin = polygon->ymin;
+ grid_scaled_y_t ymax = polygon->ymax;
+
+ if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+ return;
+
+ e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+ e->dir = edge->dir;
+
+ ytop = edge->top >= ymin ? edge->top : ymin;
+ ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+ e->ytop = ytop;
+ e->height_left = ybot - ytop;
+
+ if (dx == 0) {
+ e->vertical = TRUE;
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ e->dxdy.quo = 0;
+ e->dxdy.rem = 0;
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_divrem (dx, dy);
+ if (ytop == edge->line.p1.y) {
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ } else {
+ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+
+ if (e->height_left >= GRID_Y) {
+ e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+ } else {
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+ e->x.rem -= dy; /* Bias the remainder for faster
+ * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+ active->head.vertical = 1;
+ active->head.height_left = INT_MAX;
+ active->head.x.quo = INT_MIN;
+ active->head.prev = NULL;
+ active->head.next = &active->tail;
+ active->tail.prev = &active->head;
+ active->tail.next = NULL;
+ active->tail.x.quo = INT_MAX;
+ active->tail.height_left = INT_MAX;
+ active->tail.vertical = 1;
+ active->min_height = 0;
+ active->is_vertical = 1;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+ active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ * - head_a: The head of the first list.
+ * - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+ struct edge *head, **next, *prev;
+ int32_t x;
+
+ prev = head_a->prev;
+ next = &head;
+ if (head_a->x.quo <= head_b->x.quo) {
+ head = head_a;
+ } else {
+ head = head_b;
+ head_b->prev = prev;
+ goto start_with_b;
+ }
+
+ do {
+ x = head_b->x.quo;
+ while (head_a != NULL && head_a->x.quo <= x) {
+ prev = head_a;
+ next = &head_a->next;
+ head_a = head_a->next;
+ }
+
+ head_b->prev = prev;
+ *next = head_b;
+ if (head_a == NULL)
+ return head;
+
+start_with_b:
+ x = head_a->x.quo;
+ while (head_b != NULL && head_b->x.quo <= x) {
+ prev = head_b;
+ next = &head_b->next;
+ head_b = head_b->next;
+ }
+
+ head_a->prev = prev;
+ *next = head_a;
+ if (head_b == NULL)
+ return head;
+ } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ * - list: The list to be sorted; list cannot be NULL.
+ * - limit: Recursion limit.
+ * Output:
+ * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ * input list; if the input list has fewer elements, head_out be a sorted list
+ * containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge *list,
+ unsigned int level,
+ struct edge **head_out)
+{
+ struct edge *head_other, *remaining;
+ unsigned int i;
+
+ head_other = list->next;
+
+ if (head_other == NULL) {
+ *head_out = list;
+ return NULL;
+ }
+
+ remaining = head_other->next;
+ if (list->x.quo <= head_other->x.quo) {
+ *head_out = list;
+ head_other->next = NULL;
+ } else {
+ *head_out = head_other;
+ head_other->prev = list->prev;
+ head_other->next = list;
+ list->prev = head_other;
+ list->next = NULL;
+ }
+
+ for (i = 0; i < level && remaining; i++) {
+ remaining = sort_edges (remaining, i, &head_other);
+ *head_out = merge_sorted_edges (*head_out, head_other);
+ }
+
+ return remaining;
+}
+
+ static struct edge *
+merge_unsorted_edges (struct edge *head, struct edge *unsorted)
+{
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ return merge_sorted_edges (head, unsorted);
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+can_do_full_row (struct active_list *active)
+{
+ const struct edge *e;
+ int prev_x = INT_MIN;
+
+ /* Recomputes the minimum height of all edges on the active
+ * list if we have been dropping edges. */
+ if (active->min_height <= 0) {
+ int min_height = INT_MAX;
+ int is_vertical = 1;
+
+ e = active->head.next;
+ while (NULL != e) {
+ if (e->height_left < min_height)
+ min_height = e->height_left;
+ is_vertical &= e->vertical;
+ e = e->next;
+ }
+
+ active->is_vertical = is_vertical;
+ active->min_height = min_height;
+ }
+
+ if (active->min_height < GRID_Y)
+ return 0;
+
+ /* Check for intersections as no edges end during the next row. */
+ for (e = active->head.next; e != &active->tail; e = e->next) {
+ struct quorem x = e->x;
+
+ if (! e->vertical) {
+ x.quo += e->dxdy_full.quo;
+ x.rem += e->dxdy_full.rem;
+ if (x.rem >= 0)
+ ++x.quo;
+ }
+
+ if (x.quo < prev_x)
+ return 0;
+
+ prev_x = x.quo;
+ }
+
+ return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_bucket(struct active_list *active,
+ struct edge *edges)
+{
+ active->head.next = merge_unsorted_edges (active->head.next, edges);
+}
+
+inline static void
+polygon_fill_buckets (struct active_list *active,
+ struct edge *edge,
+ int y,
+ struct edge **buckets)
+{
+ grid_scaled_y_t min_height = active->min_height;
+ int is_vertical = active->is_vertical;
+
+ while (edge) {
+ struct edge *next = edge->next;
+ int suby = edge->ytop - y;
+ if (buckets[suby])
+ buckets[suby]->prev = edge;
+ edge->next = buckets[suby];
+ edge->prev = NULL;
+ buckets[suby] = edge;
+ if (edge->height_left < min_height)
+ min_height = edge->height_left;
+ is_vertical &= edge->vertical;
+ edge = next;
+ }
+
+ active->is_vertical = is_vertical;
+ active->min_height = min_height;
+}
+
+inline static void
+sub_row (struct active_list *active,
+ struct cell_list *coverages,
+ unsigned int mask)
+{
+ struct edge *edge = active->head.next;
+ int xstart = INT_MIN, prev_x = INT_MIN;
+ int winding = 0;
+
+ cell_list_rewind (coverages);
+
+ while (&active->tail != edge) {
+ struct edge *next = edge->next;
+ int xend = edge->x.quo;
+
+ if (--edge->height_left) {
+ edge->x.quo += edge->dxdy.quo;
+ edge->x.rem += edge->dxdy.rem;
+ if (edge->x.rem >= 0) {
+ ++edge->x.quo;
+ edge->x.rem -= edge->dy;
+ }
+
+ if (edge->x.quo < prev_x) {
+ struct edge *pos = edge->prev;
+ pos->next = next;
+ next->prev = pos;
+ do {
+ pos = pos->prev;
+ } while (edge->x.quo < pos->x.quo);
+ pos->next->prev = edge;
+ edge->next = pos->next;
+ edge->prev = pos;
+ pos->next = edge;
+ } else
+ prev_x = edge->x.quo;
+ active->min_height = -1;
+ } else {
+ edge->prev->next = next;
+ next->prev = edge->prev;
+ }
+
+ winding += edge->dir;
+ if ((winding & mask) == 0) {
+ if (next->x.quo != xend) {
+ cell_list_add_subspan (coverages, xstart, xend);
+ xstart = INT_MIN;
+ }
+ } else if (xstart == INT_MIN)
+ xstart = xend;
+
+ edge = next;
+ }
+}
+
+inline static void dec (struct active_list *a, struct edge *e, int h)
+{
+ e->height_left -= h;
+ if (e->height_left == 0) {
+ e->prev->next = e->next;
+ e->next->prev = e->prev;
+ a->min_height = -1;
+ }
+}
+
+inline static void full_step (struct edge *e)
+{
+ if (! e->vertical) {
+ e->x.quo += e->dxdy_full.quo;
+ e->x.rem += e->dxdy_full.rem;
+ if (e->x.rem >= 0) {
+ ++e->x.quo;
+ e->x.rem -= e->dy;
+ }
+ }
+}
+
+static void
+full_row (struct active_list *active,
+ struct cell_list *coverages,
+ unsigned int mask)
+{
+ struct edge *left = active->head.next;
+
+ while (&active->tail != left) {
+ struct edge *right;
+ int winding;
+
+ dec (active, left, GRID_Y);
+
+ winding = left->dir;
+ right = left->next;
+ do {
+ dec (active, right, GRID_Y);
+
+ winding += right->dir;
+ if ((winding & mask) == 0 && right->next->x.quo != right->x.quo)
+ break;
+
+ full_step (right);
+
+ right = right->next;
+ } while (1);
+
+ cell_list_set_rewind (coverages);
+ cell_list_render_edge (coverages, left, +1);
+ cell_list_render_edge (coverages, right, -1);
+
+ left = right->next;
+ }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+ polygon_init(converter->polygon, jmp);
+ active_list_init(converter->active);
+ cell_list_init(converter->coverages, jmp);
+ converter->xmin=0;
+ converter->ymin=0;
+ converter->xmax=0;
+ converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *self)
+{
+ if (self->spans != self->spans_embedded)
+ free (self->spans);
+
+ polygon_fini(self->polygon);
+ cell_list_fini(self->coverages);
+
+ self->xmin=0;
+ self->ymin=0;
+ self->xmax=0;
+ self->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+ /* Clamp to max/min representable scaled number. */
+ if (i >= 0) {
+ if (i >= INT_MAX/scale)
+ i = INT_MAX/scale;
+ }
+ else {
+ if (i <= INT_MIN/scale)
+ i = INT_MIN/scale;
+ }
+ return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+I glitter_status_t
+glitter_scan_converter_reset(
+ glitter_scan_converter_t *converter,
+ int xmin, int ymin,
+ int xmax, int ymax)
+{
+ glitter_status_t status;
+ int max_num_spans;
+
+ converter->xmin = 0; converter->xmax = 0;
+ converter->ymin = 0; converter->ymax = 0;
+
+ max_num_spans = xmax - xmin + 1;
+
+ if (max_num_spans > ARRAY_LENGTH(converter->spans_embedded)) {
+ converter->spans = _cairo_malloc_ab (max_num_spans,
+ sizeof (cairo_half_open_span_t));
+ if (unlikely (converter->spans == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else
+ converter->spans = converter->spans_embedded;
+
+ xmin = int_to_grid_scaled_x(xmin);
+ ymin = int_to_grid_scaled_y(ymin);
+ xmax = int_to_grid_scaled_x(xmax);
+ ymax = int_to_grid_scaled_y(ymax);
+
+ active_list_reset(converter->active);
+ cell_list_reset(converter->coverages);
+ status = polygon_reset(converter->polygon, ymin, ymax);
+ if (status)
+ return status;
+
+ converter->xmin = xmin;
+ converter->xmax = xmax;
+ converter->ymin = ymin;
+ converter->ymax = ymax;
+ return GLITTER_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ * These macros convert an input coordinate in the client's
+ * device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
+ long long tmp__ = (long long)(grid_scale) * (in); \
+ tmp__ >>= GLITTER_INPUT_BITS; \
+ (out) = tmp__; \
+} while (0)
+
+/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
+ * converter. The coordinates represent pixel positions scaled by
+ * 2**GLITTER_PIXEL_BITS. If this function fails then the scan
+ * converter should be reset or destroyed. Dir must be +1 or -1,
+ * with the latter reversing the orientation of the edge. */
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge)
+{
+ cairo_edge_t e;
+
+ INPUT_TO_GRID_Y (edge->top, e.top);
+ INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+ if (e.top >= e.bottom)
+ return;
+
+ /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+ INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+ if (e.line.p1.y == e.line.p2.y)
+ e.line.p2.y++; /* little fudge to prevent a div-by-zero */
+
+ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+ INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+ e.dir = edge->dir;
+
+ polygon_add_edge (converter->polygon, &e);
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+ struct edge *edge;
+
+ count *= GRID_Y;
+ for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
+ edge->height_left -= count;
+ if (! edge->height_left) {
+ edge->prev->next = edge->next;
+ edge->next->prev = edge->prev;
+ active->min_height = -1;
+ }
+ }
+}
+
+static glitter_status_t
+blit_a8 (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ cairo_half_open_span_t *spans,
+ int y, int height,
+ int xmin, int xmax)
+{
+ struct cell *cell = cells->head.next;
+ int prev_x = xmin, last_x = -1;
+ int16_t cover = 0, last_cover = 0;
+ unsigned num_spans;
+
+ if (cell == &cells->tail)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Skip cells to the left of the clip region. */
+ while (cell->x < xmin) {
+ cover += cell->covered_height;
+ cell = cell->next;
+ }
+ cover *= GRID_X*2;
+
+ /* Form the spans from the coverages and areas. */
+ num_spans = 0;
+ for (; cell->x < xmax; cell = cell->next) {
+ int x = cell->x;
+ int16_t area;
+
+ if (x > prev_x && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ last_cover = cover;
+ last_x = prev_x;
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ if (area != last_cover) {
+ spans[num_spans].x = x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+ last_cover = area;
+ last_x = x;
+ ++num_spans;
+ }
+
+ prev_x = x+1;
+ }
+
+ if (prev_x <= xmax && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ last_cover = cover;
+ last_x = prev_x;
+ ++num_spans;
+ }
+
+ if (last_x < xmax && last_cover) {
+ spans[num_spans].x = xmax;
+ spans[num_spans].coverage = 0;
+ ++num_spans;
+ }
+
+ /* Dump them into the renderer. */
+ return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+#define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0)
+static glitter_status_t
+blit_a1 (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ cairo_half_open_span_t *spans,
+ int y, int height,
+ int xmin, int xmax)
+{
+ struct cell *cell = cells->head.next;
+ int prev_x = xmin, last_x = -1;
+ int16_t cover = 0;
+ uint8_t coverage, last_cover = 0;
+ unsigned num_spans;
+
+ if (cell == &cells->tail)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Skip cells to the left of the clip region. */
+ while (cell->x < xmin) {
+ cover += cell->covered_height;
+ cell = cell->next;
+ }
+ cover *= GRID_X*2;
+
+ /* Form the spans from the coverages and areas. */
+ num_spans = 0;
+ for (; cell->x < xmax; cell = cell->next) {
+ int x = cell->x;
+ int16_t area;
+
+ coverage = GRID_AREA_TO_A1 (cover);
+ if (x > prev_x && coverage != last_cover) {
+ last_x = spans[num_spans].x = prev_x;
+ last_cover = spans[num_spans].coverage = coverage;
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ coverage = GRID_AREA_TO_A1 (area);
+ if (coverage != last_cover) {
+ last_x = spans[num_spans].x = x;
+ last_cover = spans[num_spans].coverage = coverage;
+ ++num_spans;
+ }
+
+ prev_x = x+1;
+ }
+
+ coverage = GRID_AREA_TO_A1 (cover);
+ if (prev_x <= xmax && coverage != last_cover) {
+ last_x = spans[num_spans].x = prev_x;
+ last_cover = spans[num_spans].coverage = coverage;
+ ++num_spans;
+ }
+
+ if (last_x < xmax && last_cover) {
+ spans[num_spans].x = xmax;
+ spans[num_spans].coverage = 0;
+ ++num_spans;
+ }
+ if (num_spans == 1)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Dump them into the renderer. */
+ return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+
+I void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+ unsigned int winding_mask,
+ int antialias,
+ cairo_span_renderer_t *renderer)
+{
+ int i, j;
+ int ymax_i = converter->ymax / GRID_Y;
+ int ymin_i = converter->ymin / GRID_Y;
+ int xmin_i, xmax_i;
+ int h = ymax_i - ymin_i;
+ struct polygon *polygon = converter->polygon;
+ struct cell_list *coverages = converter->coverages;
+ struct active_list *active = converter->active;
+ struct edge *buckets[GRID_Y] = { 0 };
+
+ xmin_i = converter->xmin / GRID_X;
+ xmax_i = converter->xmax / GRID_X;
+ if (xmin_i >= xmax_i)
+ return;
+
+ /* Render each pixel row. */
+ for (i = 0; i < h; i = j) {
+ int do_full_row = 0;
+
+ j = i + 1;
+
+ /* Determine if we can ignore this row or use the full pixel
+ * stepper. */
+ if (! polygon->y_buckets[i]) {
+ if (active->head.next == &active->tail) {
+ active->min_height = INT_MAX;
+ active->is_vertical = 1;
+ for (; j < h && ! polygon->y_buckets[j]; j++)
+ ;
+ continue;
+ }
+
+ do_full_row = can_do_full_row (active);
+ }
+
+ if (do_full_row) {
+ /* Step by a full pixel row's worth. */
+ full_row (active, coverages, winding_mask);
+
+ if (active->is_vertical) {
+ while (j < h &&
+ polygon->y_buckets[j] == NULL &&
+ active->min_height >= 2*GRID_Y)
+ {
+ active->min_height -= GRID_Y;
+ j++;
+ }
+ if (j != i + 1)
+ step_edges (active, j - (i + 1));
+ }
+ } else {
+ int sub;
+
+ polygon_fill_buckets (active,
+ polygon->y_buckets[i],
+ (i+ymin_i)*GRID_Y,
+ buckets);
+
+ /* Subsample this row. */
+ for (sub = 0; sub < GRID_Y; sub++) {
+ if (buckets[sub]) {
+ active_list_merge_edges_from_bucket (active, buckets[sub]);
+ buckets[sub] = NULL;
+ }
+
+ sub_row (active, coverages, winding_mask);
+ }
+ }
+
+ if (antialias)
+ blit_a8 (coverages, renderer, converter->spans,
+ i+ymin_i, j-i, xmin_i, xmax_i);
+ else
+ blit_a1 (coverages, renderer, converter->spans,
+ i+ymin_i, j-i, xmin_i, xmax_i);
+ cell_list_reset (coverages);
+
+ active->min_height -= GRID_Y;
+ }
+}
+
+struct _cairo_tor_scan_converter {
+ cairo_scan_converter_t base;
+
+ glitter_scan_converter_t converter[1];
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+
+ jmp_buf jmp;
+};
+
+typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t;
+
+static void
+_cairo_tor_scan_converter_destroy (void *converter)
+{
+ cairo_tor_scan_converter_t *self = converter;
+ if (self == NULL) {
+ return;
+ }
+ _glitter_scan_converter_fini (self->converter);
+ free(self);
+}
+
+cairo_status_t
+_cairo_tor_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon)
+{
+ cairo_tor_scan_converter_t *self = converter;
+ int i;
+
+#if 0
+ FILE *file = fopen ("polygon.txt", "w");
+ _cairo_debug_print_polygon (file, polygon);
+ fclose (file);
+#endif
+
+ for (i = 0; i < polygon->num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tor_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_tor_scan_converter_t *self = converter;
+ cairo_status_t status;
+
+ if ((status = setjmp (self->jmp)))
+ return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+ glitter_scan_converter_render (self->converter,
+ self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
+ self->antialias != CAIRO_ANTIALIAS_NONE,
+ renderer);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_tor_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias)
+{
+ cairo_tor_scan_converter_t *self;
+ cairo_status_t status;
+
+ self = malloc (sizeof(struct _cairo_tor_scan_converter));
+ if (unlikely (self == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail_nomem;
+ }
+
+ self->base.destroy = _cairo_tor_scan_converter_destroy;
+ self->base.generate = _cairo_tor_scan_converter_generate;
+
+ _glitter_scan_converter_init (self->converter, &self->jmp);
+ status = glitter_scan_converter_reset (self->converter,
+ xmin, ymin, xmax, ymax);
+ if (unlikely (status))
+ goto bail;
+
+ self->fill_rule = fill_rule;
+ self->antialias = antialias;
+
+ return &self->base;
+
+ bail:
+ self->base.destroy(&self->base);
+ bail_nomem:
+ return _cairo_scan_converter_create_in_error (status);
+}
diff --git a/src/cairo-tor22-scan-converter.c b/src/cairo-tor22-scan-converter.c
new file mode 100755
index 000000000..4cec5ee4f
--- /dev/null
+++ b/src/cairo-tor22-scan-converter.c
@@ -0,0 +1,1709 @@
+/* -*- 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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.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
+#define GRID_X_BITS 2
+#define GRID_Y_BITS 2
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as
+ * pixel scaled numbers. These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<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);
+
+/* 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. */
+
+/*-------------------------------------------------------------------------
+ * 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. */
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1)
+#elif GRID_XY == 255
+# define GRID_AREA_TO_ALPHA(c) (c)
+#elif GRID_XY == 64
+# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 32
+# define GRID_AREA_TO_ALPHA(c) (((c) << 3) | -(((c) & 0x20) >> 5))
+#elif GRID_XY == 128
+# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9)
+#else
+# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+ int32_t quo;
+ int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+ /* # bytes used in this chunk. */
+ size_t size;
+
+ /* # bytes total in this chunk */
+ size_t capacity;
+
+ /* Pointer to the previous chunk or %NULL if this is the sentinel
+ * chunk in the pool header. */
+ struct _pool_chunk *prev_chunk;
+
+ /* Actual data starts here. Well aligned for pointers. */
+};
+
+/* A memory pool. This is supposed to be embedded on the stack or
+ * within some other structure. It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+ /* Chunk we're allocating from. */
+ struct _pool_chunk *current;
+
+ jmp_buf *jmp;
+
+ /* Free list of previously allocated chunks. All have >= default
+ * capacity. */
+ struct _pool_chunk *first_free;
+
+ /* The default capacity of a chunk. */
+ size_t default_capacity;
+
+ /* Header for the sentinel chunk. Directly following the pool
+ * struct should be some space for embedded elements from which
+ * the sentinel chunk allocates from. */
+ struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+ /* Next in y-bucket or active list. */
+ struct edge *next, *prev;
+
+ /* Number of subsample rows remaining to scan convert of this
+ * edge. */
+ grid_scaled_y_t height_left;
+
+ /* Original sign of the edge: +1 for downwards, -1 for upwards
+ * edges. */
+ int dir;
+ int vertical;
+
+ /* Current x coordinate while the edge is on the active
+ * list. Initialised to the x coordinate of the top of the
+ * edge. The quotient is in grid_scaled_x_t units and the
+ * remainder is mod dy in grid_scaled_y_t units.*/
+ struct quorem x;
+
+ /* Advance of the current x when moving down a subsample line. */
+ struct quorem dxdy;
+
+ /* The clipped y of the top of the edge. */
+ grid_scaled_y_t ytop;
+
+ /* y2-y1 after orienting the edge downwards. */
+ grid_scaled_y_t dy;
+};
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+ /* The vertical clip extents. */
+ grid_scaled_y_t ymin, ymax;
+
+ /* Array of edges all starting in the same bucket. An edge is put
+ * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+ * it is added to the polygon. */
+ struct edge **y_buckets;
+ struct edge *y_buckets_embedded[64];
+
+ struct {
+ struct pool base[1];
+ struct edge embedded[32];
+ } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel. It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one. The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * | | |
+ * | | |
+ * |_______________________|_______________________|
+ * | \...................|.......................|\
+ * | \..................|.......................| |
+ * | \.................|.......................| |
+ * | \....covered.....|.......................| |
+ * | \....area.......|.......................| } covered height
+ * | \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * | area \............|.......................| |
+ * |___________\...........|.......................|/
+ * | | |
+ * | | |
+ * | | |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead. The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge. As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign. When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+ struct cell *next;
+ int x;
+ int16_t uncovered_area;
+ int16_t covered_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x. It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+ /* Sentinel nodes */
+ struct cell head, tail;
+
+ /* Cursor state for iterating through the cell list. */
+ struct cell *cursor, *rewind;
+
+ /* Cells in the cell list are owned by the cell list and are
+ * allocated from this pool. */
+ struct {
+ struct pool base[1];
+ struct cell embedded[32];
+ } cell_pool;
+};
+
+struct cell_pair {
+ struct cell *cell1;
+ struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+ /* Leftmost edge on the current scan line. */
+ struct edge head, tail;
+
+ /* A lower bound on the height of the active edges is used to
+ * estimate how soon some active edge ends. We can't advance the
+ * scan conversion by a full pixel row if an edge ends somewhere
+ * within it. */
+ grid_scaled_y_t min_height;
+ int is_vertical;
+};
+
+struct glitter_scan_converter {
+ struct polygon polygon[1];
+ struct active_list active[1];
+ struct cell_list coverages[1];
+
+ cairo_half_open_span_t *spans;
+ cairo_half_open_span_t spans_embedded[64];
+
+ /* Clip box. */
+ grid_scaled_x_t xmin, xmax;
+ grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+ struct quorem qr;
+ qr.quo = a/b;
+ qr.rem = a%b;
+ if ((a^b)<0 && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+ struct quorem qr;
+ long long xa = (long long)x*a;
+ qr.quo = xa/b;
+ qr.rem = xa%b;
+ if ((xa>=0) != (b>=0) && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+ struct _pool_chunk *p,
+ struct _pool_chunk *prev_chunk,
+ size_t capacity)
+{
+ p->prev_chunk = prev_chunk;
+ p->size = 0;
+ p->capacity = capacity;
+ return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+ struct _pool_chunk *p;
+
+ p = malloc(size + sizeof(struct _pool_chunk));
+ if (unlikely (NULL == p))
+ longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+ jmp_buf *jmp,
+ size_t default_capacity,
+ size_t embedded_capacity)
+{
+ pool->jmp = jmp;
+ pool->current = pool->sentinel;
+ pool->first_free = NULL;
+ pool->default_capacity = default_capacity;
+ _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+ struct _pool_chunk *p = pool->current;
+ do {
+ while (NULL != p) {
+ struct _pool_chunk *prev = p->prev_chunk;
+ if (p != pool->sentinel)
+ free(p);
+ p = prev;
+ }
+ p = pool->first_free;
+ pool->first_free = NULL;
+ } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+ struct pool *pool,
+ size_t size)
+{
+ struct _pool_chunk *chunk;
+ void *obj;
+ size_t capacity;
+
+ /* If the allocation is smaller than the default chunk size then
+ * try getting a chunk off the free list. Force alloc of a new
+ * chunk for large requests. */
+ capacity = size;
+ chunk = NULL;
+ if (size < pool->default_capacity) {
+ capacity = pool->default_capacity;
+ chunk = pool->first_free;
+ if (chunk) {
+ pool->first_free = chunk->prev_chunk;
+ _pool_chunk_init(chunk, pool->current, chunk->capacity);
+ }
+ }
+
+ if (NULL == chunk)
+ chunk = _pool_chunk_create (pool, capacity);
+ pool->current = chunk;
+
+ obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+}
+
+/* Allocate size bytes from the pool. The first allocated address
+ * returned from a pool is aligned to sizeof(void*). Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated. Returns the address of a new memory area or %NULL on
+ * allocation failures. The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+ struct _pool_chunk *chunk = pool->current;
+
+ if (size <= chunk->capacity - chunk->size) {
+ void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+ } else {
+ return _pool_alloc_from_new_chunk(pool, size);
+ }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+ /* Transfer all used chunks to the chunk free list. */
+ struct _pool_chunk *chunk = pool->current;
+ if (chunk != pool->sentinel) {
+ while (chunk->prev_chunk != pool->sentinel) {
+ chunk = chunk->prev_chunk;
+ }
+ chunk->prev_chunk = pool->first_free;
+ pool->first_free = pool->current;
+ }
+ /* Reset the sentinel as the current chunk. */
+ pool->current = pool->sentinel;
+ pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning. After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+ cells->cursor = &cells->head;
+}
+
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+ if (x < cells->cursor->x) {
+ cells->cursor = cells->rewind;
+ if (x < cells->cursor->x)
+ cells->cursor = &cells->head;
+ }
+}
+
+inline static void
+cell_list_set_rewind (struct cell_list *cells)
+{
+ cells->rewind = cells->cursor;
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+ pool_init(cells->cell_pool.base, jmp,
+ 256*sizeof(struct cell),
+ sizeof(cells->cell_pool.embedded));
+ cells->tail.next = NULL;
+ cells->tail.x = INT_MAX;
+ cells->head.x = INT_MIN;
+ cells->head.next = &cells->tail;
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+ pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list. This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+ cell_list_rewind (cells);
+ cells->head.next = &cells->tail;
+ pool_reset (cells->cell_pool.base);
+}
+
+inline static struct cell *
+cell_list_alloc (struct cell_list *cells,
+ struct cell *tail,
+ int x)
+{
+ struct cell *cell;
+
+ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+ cell->next = tail->next;
+ tail->next = cell;
+ cell->x = x;
+ *(uint32_t *)&cell->uncovered_area = 0;
+
+ return cell;
+}
+
+/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
+ * needed to be allocated but couldn't be. Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+
+ if (tail->x == x)
+ return tail;
+
+ while (1) {
+ UNROLL3({
+ if (tail->next->x > x)
+ break;
+ tail = tail->next;
+ });
+ }
+
+ if (tail->x != x)
+ tail = cell_list_alloc (cells, tail, x);
+ return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2. This is exactly equivalent
+ * to
+ *
+ * pair.cell1 = cell_list_find(cells, x1);
+ * pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+ struct cell_pair pair;
+
+ pair.cell1 = cells->cursor;
+ while (1) {
+ UNROLL3({
+ if (pair.cell1->next->x > x1)
+ break;
+ pair.cell1 = pair.cell1->next;
+ });
+ }
+ if (pair.cell1->x != x1)
+ pair.cell1 = cell_list_alloc (cells, pair.cell1, x1);
+
+ pair.cell2 = pair.cell1;
+ while (1) {
+ UNROLL3({
+ if (pair.cell2->next->x > x2)
+ break;
+ pair.cell2 = pair.cell2->next;
+ });
+ }
+ if (pair.cell2->x != x2)
+ pair.cell2 = cell_list_alloc (cells, pair.cell2, x2);
+
+ cells->cursor = pair.cell2;
+ return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+ grid_scaled_x_t x1,
+ grid_scaled_x_t x2)
+{
+ int ix1, fx1;
+ int ix2, fx2;
+
+ if (x1 == x2)
+ return;
+
+ GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+ if (ix1 != ix2) {
+ struct cell_pair p;
+ p = cell_list_find_pair(cells, ix1, ix2);
+ p.cell1->uncovered_area += 2*fx1;
+ ++p.cell1->covered_height;
+ p.cell2->uncovered_area -= 2*fx2;
+ --p.cell2->covered_height;
+ } else {
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->uncovered_area += 2*(fx1-fx2);
+ }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change. In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.) */
+static void
+cell_list_render_edge(struct cell_list *cells,
+ struct edge *edge,
+ int sign)
+{
+ grid_scaled_x_t fx;
+ struct cell *cell;
+ int ix;
+
+ GRID_X_TO_INT_FRAC(edge->x.quo, ix, fx);
+
+ /* We always know that ix1 is >= the cell list cursor in this
+ * case due to the no-intersections precondition. */
+ cell = cell_list_find(cells, ix);
+ cell->covered_height += sign*GRID_Y;
+ cell->uncovered_area += sign*2*fx*GRID_Y;
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+ polygon->ymin = polygon->ymax = 0;
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ pool_init (polygon->edge_pool.base, jmp,
+ 8192 - sizeof (struct _pool_chunk),
+ sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static glitter_status_t
+polygon_reset (struct polygon *polygon,
+ grid_scaled_y_t ymin,
+ grid_scaled_y_t ymax)
+{
+ unsigned h = ymax - ymin;
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
+
+ pool_reset(polygon->edge_pool.base);
+
+ if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
+ goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+ polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+ sizeof (struct edge *));
+ if (unlikely (NULL == polygon->y_buckets))
+ goto bail_no_mem;
+ }
+ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
+ return GLITTER_STATUS_SUCCESS;
+
+bail_no_mem:
+ polygon->ymin = 0;
+ polygon->ymax = 0;
+ return GLITTER_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
+ struct edge *e)
+{
+ unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+ struct edge **ptail = &polygon->y_buckets[ix];
+ e->next = *ptail;
+ *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+ const cairo_edge_t *edge)
+{
+ struct edge *e;
+ grid_scaled_x_t dx;
+ grid_scaled_y_t dy;
+ grid_scaled_y_t ytop, ybot;
+ grid_scaled_y_t ymin = polygon->ymin;
+ grid_scaled_y_t ymax = polygon->ymax;
+
+ if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+ return;
+
+ e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+ e->dir = edge->dir;
+
+ ytop = edge->top >= ymin ? edge->top : ymin;
+ ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+ e->ytop = ytop;
+ e->height_left = ybot - ytop;
+
+ if (dx == 0) {
+ e->vertical = TRUE;
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ e->dxdy.quo = 0;
+ e->dxdy.rem = 0;
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_divrem (dx, dy);
+ if (ytop == edge->line.p1.y) {
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ } else {
+ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+ e->x.rem -= dy; /* Bias the remainder for faster
+ * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+ active->head.vertical = 1;
+ active->head.height_left = INT_MAX;
+ active->head.x.quo = INT_MIN;
+ active->head.prev = NULL;
+ active->head.next = &active->tail;
+ active->tail.prev = &active->head;
+ active->tail.next = NULL;
+ active->tail.x.quo = INT_MAX;
+ active->tail.height_left = INT_MAX;
+ active->tail.vertical = 1;
+ active->min_height = 0;
+ active->is_vertical = 1;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+ active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ * - head_a: The head of the first list.
+ * - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+ struct edge *head, **next, *prev;
+ int32_t x;
+
+ prev = head_a->prev;
+ next = &head;
+ if (head_a->x.quo <= head_b->x.quo) {
+ head = head_a;
+ } else {
+ head = head_b;
+ head_b->prev = prev;
+ goto start_with_b;
+ }
+
+ do {
+ x = head_b->x.quo;
+ while (head_a != NULL && head_a->x.quo <= x) {
+ prev = head_a;
+ next = &head_a->next;
+ head_a = head_a->next;
+ }
+
+ head_b->prev = prev;
+ *next = head_b;
+ if (head_a == NULL)
+ return head;
+
+start_with_b:
+ x = head_a->x.quo;
+ while (head_b != NULL && head_b->x.quo <= x) {
+ prev = head_b;
+ next = &head_b->next;
+ head_b = head_b->next;
+ }
+
+ head_a->prev = prev;
+ *next = head_a;
+ if (head_b == NULL)
+ return head;
+ } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ * - list: The list to be sorted; list cannot be NULL.
+ * - limit: Recursion limit.
+ * Output:
+ * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ * input list; if the input list has fewer elements, head_out be a sorted list
+ * containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge *list,
+ unsigned int level,
+ struct edge **head_out)
+{
+ struct edge *head_other, *remaining;
+ unsigned int i;
+
+ head_other = list->next;
+
+ if (head_other == NULL) {
+ *head_out = list;
+ return NULL;
+ }
+
+ remaining = head_other->next;
+ if (list->x.quo <= head_other->x.quo) {
+ *head_out = list;
+ head_other->next = NULL;
+ } else {
+ *head_out = head_other;
+ head_other->prev = list->prev;
+ head_other->next = list;
+ list->prev = head_other;
+ list->next = NULL;
+ }
+
+ for (i = 0; i < level && remaining; i++) {
+ remaining = sort_edges (remaining, i, &head_other);
+ *head_out = merge_sorted_edges (*head_out, head_other);
+ }
+
+ return remaining;
+}
+
+static struct edge *
+merge_unsorted_edges (struct edge *head, struct edge *unsorted)
+{
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ return merge_sorted_edges (head, unsorted);
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+can_do_full_row (struct active_list *active)
+{
+ const struct edge *e;
+
+ /* Recomputes the minimum height of all edges on the active
+ * list if we have been dropping edges. */
+ if (active->min_height <= 0) {
+ int min_height = INT_MAX;
+ int is_vertical = 1;
+
+ e = active->head.next;
+ while (NULL != e) {
+ if (e->height_left < min_height)
+ min_height = e->height_left;
+ is_vertical &= e->vertical;
+ e = e->next;
+ }
+
+ active->is_vertical = is_vertical;
+ active->min_height = min_height;
+ }
+
+ if (active->min_height < GRID_Y)
+ return 0;
+
+ return active->is_vertical;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_bucket(struct active_list *active,
+ struct edge *edges)
+{
+ active->head.next = merge_unsorted_edges (active->head.next, edges);
+}
+
+inline static void
+polygon_fill_buckets (struct active_list *active,
+ struct edge *edge,
+ int y,
+ struct edge **buckets)
+{
+ grid_scaled_y_t min_height = active->min_height;
+ int is_vertical = active->is_vertical;
+
+ while (edge) {
+ struct edge *next = edge->next;
+ int suby = edge->ytop - y;
+ if (buckets[suby])
+ buckets[suby]->prev = edge;
+ edge->next = buckets[suby];
+ edge->prev = NULL;
+ buckets[suby] = edge;
+ if (edge->height_left < min_height)
+ min_height = edge->height_left;
+ is_vertical &= edge->vertical;
+ edge = next;
+ }
+
+ active->is_vertical = is_vertical;
+ active->min_height = min_height;
+}
+
+inline static void
+sub_row (struct active_list *active,
+ struct cell_list *coverages,
+ unsigned int mask)
+{
+ struct edge *edge = active->head.next;
+ int xstart = INT_MIN, prev_x = INT_MIN;
+ int winding = 0;
+
+ cell_list_rewind (coverages);
+
+ while (&active->tail != edge) {
+ struct edge *next = edge->next;
+ int xend = edge->x.quo;
+
+ if (--edge->height_left) {
+ edge->x.quo += edge->dxdy.quo;
+ edge->x.rem += edge->dxdy.rem;
+ if (edge->x.rem >= 0) {
+ ++edge->x.quo;
+ edge->x.rem -= edge->dy;
+ }
+
+ if (edge->x.quo < prev_x) {
+ struct edge *pos = edge->prev;
+ pos->next = next;
+ next->prev = pos;
+ do {
+ pos = pos->prev;
+ } while (edge->x.quo < pos->x.quo);
+ pos->next->prev = edge;
+ edge->next = pos->next;
+ edge->prev = pos;
+ pos->next = edge;
+ } else
+ prev_x = edge->x.quo;
+ } else {
+ edge->prev->next = next;
+ next->prev = edge->prev;
+ }
+
+ winding += edge->dir;
+ if ((winding & mask) == 0) {
+ if (next->x.quo != xend) {
+ cell_list_add_subspan (coverages, xstart, xend);
+ xstart = INT_MIN;
+ }
+ } else if (xstart == INT_MIN)
+ xstart = xend;
+
+ edge = next;
+ }
+}
+
+inline static void dec (struct edge *e, int h)
+{
+ e->height_left -= h;
+ if (e->height_left == 0) {
+ e->prev->next = e->next;
+ e->next->prev = e->prev;
+ }
+}
+
+static void
+full_row (struct active_list *active,
+ struct cell_list *coverages,
+ unsigned int mask)
+{
+ struct edge *left = active->head.next;
+
+ while (&active->tail != left) {
+ struct edge *right;
+ int winding;
+
+ dec (left, GRID_Y);
+
+ winding = left->dir;
+ right = left->next;
+ do {
+ dec (right, GRID_Y);
+
+ winding += right->dir;
+ if ((winding & mask) == 0 && right->next->x.quo != right->x.quo)
+ break;
+
+ right = right->next;
+ } while (1);
+
+ cell_list_set_rewind (coverages);
+ cell_list_render_edge (coverages, left, +1);
+ cell_list_render_edge (coverages, right, -1);
+
+ left = right->next;
+ }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+ polygon_init(converter->polygon, jmp);
+ active_list_init(converter->active);
+ cell_list_init(converter->coverages, jmp);
+ converter->xmin=0;
+ converter->ymin=0;
+ converter->xmax=0;
+ converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *self)
+{
+ if (self->spans != self->spans_embedded)
+ free (self->spans);
+
+ polygon_fini(self->polygon);
+ cell_list_fini(self->coverages);
+
+ self->xmin=0;
+ self->ymin=0;
+ self->xmax=0;
+ self->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+ /* Clamp to max/min representable scaled number. */
+ if (i >= 0) {
+ if (i >= INT_MAX/scale)
+ i = INT_MAX/scale;
+ }
+ else {
+ if (i <= INT_MIN/scale)
+ i = INT_MIN/scale;
+ }
+ return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+I glitter_status_t
+glitter_scan_converter_reset(
+ glitter_scan_converter_t *converter,
+ int xmin, int ymin,
+ int xmax, int ymax)
+{
+ glitter_status_t status;
+
+ converter->xmin = 0; converter->xmax = 0;
+ converter->ymin = 0; converter->ymax = 0;
+
+ if (xmax - xmin > ARRAY_LENGTH(converter->spans_embedded)) {
+ converter->spans = _cairo_malloc_ab (xmax - xmin,
+ sizeof (cairo_half_open_span_t));
+ if (unlikely (converter->spans == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else
+ converter->spans = converter->spans_embedded;
+
+ xmin = int_to_grid_scaled_x(xmin);
+ ymin = int_to_grid_scaled_y(ymin);
+ xmax = int_to_grid_scaled_x(xmax);
+ ymax = int_to_grid_scaled_y(ymax);
+
+ active_list_reset(converter->active);
+ cell_list_reset(converter->coverages);
+ status = polygon_reset(converter->polygon, ymin, ymax);
+ if (status)
+ return status;
+
+ converter->xmin = xmin;
+ converter->xmax = xmax;
+ converter->ymin = ymin;
+ converter->ymax = ymax;
+ return GLITTER_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ * These macros convert an input coordinate in the client's
+ * device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
+ long long tmp__ = (long long)(grid_scale) * (in); \
+ tmp__ >>= GLITTER_INPUT_BITS; \
+ (out) = tmp__; \
+} while (0)
+
+/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
+ * converter. The coordinates represent pixel positions scaled by
+ * 2**GLITTER_PIXEL_BITS. If this function fails then the scan
+ * converter should be reset or destroyed. Dir must be +1 or -1,
+ * with the latter reversing the orientation of the edge. */
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge)
+{
+ cairo_edge_t e;
+
+ INPUT_TO_GRID_Y (edge->top, e.top);
+ INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+ if (e.top >= e.bottom)
+ return;
+
+ /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+ INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+ if (e.line.p1.y == e.line.p2.y)
+ e.line.p2.y++; /* Fudge to prevent div-by-zero */
+
+ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+ INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+ e.dir = edge->dir;
+
+ polygon_add_edge (converter->polygon, &e);
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+ struct edge *edge;
+
+ count *= GRID_Y;
+ for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
+ edge->height_left -= count;
+ if (! edge->height_left) {
+ edge->prev->next = edge->next;
+ edge->next->prev = edge->prev;
+ }
+ }
+}
+
+static glitter_status_t
+blit_a8 (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ cairo_half_open_span_t *spans,
+ int y, int height,
+ int xmin, int xmax)
+{
+ struct cell *cell = cells->head.next;
+ int prev_x = xmin, last_x = -1;
+ int16_t cover = 0, last_cover = 0;
+ unsigned num_spans;
+
+ if (cell == &cells->tail)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Skip cells to the left of the clip region. */
+ while (cell->x < xmin) {
+ cover += cell->covered_height;
+ cell = cell->next;
+ }
+ cover *= GRID_X*2;
+
+ /* Form the spans from the coverages and areas. */
+ num_spans = 0;
+ for (; cell->x < xmax; cell = cell->next) {
+ int x = cell->x;
+ int16_t area;
+
+ if (x > prev_x && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ last_cover = cover;
+ last_x = prev_x;
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ if (area != last_cover) {
+ spans[num_spans].x = x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+ last_cover = area;
+ last_x = x;
+ ++num_spans;
+ }
+
+ prev_x = x+1;
+ }
+
+ if (prev_x <= xmax && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ last_cover = cover;
+ last_x = prev_x;
+ ++num_spans;
+ }
+
+ if (last_x < xmax && last_cover) {
+ spans[num_spans].x = xmax;
+ spans[num_spans].coverage = 0;
+ ++num_spans;
+ }
+
+ /* Dump them into the renderer. */
+ return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+#define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0)
+static glitter_status_t
+blit_a1 (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ cairo_half_open_span_t *spans,
+ int y, int height,
+ int xmin, int xmax)
+{
+ struct cell *cell = cells->head.next;
+ int prev_x = xmin, last_x = -1;
+ int16_t cover = 0;
+ uint8_t coverage, last_cover = 0;
+ unsigned num_spans;
+
+ if (cell == &cells->tail)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Skip cells to the left of the clip region. */
+ while (cell->x < xmin) {
+ cover += cell->covered_height;
+ cell = cell->next;
+ }
+ cover *= GRID_X*2;
+
+ /* Form the spans from the coverages and areas. */
+ num_spans = 0;
+ for (; cell->x < xmax; cell = cell->next) {
+ int x = cell->x;
+ int16_t area;
+
+ coverage = GRID_AREA_TO_A1 (cover);
+ if (x > prev_x && coverage != last_cover) {
+ last_x = spans[num_spans].x = prev_x;
+ last_cover = spans[num_spans].coverage = coverage;
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ coverage = GRID_AREA_TO_A1 (area);
+ if (coverage != last_cover) {
+ last_x = spans[num_spans].x = x;
+ last_cover = spans[num_spans].coverage = coverage;
+ ++num_spans;
+ }
+
+ prev_x = x+1;
+ }
+
+ coverage = GRID_AREA_TO_A1 (cover);
+ if (prev_x <= xmax && coverage != last_cover) {
+ last_x = spans[num_spans].x = prev_x;
+ last_cover = spans[num_spans].coverage = coverage;
+ ++num_spans;
+ }
+
+ if (last_x < xmax && last_cover) {
+ spans[num_spans].x = xmax;
+ spans[num_spans].coverage = 0;
+ ++num_spans;
+ }
+ if (num_spans == 1)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Dump them into the renderer. */
+ return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+
+I void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+ unsigned int winding_mask,
+ int antialias,
+ cairo_span_renderer_t *renderer)
+{
+ int i, j;
+ int ymax_i = converter->ymax / GRID_Y;
+ int ymin_i = converter->ymin / GRID_Y;
+ int xmin_i, xmax_i;
+ int h = ymax_i - ymin_i;
+ struct polygon *polygon = converter->polygon;
+ struct cell_list *coverages = converter->coverages;
+ struct active_list *active = converter->active;
+ struct edge *buckets[GRID_Y] = { 0 };
+
+ xmin_i = converter->xmin / GRID_X;
+ xmax_i = converter->xmax / GRID_X;
+ if (xmin_i >= xmax_i)
+ return;
+
+ /* Render each pixel row. */
+ for (i = 0; i < h; i = j) {
+ int do_full_row = 0;
+
+ j = i + 1;
+
+ /* Determine if we can ignore this row or use the full pixel
+ * stepper. */
+ if (! polygon->y_buckets[i]) {
+ if (active->head.next == &active->tail) {
+ active->min_height = INT_MAX;
+ active->is_vertical = 1;
+ for (; j < h && ! polygon->y_buckets[j]; j++)
+ ;
+ continue;
+ }
+
+ do_full_row = can_do_full_row (active);
+ }
+
+ if (do_full_row) {
+ /* Step by a full pixel row's worth. */
+ full_row (active, coverages, winding_mask);
+
+ if (active->is_vertical) {
+ while (j < h &&
+ polygon->y_buckets[j] == NULL &&
+ active->min_height >= 2*GRID_Y)
+ {
+ active->min_height -= GRID_Y;
+ j++;
+ }
+ if (j != i + 1)
+ step_edges (active, j - (i + 1));
+ }
+ } else {
+ int sub;
+
+ polygon_fill_buckets (active,
+ polygon->y_buckets[i],
+ (i+ymin_i)*GRID_Y,
+ buckets);
+
+ /* Subsample this row. */
+ for (sub = 0; sub < GRID_Y; sub++) {
+ if (buckets[sub]) {
+ active_list_merge_edges_from_bucket (active, buckets[sub]);
+ buckets[sub] = NULL;
+ }
+
+ sub_row (active, coverages, winding_mask);
+ }
+ }
+
+ if (antialias)
+ blit_a8 (coverages, renderer, converter->spans,
+ i+ymin_i, j-i, xmin_i, xmax_i);
+ else
+ blit_a1 (coverages, renderer, converter->spans,
+ i+ymin_i, j-i, xmin_i, xmax_i);
+ cell_list_reset (coverages);
+
+ active->min_height -= GRID_Y;
+ }
+}
+
+struct _cairo_tor22_scan_converter {
+ cairo_scan_converter_t base;
+
+ glitter_scan_converter_t converter[1];
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+
+ jmp_buf jmp;
+};
+
+typedef struct _cairo_tor22_scan_converter cairo_tor22_scan_converter_t;
+
+static void
+_cairo_tor22_scan_converter_destroy (void *converter)
+{
+ cairo_tor22_scan_converter_t *self = converter;
+ if (self == NULL) {
+ return;
+ }
+ _glitter_scan_converter_fini (self->converter);
+ free(self);
+}
+
+cairo_status_t
+_cairo_tor22_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon)
+{
+ cairo_tor22_scan_converter_t *self = converter;
+ int i;
+
+#if 0
+ FILE *file = fopen ("polygon.txt", "w");
+ _cairo_debug_print_polygon (file, polygon);
+ fclose (file);
+#endif
+
+ for (i = 0; i < polygon->num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tor22_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_tor22_scan_converter_t *self = converter;
+ cairo_status_t status;
+
+ if ((status = setjmp (self->jmp)))
+ return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+ glitter_scan_converter_render (self->converter,
+ self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
+ self->antialias != CAIRO_ANTIALIAS_NONE,
+ renderer);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_tor22_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias)
+{
+ cairo_tor22_scan_converter_t *self;
+ cairo_status_t status;
+
+ self = malloc (sizeof(struct _cairo_tor22_scan_converter));
+ if (unlikely (self == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail_nomem;
+ }
+
+ self->base.destroy = _cairo_tor22_scan_converter_destroy;
+ self->base.generate = _cairo_tor22_scan_converter_generate;
+
+ _glitter_scan_converter_init (self->converter, &self->jmp);
+ status = glitter_scan_converter_reset (self->converter,
+ xmin, ymin, xmax, ymax);
+ if (unlikely (status))
+ goto bail;
+
+ self->fill_rule = fill_rule;
+ self->antialias = antialias;
+
+ return &self->base;
+
+ bail:
+ self->base.destroy(&self->base);
+ bail_nomem:
+ return _cairo_scan_converter_create_in_error (status);
+}
diff --git a/src/cairo-toy-font-face.c b/src/cairo-toy-font-face.c
new file mode 100755
index 000000000..363b9a284
--- /dev/null
+++ b/src/cairo-toy-font-face.c
@@ -0,0 +1,523 @@
+/* -*- 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;
+
+ 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) {
+ cairo_font_face_reference (&font_face->base);
+ _cairo_toy_font_face_hash_table_unlock ();
+ return &font_face->base;
+ }
+
+ /* remove the bad font from the hash table */
+ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+ }
+
+ /* Otherwise create it and insert into hash table. */
+ font_face = 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;
+
+ 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;
+ }
+
+ /* Font faces in SUCCESS status are guaranteed to be in the
+ * hashtable. Font faces in an error status are removed from the
+ * hashtable if they are found during a lookup, thus they should
+ * only be removed if they are in the hashtable. */
+ if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) ||
+ _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face)
+ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+
+ _cairo_toy_font_face_hash_table_unlock ();
+
+ _cairo_toy_font_face_fini (font_face);
+}
+
+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/src/cairo-traps-compositor.c b/src/cairo-traps-compositor.c
new file mode 100755
index 000000000..5d561f2be
--- /dev/null
+++ b/src/cairo-traps-compositor.c
@@ -0,0 +1,2403 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-inline.h"
+#include "cairo-boxes-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-region-private.h"
+#include "cairo-spans-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-tristrip-private.h"
+
+typedef cairo_int_status_t
+(*draw_func_t) (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip);
+
+static void do_unaligned_row(void (*blt)(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage),
+ void *closure,
+ const cairo_box_t *b,
+ int tx, int y, int h,
+ uint16_t coverage)
+{
+ int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
+ int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
+ if (x2 > x1) {
+ if (! _cairo_fixed_is_integer (b->p1.x)) {
+ blt(closure, x1, y, 1, h,
+ coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
+ x1++;
+ }
+
+ if (x2 > x1)
+ blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
+
+ if (! _cairo_fixed_is_integer (b->p2.x))
+ blt(closure, x2, y, 1, h,
+ coverage * _cairo_fixed_fractional_part (b->p2.x));
+ } else
+ blt(closure, x1, y, 1, h,
+ coverage * (b->p2.x - b->p1.x));
+}
+
+static void do_unaligned_box(void (*blt)(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage),
+ void *closure,
+ const cairo_box_t *b, int tx, int ty)
+{
+ int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
+ int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
+ if (y2 > y1) {
+ if (! _cairo_fixed_is_integer (b->p1.y)) {
+ do_unaligned_row(blt, closure, b, tx, y1, 1,
+ 256 - _cairo_fixed_fractional_part (b->p1.y));
+ y1++;
+ }
+
+ if (y2 > y1)
+ do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
+
+ if (! _cairo_fixed_is_integer (b->p2.y))
+ do_unaligned_row(blt, closure, b, tx, y2, 1,
+ _cairo_fixed_fractional_part (b->p2.y));
+ } else
+ do_unaligned_row(blt, closure, b, tx, y1, 1,
+ b->p2.y - b->p1.y);
+}
+
+struct blt_in {
+ const cairo_traps_compositor_t *compositor;
+ cairo_surface_t *dst;
+ cairo_boxes_t boxes;
+};
+
+static void blt_in(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct blt_in *info = closure;
+ cairo_color_t color;
+
+ if (CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage))
+ return;
+
+ _cairo_box_from_integers (&info->boxes.chunks.base[0], x, y, w, h);
+
+ _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
+ info->compositor->fill_boxes (info->dst,
+ CAIRO_OPERATOR_IN, &color,
+ &info->boxes);
+}
+
+static void
+add_rect_with_offset (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2, int dx, int dy)
+{
+ cairo_box_t box;
+ cairo_int_status_t status;
+
+ box.p1.x = _cairo_fixed_from_int (x1 - dx);
+ box.p1.y = _cairo_fixed_from_int (y1 - dy);
+ box.p2.x = _cairo_fixed_from_int (x2 - dx);
+ box.p2.y = _cairo_fixed_from_int (y2 - dy);
+
+ status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+}
+
+static cairo_int_status_t
+combine_clip_as_traps (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *mask,
+ const cairo_clip_t *clip,
+ const cairo_rectangle_int_t *extents,
+ const cairo_bool_t draw_color_glyph)
+{
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+ cairo_traps_t traps;
+ cairo_surface_t *src;
+ cairo_box_t box;
+ cairo_rectangle_int_t fixup;
+ int src_x, src_y;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = _cairo_clip_get_polygon (clip, &polygon,
+ &fill_rule, &antialias);
+ if (status)
+ return status;
+
+ _cairo_traps_init (&traps);
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ fill_rule);
+ _cairo_polygon_fini (&polygon);
+ if (unlikely (status))
+ return status;
+
+ src = compositor->pattern_to_surface (mask, NULL, FALSE,
+ extents, NULL,
+ &src_x, &src_y);
+ if (unlikely (src->status)) {
+ _cairo_traps_fini (&traps);
+ return src->status;
+ }
+
+ if (draw_color_glyph)
+ status = compositor->composite_traps (mask, CAIRO_OPERATOR_IN, mask,
+ 0, 0,
+ extents->x, extents->y,
+ extents,
+ antialias, &traps);
+ else
+ status = compositor->composite_traps (mask, CAIRO_OPERATOR_IN, src,
+ src_x, src_y,
+ extents->x, extents->y,
+ extents,
+ antialias, &traps);
+
+ _cairo_traps_extents (&traps, &box);
+ _cairo_box_round_to_rectangle (&box, &fixup);
+ _cairo_traps_fini (&traps);
+ cairo_surface_destroy (src);
+
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_rectangle_intersect (&fixup, extents))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (fixup.width < extents->width || fixup.height < extents->height) {
+ cairo_boxes_t clear;
+
+ _cairo_boxes_init (&clear);
+
+ /* top */
+ if (fixup.y != extents->y) {
+ add_rect_with_offset (&clear,
+ extents->x, extents->y,
+ extents->x + extents->width,
+ fixup.y,
+ extents->x, extents->y);
+ }
+ /* left */
+ if (fixup.x != extents->x) {
+ add_rect_with_offset (&clear,
+ extents->x, fixup.y,
+ fixup.x,
+ fixup.y + fixup.height,
+ extents->x, extents->y);
+ }
+ /* right */
+ if (fixup.x + fixup.width != extents->x + extents->width) {
+ add_rect_with_offset (&clear,
+ fixup.x + fixup.width,
+ fixup.y,
+ extents->x + extents->width,
+ fixup.y + fixup.height,
+ extents->x, extents->y);
+ }
+ /* bottom */
+ if (fixup.y + fixup.height != extents->y + extents->height) {
+ add_rect_with_offset (&clear,
+ extents->x,
+ fixup.y + fixup.height,
+ extents->x + extents->width,
+ extents->y + extents->height,
+ extents->x, extents->y);
+ }
+
+ status = compositor->fill_boxes (mask,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+
+ _cairo_boxes_fini (&clear);
+ }
+
+ return status;
+}
+
+static cairo_status_t
+__clip_to_surface (const cairo_traps_compositor_t *compositor,
+ const cairo_composite_rectangles_t *composite,
+ const cairo_rectangle_int_t *extents,
+ cairo_surface_t **surface)
+{
+ cairo_surface_t *mask;
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+ cairo_traps_t traps;
+ cairo_boxes_t clear;
+ cairo_surface_t *src;
+ int src_x, src_y;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = _cairo_clip_get_polygon (composite->clip, &polygon,
+ &fill_rule, &antialias);
+ if (status)
+ return status;
+
+ _cairo_traps_init (&traps);
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ fill_rule);
+ _cairo_polygon_fini (&polygon);
+ if (unlikely (status))
+ return status;
+
+ mask = _cairo_surface_create_similar_scratch (composite->surface,
+ CAIRO_CONTENT_ALPHA,
+ extents->width,
+ extents->height);
+ if (unlikely (mask->status)) {
+ _cairo_traps_fini (&traps);
+ return status;
+ }
+
+ src = compositor->pattern_to_surface (mask, NULL, FALSE,
+ extents, NULL,
+ &src_x, &src_y);
+ if (unlikely (status = src->status))
+ goto error;
+
+ status = compositor->acquire (mask);
+ if (unlikely (status))
+ goto error;
+
+ _cairo_boxes_init_from_rectangle (&clear,
+ 0, 0,
+ extents->width,
+ extents->height);
+ status = compositor->fill_boxes (mask,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+ if (unlikely (status))
+ goto error_release;
+
+ status = compositor->composite_traps (mask, CAIRO_OPERATOR_ADD, src,
+ src_x, src_y,
+ extents->x, extents->y,
+ extents,
+ antialias, &traps);
+ if (unlikely (status))
+ goto error_release;
+
+ compositor->release (mask);
+ *surface = mask;
+out:
+ cairo_surface_destroy (src);
+ _cairo_traps_fini (&traps);
+ return status;
+
+error_release:
+ compositor->release (mask);
+error:
+ cairo_surface_destroy (mask);
+ goto out;
+}
+
+static cairo_surface_t *
+traps_get_clip_surface (const cairo_traps_compositor_t *compositor,
+ const cairo_composite_rectangles_t *composite,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *surface = NULL;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = __clip_to_surface (compositor, composite, extents, &surface);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ surface = _cairo_surface_create_similar_solid (composite->surface,
+ CAIRO_CONTENT_ALPHA,
+ extents->width,
+ extents->height,
+ CAIRO_COLOR_WHITE);
+ if (unlikely (surface->status))
+ return surface;
+
+ status = _cairo_clip_combine_with_surface (composite->clip, surface,
+ extents->x, extents->y);
+ }
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ surface = _cairo_surface_create_in_error (status);
+ }
+
+ return surface;
+}
+
+static void blt_unaligned_boxes(const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *surface,
+ int dx, int dy,
+ cairo_box_t *boxes,
+ int num_boxes)
+{
+ struct blt_in info;
+ int i;
+
+ info.compositor = compositor;
+ info.dst = surface;
+ _cairo_boxes_init (&info.boxes);
+ info.boxes.num_boxes = 1;
+ for (i = 0; i < num_boxes; i++) {
+ cairo_box_t *b = &boxes[i];
+
+ if (! _cairo_fixed_is_integer (b->p1.x) ||
+ ! _cairo_fixed_is_integer (b->p1.y) ||
+ ! _cairo_fixed_is_integer (b->p2.x) ||
+ ! _cairo_fixed_is_integer (b->p2.y))
+ {
+ do_unaligned_box(blt_in, &info, b, dx, dy);
+ }
+ }
+}
+
+static cairo_surface_t *
+create_composite_mask (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *draw_closure,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *surface, *src;
+ cairo_int_status_t status;
+ int src_x, src_y;
+ cairo_bool_t draw_color_glyph = FALSE;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ surface = _cairo_surface_create_similar_scratch (dst, CAIRO_CONTENT_ALPHA,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (surface->status))
+ return surface;
+ /* FIXME: This is more like an ugly hack and wasteful. Reason
+ for this code is that we don't know whether the mask surface
+ should alpha-only or argb32 before we render a glyph.
+ */
+
+ redo:
+ src = compositor->pattern_to_surface (surface,
+ &_cairo_pattern_white.base,
+ FALSE,
+ &extents->bounded,
+ &extents->bounded,
+ &src_x, &src_y);
+ if (unlikely (src->status)) {
+ cairo_surface_destroy (surface);
+ return src;
+ }
+
+ status = compositor->acquire (surface);
+ if (unlikely (status)) {
+ cairo_surface_destroy (src);
+ cairo_surface_destroy (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ if (!surface->is_clear) {
+ cairo_boxes_t clear;
+
+ _cairo_boxes_init_from_rectangle (&clear,
+ 0, 0,
+ extents->bounded.width,
+ extents->bounded.height);
+ status = compositor->fill_boxes (surface,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+ if (unlikely (status))
+ goto error;
+
+ surface->is_clear = TRUE;
+ }
+
+ if (mask_func) {
+ status = mask_func (compositor, surface, draw_closure,
+ CAIRO_OPERATOR_SOURCE, src, src_x, src_y,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded, extents->clip);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ surface->is_clear = FALSE;
+ goto out;
+ }
+ if (unlikely (status != CAIRO_INT_STATUS_UNSUPPORTED))
+ goto error;
+ }
+
+ /* Is it worth setting the clip region here? */
+ status = draw_func (compositor, surface, draw_closure,
+ CAIRO_OPERATOR_ADD, src, src_x, src_y,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded, NULL);
+ if (unlikely (status)){
+ if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA)
+ goto error;
+ else {
+ compositor->release (surface);
+ cairo_surface_destroy (surface);
+ cairo_surface_destroy (src);
+ surface = _cairo_surface_create_similar_scratch (dst, CAIRO_CONTENT_COLOR_ALPHA,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (surface->status))
+ return surface;
+ /* we are drawing color glyph */
+ draw_color_glyph = TRUE;
+ goto redo;
+ }
+ }
+
+ surface->is_clear = FALSE;
+ if (extents->clip->path != NULL) {
+ status = combine_clip_as_traps (compositor, surface,
+ extents->clip, &extents->bounded,
+ draw_color_glyph);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_clip_combine_with_surface (extents->clip, surface,
+ extents->bounded.x,
+ extents->bounded.y);
+ }
+ if (unlikely (status))
+ goto error;
+ } else if (extents->clip->boxes) {
+ blt_unaligned_boxes(compositor, surface,
+ extents->bounded.x, extents->bounded.y,
+ extents->clip->boxes, extents->clip->num_boxes);
+
+ }
+
+out:
+ compositor->release (surface);
+ cairo_surface_destroy (src);
+ return surface;
+
+error:
+ compositor->release (surface);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ cairo_surface_destroy (surface);
+ surface = _cairo_surface_create_in_error (status);
+ }
+ cairo_surface_destroy (src);
+ return surface;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+clip_and_composite_with_mask (const cairo_traps_compositor_t *compositor,
+ const cairo_composite_rectangles_t*extents,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ void *draw_closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x, int src_y)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_surface_t *mask;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ mask = create_composite_mask (compositor, dst, draw_closure,
+ draw_func, mask_func,
+ extents);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ if (mask->is_clear)
+ goto skip;
+
+ if (mask->content == CAIRO_CONTENT_ALPHA) {
+ /* This is real mask */
+ if (src != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
+ compositor->composite (dst, op, src, mask,
+ extents->bounded.x + src_x,
+ extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ } else {
+ compositor->composite (dst, op, mask, NULL,
+ 0, 0,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+ } else {
+ compositor->composite (dst, op, mask, NULL,
+ 0, 0,
+ extents->bounded.x + src_x,
+ extents->bounded.y + src_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+skip:
+ cairo_surface_destroy (mask);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+clip_and_composite_combine (const cairo_traps_compositor_t *compositor,
+ const cairo_composite_rectangles_t*extents,
+ draw_func_t draw_func,
+ void *draw_closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x, int src_y)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_surface_t *tmp, *clip;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ tmp = _cairo_surface_create_similar_scratch (dst, dst->content,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (tmp->status)) {
+ status = tmp->status;
+ cairo_surface_destroy (tmp);
+ return status;
+ }
+
+ status = compositor->acquire (tmp);
+ if (unlikely (status)) {
+ cairo_surface_destroy (tmp);
+ return status;
+ }
+
+ compositor->composite (tmp,
+ dst->is_clear ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE,
+ dst, NULL,
+ extents->bounded.x, extents->bounded.y,
+ 0, 0,
+ 0, 0,
+ extents->bounded.width, extents->bounded.height);
+
+ status = draw_func (compositor, tmp, draw_closure, op,
+ src, src_x, src_y,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded, NULL);
+
+ if (unlikely (status))
+ goto cleanup;
+
+ clip = traps_get_clip_surface (compositor, extents, &extents->bounded);
+ if (unlikely ((status = clip->status)))
+ goto cleanup;
+
+ if (dst->is_clear) {
+ compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
+ 0, 0,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ } else {
+ compositor->lerp (dst, tmp, clip,
+ 0, 0,
+ 0,0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+ cairo_surface_destroy (clip);
+
+cleanup:
+ compositor->release (tmp);
+ cairo_surface_destroy (tmp);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+clip_and_composite_source (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ void *draw_closure,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *mask = NULL;
+ /* create a white color pattern */
+ cairo_pattern_t *white_pattern = _cairo_pattern_create_solid (CAIRO_COLOR_WHITE);
+ cairo_surface_t *white_mask =
+ compositor->pattern_to_surface (dst, white_pattern, TRUE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (white_mask->status))
+ goto skip;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ /* Create a surface that is mask IN clip */
+ mask = create_composite_mask (compositor, dst, draw_closure,
+ draw_func, mask_func,
+ extents);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ if (mask->is_clear)
+ goto skip;
+
+ if (dst->is_clear) {
+ compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ } else {
+ if (mask->content == CAIRO_CONTENT_ALPHA)
+ compositor->lerp (dst, src, mask,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ else
+ compositor->lerp_color_glyph (dst, mask, white_mask,
+ 0, 0,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+
+skip:
+ cairo_surface_destroy (mask);
+ cairo_surface_destroy (white_mask);
+ cairo_pattern_destroy (white_pattern);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+ int iop = op;
+ switch (iop) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_operator_t op = extents->op;
+ const cairo_pattern_t *pattern = &extents->source_pattern.base;
+ return dst->is_clear &&
+ dst->content == CAIRO_CONTENT_ALPHA &&
+ _cairo_pattern_is_opaque_solid (pattern) &&
+ can_reduce_alpha_op (op);
+}
+
+static cairo_status_t
+fixup_unbounded_with_mask (const cairo_traps_compositor_t *compositor,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_surface_t *mask;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ /* XXX can we avoid querying the clip surface again? */
+ mask = traps_get_clip_surface (compositor, extents, &extents->unbounded);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ /* top */
+ if (extents->bounded.y != extents->unbounded.y) {
+ int x = extents->unbounded.x;
+ int y = extents->unbounded.y;
+ int width = extents->unbounded.width;
+ int height = extents->bounded.y - y;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ 0, 0,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* left */
+ if (extents->bounded.x != extents->unbounded.x) {
+ int x = extents->unbounded.x;
+ int y = extents->bounded.y;
+ int width = extents->bounded.x - x;
+ int height = extents->bounded.height;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ 0, y - extents->unbounded.y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* right */
+ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+ int x = extents->bounded.x + extents->bounded.width;
+ int y = extents->bounded.y;
+ int width = extents->unbounded.x + extents->unbounded.width - x;
+ int height = extents->bounded.height;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ x - extents->unbounded.x, y - extents->unbounded.y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* bottom */
+ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+ int x = extents->unbounded.x;
+ int y = extents->bounded.y + extents->bounded.height;
+ int width = extents->unbounded.width;
+ int height = extents->unbounded.y + extents->unbounded.height - y;
+
+ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+ 0, y - extents->unbounded.y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ cairo_surface_destroy (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_rect (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2)
+{
+ cairo_box_t box;
+ cairo_int_status_t status;
+
+ box.p1.x = _cairo_fixed_from_int (x1);
+ box.p1.y = _cairo_fixed_from_int (y1);
+ box.p2.x = _cairo_fixed_from_int (x2);
+ box.p2.y = _cairo_fixed_from_int (y2);
+
+ status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+fixup_unbounded (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_boxes_t clear, tmp;
+ cairo_box_t box;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (extents->bounded.width == extents->unbounded.width &&
+ extents->bounded.height == extents->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ assert (extents->clip->path == NULL);
+
+ /* subtract the drawn boxes from the unbounded area */
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (boxes == NULL) {
+ if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+ goto empty;
+ } else {
+ /* top */
+ if (extents->bounded.y != extents->unbounded.y) {
+ add_rect (&clear,
+ extents->unbounded.x, extents->unbounded.y,
+ extents->unbounded.x + extents->unbounded.width,
+ extents->bounded.y);
+ }
+ /* left */
+ if (extents->bounded.x != extents->unbounded.x) {
+ add_rect (&clear,
+ extents->unbounded.x, extents->bounded.y,
+ extents->bounded.x,
+ extents->bounded.y + extents->bounded.height);
+ }
+ /* right */
+ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+ add_rect (&clear,
+ extents->bounded.x + extents->bounded.width,
+ extents->bounded.y,
+ extents->unbounded.x + extents->unbounded.width,
+ extents->bounded.y + extents->bounded.height);
+ }
+ /* bottom */
+ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+ add_rect (&clear,
+ extents->unbounded.x,
+ extents->bounded.y + extents->bounded.height,
+ extents->unbounded.x + extents->unbounded.width,
+ extents->unbounded.y + extents->unbounded.height);
+ }
+ }
+ } else if (boxes->num_boxes) {
+ _cairo_boxes_init (&tmp);
+
+ assert (boxes->is_pixel_aligned);
+
+ status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ tmp.chunks.next = NULL;
+ if (unlikely (status))
+ goto error;
+ } else {
+empty:
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+
+ status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_INT_STATUS_SUCCESS);
+ }
+
+ /* Now intersect with the clip boxes */
+ if (extents->clip->num_boxes) {
+ _cairo_boxes_init_for_array (&tmp,
+ extents->clip->boxes,
+ extents->clip->num_boxes);
+ status = _cairo_boxes_intersect (&clear, &tmp, &clear);
+ if (unlikely (status))
+ goto error;
+ }
+
+ status = compositor->fill_boxes (dst,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+
+error:
+ _cairo_boxes_fini (&clear);
+ return status;
+}
+
+enum {
+ NEED_CLIP_REGION = 0x1,
+ NEED_CLIP_SURFACE = 0x2,
+ FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = 0;
+
+ if (extents->unbounded.width < extents->destination.width ||
+ extents->unbounded.height < extents->destination.height)
+ {
+ flags |= NEED_CLIP_REGION;
+ }
+
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+
+ return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = 0;
+ if (! extents->is_bounded) {
+ flags |= NEED_CLIP_REGION;
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+ }
+ if (extents->clip->path != NULL)
+ flags |= NEED_CLIP_SURFACE;
+ return flags;
+}
+
+static cairo_status_t
+clip_and_composite (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ draw_func_t draw_func,
+ draw_func_t mask_func,
+ void *draw_closure,
+ unsigned int need_clip)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_operator_t op = extents->op;
+ cairo_pattern_t *source = &extents->source_pattern.base;
+ cairo_surface_t *src;
+ int src_x, src_y;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (reduce_alpha_op (extents)) {
+ op = CAIRO_OPERATOR_ADD;
+ source = NULL;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ op = CAIRO_OPERATOR_DEST_OUT;
+ source = NULL;
+ }
+
+ compositor->acquire (dst);
+
+ if (need_clip & NEED_CLIP_REGION) {
+ const cairo_rectangle_int_t *limit;
+
+ if ((need_clip & FORCE_CLIP_REGION) == 0)
+ limit = &extents->unbounded;
+ else
+ limit = &extents->destination;
+
+ clip_region = _cairo_clip_get_region (extents->clip);
+ if (clip_region != NULL &&
+ cairo_region_contains_rectangle (clip_region,
+ limit) == CAIRO_REGION_OVERLAP_IN)
+ clip_region = NULL;
+
+ if (clip_region != NULL) {
+ status = compositor->set_clip_region (dst, clip_region);
+ if (unlikely (status)) {
+ compositor->release (dst);
+ return status;
+ }
+ }
+ }
+
+ if (extents->bounded.width == 0 || extents->bounded.height == 0)
+ goto skip;
+
+ src = compositor->pattern_to_surface (dst, source, FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (status = src->status))
+ goto error;
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = clip_and_composite_source (compositor, dst,
+ draw_func, mask_func, draw_closure,
+ src, src_x, src_y,
+ extents);
+ } else {
+ if (need_clip & NEED_CLIP_SURFACE) {
+ if (extents->is_bounded) {
+ status = clip_and_composite_with_mask (compositor, extents,
+ draw_func, mask_func,
+ draw_closure,
+ op, src, src_x, src_y);
+ } else {
+ status = clip_and_composite_combine (compositor, extents,
+ draw_func, draw_closure,
+ op, src, src_x, src_y);
+ }
+ } else {
+ status = draw_func (compositor,
+ dst, draw_closure,
+ op, src, src_x, src_y,
+ 0, 0,
+ &extents->bounded,
+ extents->clip);
+ }
+ }
+ cairo_surface_destroy (src);
+
+skip:
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+ if (need_clip & NEED_CLIP_SURFACE)
+ status = fixup_unbounded_with_mask (compositor, extents);
+ else
+ status = fixup_unbounded (compositor, extents, NULL);
+ }
+
+error:
+ if (clip_region)
+ compositor->set_clip_region (dst, NULL);
+
+ compositor->release (dst);
+
+ return status;
+}
+
+/* meta-ops */
+
+typedef struct {
+ cairo_traps_t traps;
+ cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+static cairo_int_status_t
+composite_traps (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ composite_traps_info_t *info = closure;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ return compositor->composite_traps (dst, op, src,
+ src_x - dst_x, src_y - dst_y,
+ dst_x, dst_y,
+ extents,
+ info->antialias, &info->traps);
+}
+
+typedef struct {
+ cairo_tristrip_t strip;
+ cairo_antialias_t antialias;
+} composite_tristrip_info_t;
+
+static cairo_int_status_t
+composite_tristrip (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ composite_tristrip_info_t *info = closure;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ return compositor->composite_tristrip (dst, op, src,
+ src_x - dst_x, src_y - dst_y,
+ dst_x, dst_y,
+ extents,
+ info->antialias, &info->strip);
+}
+
+static cairo_bool_t
+is_recording_pattern (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return FALSE;
+
+ surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+ surface = _cairo_surface_get_source (surface, NULL);
+ return _cairo_surface_is_recording (surface);
+}
+
+static cairo_surface_t *
+recording_pattern_get_surface (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+ return _cairo_surface_get_source (surface, NULL);
+}
+
+static cairo_bool_t
+recording_pattern_contains_sample (const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *sample)
+{
+ cairo_recording_surface_t *surface;
+
+ if (! is_recording_pattern (pattern))
+ return FALSE;
+
+ if (pattern->extend == CAIRO_EXTEND_NONE)
+ return TRUE;
+
+ surface = (cairo_recording_surface_t *) recording_pattern_get_surface (pattern);
+ if (surface->unbounded)
+ return TRUE;
+
+ return _cairo_rectangle_contains_rectangle (&surface->extents, sample);
+}
+
+static cairo_bool_t
+op_reduces_to_source (cairo_composite_rectangles_t *extents)
+{
+ if (extents->op == CAIRO_OPERATOR_SOURCE)
+ return TRUE;
+
+ if (extents->surface->is_clear)
+ return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD;
+
+ return FALSE;
+}
+
+static cairo_status_t
+composite_aligned_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ cairo_operator_t op = extents->op;
+ cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (extents->clip);
+ cairo_bool_t op_is_source;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (need_clip_mask &&
+ (! extents->is_bounded || extents->op == CAIRO_OPERATOR_SOURCE))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ op_is_source = op_reduces_to_source (extents);
+
+ /* Are we just copying a recording surface? */
+ if (! need_clip_mask && op_is_source &&
+ recording_pattern_contains_sample (&extents->source_pattern.base,
+ &extents->source_sample_area))
+ {
+ cairo_clip_t *recording_clip;
+ cairo_pattern_t *source = &extents->source_pattern.base;
+
+ /* XXX could also do tiling repeat modes... */
+
+ /* first clear the area about to be overwritten */
+ if (! dst->is_clear) {
+ status = compositor->acquire (dst);
+ if (unlikely (status))
+ return status;
+
+ status = compositor->fill_boxes (dst,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ boxes);
+ compositor->release (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ recording_clip = _cairo_clip_from_boxes (boxes);
+ status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source),
+ &source->matrix,
+ dst, recording_clip);
+ _cairo_clip_destroy (recording_clip);
+
+ return status;
+ }
+
+ status = compositor->acquire (dst);
+ if (unlikely (status))
+ return status;
+
+ if (! need_clip_mask &&
+ (op == CAIRO_OPERATOR_CLEAR ||
+ extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID))
+ {
+ const cairo_color_t *color;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ color = CAIRO_COLOR_TRANSPARENT;
+ } else {
+ color = &((cairo_solid_pattern_t *) &extents->source_pattern)->color;
+ if (op_is_source)
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ status = compositor->fill_boxes (dst, op, color, boxes);
+ }
+ else
+ {
+ cairo_surface_t *src, *mask = NULL;
+ cairo_pattern_t *source = &extents->source_pattern.base;
+ int src_x, src_y;
+ int mask_x = 0, mask_y = 0;
+
+ if (need_clip_mask) {
+ mask = traps_get_clip_surface (compositor,
+ extents, &extents->bounded);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ mask_x = -extents->bounded.x;
+ mask_y = -extents->bounded.y;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ source = NULL;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+ } else if (op_is_source)
+ op = CAIRO_OPERATOR_SOURCE;
+
+ src = compositor->pattern_to_surface (dst, source, FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (likely (src->status == CAIRO_STATUS_SUCCESS)) {
+ status = compositor->composite_boxes (dst, op, src, mask,
+ src_x, src_y,
+ mask_x, mask_y,
+ 0, 0,
+ boxes, &extents->bounded);
+ cairo_surface_destroy (src);
+ } else
+ status = src->status;
+
+ cairo_surface_destroy (mask);
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
+ status = fixup_unbounded (compositor, extents, boxes);
+
+ compositor->release (dst);
+
+ return status;
+}
+
+static cairo_status_t
+upload_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_surface_t *dst = extents->surface;
+ const cairo_pattern_t *source = &extents->source_pattern.base;
+ cairo_surface_t *src;
+ cairo_rectangle_int_t limit;
+ cairo_int_status_t status;
+ int tx, ty;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ src = _cairo_pattern_get_source((cairo_surface_pattern_t *)source,
+ &limit);
+ if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Check that the data is entirely within the image */
+ if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width ||
+ extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ tx += limit.x;
+ ty += limit.y;
+
+ if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
+ status = compositor->draw_image_boxes (dst,
+ (cairo_image_surface_t *)src,
+ boxes, tx, ty);
+ else
+ status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
+ tx, ty);
+
+ return status;
+}
+
+static cairo_int_status_t
+trim_extents_to_traps (cairo_composite_rectangles_t *extents,
+ cairo_traps_t *traps)
+{
+ cairo_box_t box;
+
+ _cairo_traps_extents (traps, &box);
+ return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+trim_extents_to_tristrip (cairo_composite_rectangles_t *extents,
+ cairo_tristrip_t *strip)
+{
+ cairo_box_t box;
+
+ _cairo_tristrip_extents (strip, &box);
+ return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_box_t box;
+
+ _cairo_boxes_extents (boxes, &box);
+ return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+boxes_for_traps (cairo_boxes_t *boxes,
+ cairo_traps_t *traps,
+ cairo_antialias_t antialias)
+{
+ int i;
+
+ /* first check that the traps are rectilinear */
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ const cairo_trapezoid_t *t = &traps->traps[i];
+ if (_cairo_fixed_integer_round_down (t->left.p1.x) !=
+ _cairo_fixed_integer_round_down (t->left.p2.x) ||
+ _cairo_fixed_integer_round_down (t->right.p1.x) !=
+ _cairo_fixed_integer_round_down (t->right.p2.x))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+ } else {
+ for (i = 0; i < traps->num_traps; i++) {
+ const cairo_trapezoid_t *t = &traps->traps[i];
+ if (t->left.p1.x != t->left.p2.x || t->right.p1.x != t->right.p2.x)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ _cairo_boxes_init (boxes);
+
+ boxes->num_boxes = traps->num_traps;
+ boxes->chunks.base = (cairo_box_t *) traps->traps;
+ boxes->chunks.count = traps->num_traps;
+ boxes->chunks.size = traps->num_traps;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ boxes->chunks.base[i].p1.x = x1;
+ boxes->chunks.base[i].p1.y = y1;
+ boxes->chunks.base[i].p2.x = x2;
+ boxes->chunks.base[i].p2.y = y2;
+
+ if (boxes->is_pixel_aligned) {
+ boxes->is_pixel_aligned =
+ _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+ _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+ }
+ }
+ } else {
+ boxes->is_pixel_aligned = TRUE;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ /* round down here to match Pixman's behavior when using traps. */
+ boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+ boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+ boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+ boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+clip_and_composite_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes);
+
+static cairo_status_t
+clip_and_composite_polygon (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_polygon_t *polygon,
+ cairo_antialias_t antialias,
+ cairo_fill_rule_t fill_rule,
+ cairo_bool_t curvy)
+{
+ composite_traps_info_t traps;
+ cairo_surface_t *dst = extents->surface;
+ cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip);
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (polygon->num_edges == 0) {
+ status = CAIRO_INT_STATUS_SUCCESS;
+
+ if (! extents->is_bounded) {
+ cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+
+ if (clip_region &&
+ cairo_region_contains_rectangle (clip_region,
+ &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+ clip_region = NULL;
+
+ if (clip_region != NULL) {
+ status = compositor->set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (clip_surface)
+ status = fixup_unbounded_with_mask (compositor, extents);
+ else
+ status = fixup_unbounded (compositor, extents, NULL);
+
+ if (clip_region != NULL)
+ compositor->set_clip_region (dst, NULL);
+ }
+
+ return status;
+ }
+
+ if (extents->clip->path != NULL && extents->is_bounded) {
+ cairo_polygon_t clipper;
+ cairo_fill_rule_t clipper_fill_rule;
+ cairo_antialias_t clipper_antialias;
+
+ status = _cairo_clip_get_polygon (extents->clip,
+ &clipper,
+ &clipper_fill_rule,
+ &clipper_antialias);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ if (clipper_antialias == antialias) {
+ status = _cairo_polygon_intersect (polygon, fill_rule,
+ &clipper, clipper_fill_rule);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip);
+ _cairo_clip_destroy (extents->clip);
+ extents->clip = clip;
+
+ fill_rule = CAIRO_FILL_RULE_WINDING;
+ }
+ _cairo_polygon_fini (&clipper);
+ }
+ }
+ }
+
+ if (antialias == CAIRO_ANTIALIAS_NONE && curvy) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ status = _cairo_rasterise_polygon_to_boxes (polygon, fill_rule, &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ assert (boxes.is_pixel_aligned);
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ }
+ _cairo_boxes_fini (&boxes);
+ if ((status != CAIRO_INT_STATUS_UNSUPPORTED))
+ return status;
+ }
+
+ _cairo_traps_init (&traps.traps);
+
+ if (antialias == CAIRO_ANTIALIAS_NONE && curvy) {
+ status = _cairo_rasterise_polygon_to_traps (polygon, fill_rule, antialias, &traps.traps);
+ } else {
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
+ }
+ if (unlikely (status))
+ goto CLEANUP_TRAPS;
+
+ status = trim_extents_to_traps (extents, &traps.traps);
+ if (unlikely (status))
+ goto CLEANUP_TRAPS;
+
+ /* Use a fast path if the trapezoids consist of a set of boxes. */
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (1) {
+ cairo_boxes_t boxes;
+
+ status = boxes_for_traps (&boxes, &traps.traps, antialias);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ /* XXX need to reconstruct the traps! */
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ }
+ }
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ /* Otherwise render the trapezoids to a mask and composite in the usual
+ * fashion.
+ */
+ unsigned int flags = 0;
+
+ /* For unbounded operations, the X11 server will estimate the
+ * affected rectangle and apply the operation to that. However,
+ * there are cases where this is an overestimate (e.g. the
+ * clip-fill-{eo,nz}-unbounded test).
+ *
+ * The clip will trim that overestimate to our expectations.
+ */
+ if (! extents->is_bounded)
+ flags |= FORCE_CLIP_REGION;
+
+ traps.antialias = antialias;
+ status = clip_and_composite (compositor, extents,
+ composite_traps, NULL, &traps,
+ need_unbounded_clip (extents) | flags);
+ }
+
+CLEANUP_TRAPS:
+ _cairo_traps_fini (&traps.traps);
+
+ return status;
+}
+
+struct composite_opacity_info {
+ const cairo_traps_compositor_t *compositor;
+ uint8_t op;
+ cairo_surface_t *dst;
+ cairo_surface_t *src;
+ int src_x, src_y;
+ double opacity;
+};
+
+static void composite_opacity(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct composite_opacity_info *info = closure;
+ const cairo_traps_compositor_t *compositor = info->compositor;
+ cairo_surface_t *mask;
+ int mask_x, mask_y;
+ cairo_color_t color;
+ cairo_solid_pattern_t solid;
+
+ _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
+ _cairo_pattern_init_solid (&solid, &color);
+ mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
+ &_cairo_unbounded_rectangle,
+ &_cairo_unbounded_rectangle,
+ &mask_x, &mask_y);
+ if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+ if (info->src) {
+ compositor->composite (info->dst, info->op, info->src, mask,
+ x + info->src_x, y + info->src_y,
+ mask_x, mask_y,
+ x, y,
+ w, h);
+ } else {
+ compositor->composite (info->dst, info->op, mask, NULL,
+ mask_x, mask_y,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+ }
+
+ cairo_surface_destroy (mask);
+}
+
+
+static cairo_int_status_t
+composite_opacity_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ const cairo_solid_pattern_t *mask = closure;
+ struct composite_opacity_info info;
+ int i;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ info.compositor = compositor;
+ info.op = op;
+ info.dst = dst;
+
+ info.src = src;
+ info.src_x = src_x;
+ info.src_y = src_y;
+
+ info.opacity = mask->color.alpha / (double) 0xffff;
+
+ /* XXX for lots of boxes create a clip region for the fully opaque areas */
+ for (i = 0; i < clip->num_boxes; i++)
+ do_unaligned_box(composite_opacity, &info,
+ &clip->boxes[i], dst_x, dst_y);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_traps_t traps;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = _cairo_traps_init_boxes (&traps, closure);
+ if (unlikely (status))
+ return status;
+
+ status = compositor->composite_traps (dst, op, src,
+ src_x - dst_x, src_y - dst_y,
+ dst_x, dst_y,
+ extents,
+ CAIRO_ANTIALIAS_DEFAULT, &traps);
+ _cairo_traps_fini (&traps);
+
+ return status;
+}
+
+static cairo_status_t
+clip_and_composite_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (boxes->num_boxes == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = trim_extents_to_boxes (extents, boxes);
+ if (unlikely (status))
+ return status;
+
+ if (boxes->is_pixel_aligned && extents->clip->path == NULL &&
+ extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (op_reduces_to_source (extents) ||
+ (extents->op == CAIRO_OPERATOR_OVER &&
+ (extents->source_pattern.surface.surface->content & CAIRO_CONTENT_ALPHA) == 0)))
+ {
+ status = upload_boxes (compositor, extents, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+ if (extents->clip->path != NULL && extents->is_bounded) {
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+ cairo_clip_t *clip;
+
+ clip = _cairo_clip_copy (extents->clip);
+ clip = _cairo_clip_intersect_boxes (clip, boxes);
+ if (_cairo_clip_is_all_clipped (clip))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ status = _cairo_clip_get_polygon (clip, &polygon,
+ &fill_rule, &antialias);
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *saved_clip = extents->clip;
+ extents->clip = clip;
+
+ status = clip_and_composite_polygon (compositor, extents, &polygon,
+ antialias, fill_rule, FALSE);
+
+ clip = extents->clip;
+ extents->clip = saved_clip;
+
+ _cairo_polygon_fini (&polygon);
+ }
+ _cairo_clip_destroy (clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ /* Use a fast path if the boxes are pixel aligned (or nearly aligned!) */
+ if (boxes->is_pixel_aligned) {
+ status = composite_aligned_boxes (compositor, extents, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ return clip_and_composite (compositor, extents,
+ composite_boxes, NULL, boxes,
+ need_unbounded_clip (extents));
+}
+
+static cairo_int_status_t
+composite_traps_as_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ composite_traps_info_t *info)
+{
+ cairo_boxes_t boxes;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (! _cairo_traps_to_boxes (&info->traps, info->antialias, &boxes))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return clip_and_composite_boxes (compositor, extents, &boxes);
+}
+
+static cairo_int_status_t
+clip_and_composite_traps (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ composite_traps_info_t *info,
+ unsigned flags)
+{
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = trim_extents_to_traps (extents, &info->traps);
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+ return status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if ((flags & FORCE_CLIP_REGION) == 0)
+ status = composite_traps_as_boxes (compositor, extents, info);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ /* For unbounded operations, the X11 server will estimate the
+ * affected rectangle and apply the operation to that. However,
+ * there are cases where this is an overestimate (e.g. the
+ * clip-fill-{eo,nz}-unbounded test).
+ *
+ * The clip will trim that overestimate to our expectations.
+ */
+ if (! extents->is_bounded)
+ flags |= FORCE_CLIP_REGION;
+
+ status = clip_and_composite (compositor, extents,
+ composite_traps, NULL, info,
+ need_unbounded_clip (extents) | flags);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+clip_and_composite_tristrip (const cairo_traps_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ composite_tristrip_info_t *info)
+{
+ cairo_int_status_t status;
+ unsigned int flags = 0;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = trim_extents_to_tristrip (extents, &info->strip);
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+ return status;
+
+ if (! extents->is_bounded)
+ flags |= FORCE_CLIP_REGION;
+
+ status = clip_and_composite (compositor, extents,
+ composite_tristrip, NULL, info,
+ need_unbounded_clip (extents) | flags);
+
+ return status;
+}
+
+struct composite_mask {
+ cairo_surface_t *mask;
+ int mask_x, mask_y;
+};
+
+static cairo_int_status_t
+composite_mask (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ struct composite_mask *data = closure;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (src != NULL) {
+ compositor->composite (dst, op, src, data->mask,
+ extents->x + src_x, extents->y + src_y,
+ extents->x + data->mask_x, extents->y + data->mask_y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ } else {
+ compositor->composite (dst, op, data->mask, NULL,
+ extents->x + data->mask_x, extents->y + data->mask_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct composite_box_info {
+ const cairo_traps_compositor_t *compositor;
+ cairo_surface_t *dst;
+ cairo_surface_t *src;
+ int src_x, src_y;
+ uint8_t op;
+};
+
+static void composite_box(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct composite_box_info *info = closure;
+ const cairo_traps_compositor_t *compositor = info->compositor;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
+ cairo_surface_t *mask;
+ cairo_color_t color;
+ cairo_solid_pattern_t solid;
+ int mask_x, mask_y;
+
+ _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
+ _cairo_pattern_init_solid (&solid, &color);
+
+ mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
+ &_cairo_unbounded_rectangle,
+ &_cairo_unbounded_rectangle,
+ &mask_x, &mask_y);
+
+ if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+ compositor->composite (info->dst, info->op, info->src, mask,
+ x + info->src_x, y + info->src_y,
+ mask_x, mask_y,
+ x, y,
+ w, h);
+ }
+
+ cairo_surface_destroy (mask);
+ } else {
+ compositor->composite (info->dst, info->op, info->src, NULL,
+ x + info->src_x, y + info->src_y,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+}
+
+static cairo_int_status_t
+composite_mask_clip_boxes (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ struct composite_mask *data = closure;
+ struct composite_box_info info;
+ int i;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ info.compositor = compositor;
+ info.op = CAIRO_OPERATOR_SOURCE;
+ info.dst = dst;
+ info.src = data->mask;
+ info.src_x = data->mask_x;
+ info.src_y = data->mask_y;
+
+ info.src_x += dst_x;
+ info.src_y += dst_y;
+
+ for (i = 0; i < clip->num_boxes; i++)
+ do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_mask_clip (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ struct composite_mask *data = closure;
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ composite_traps_info_t info;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = _cairo_clip_get_polygon (clip, &polygon,
+ &fill_rule, &info.antialias);
+ if (unlikely (status))
+ return status;
+
+ _cairo_traps_init (&info.traps);
+ status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps,
+ &polygon,
+ fill_rule);
+ _cairo_polygon_fini (&polygon);
+ if (unlikely (status))
+ return status;
+
+ status = composite_traps (compositor, dst, &info,
+ CAIRO_OPERATOR_SOURCE,
+ data->mask,
+ data->mask_x + dst_x, data->mask_y + dst_y,
+ dst_x, dst_y,
+ extents, NULL);
+ _cairo_traps_fini (&info.traps);
+
+ return status;
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_traps_compositor_paint (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor;
+ cairo_boxes_t boxes;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ _cairo_clip_steal_boxes (extents->clip, &boxes);
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_mask (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+ extents->clip->path == NULL) {
+ status = clip_and_composite (compositor, extents,
+ composite_opacity_boxes,
+ composite_opacity_boxes,
+ &extents->mask_pattern,
+ need_unbounded_clip (extents));
+ } else {
+ struct composite_mask data;
+
+ data.mask = compositor->pattern_to_surface (extents->surface,
+ &extents->mask_pattern.base,
+ TRUE,
+ &extents->bounded,
+ &extents->mask_sample_area,
+ &data.mask_x,
+ &data.mask_y);
+ if (unlikely (data.mask->status))
+ return data.mask->status;
+
+ status = clip_and_composite (compositor, extents,
+ composite_mask,
+ extents->clip->path ? composite_mask_clip : composite_mask_clip_boxes,
+ &data, need_bounded_clip (extents));
+
+ cairo_surface_destroy (data.mask);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, extents->clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED && 0 &&
+ _cairo_clip_is_region (extents->clip)) /* XXX */
+ {
+ composite_tristrip_info_t info;
+
+ info.antialias = antialias;
+ _cairo_tristrip_init_with_clip (&info.strip, extents->clip);
+ status = _cairo_path_fixed_stroke_to_tristrip (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &info.strip);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_tristrip (compositor, extents, &info);
+ _cairo_tristrip_fini (&info.strip);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
+ path->has_curve_to && antialias == CAIRO_ANTIALIAS_NONE) {
+ cairo_polygon_t polygon;
+
+ _cairo_polygon_init_with_clip (&polygon, extents->clip);
+ status = _cairo_path_fixed_stroke_to_polygon (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_polygon (compositor,
+ extents, &polygon,
+ CAIRO_ANTIALIAS_NONE,
+ CAIRO_FILL_RULE_WINDING,
+ TRUE);
+ _cairo_polygon_fini (&polygon);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_int_status_t (*func) (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps);
+ composite_traps_info_t info;
+ unsigned flags = 0;
+
+ if (antialias == CAIRO_ANTIALIAS_BEST || antialias == CAIRO_ANTIALIAS_GOOD) {
+ func = _cairo_path_fixed_stroke_polygon_to_traps;
+ } else {
+ func = _cairo_path_fixed_stroke_to_traps;
+ if (extents->clip->num_boxes > 1 ||
+ extents->mask.width > extents->unbounded.width ||
+ extents->mask.height > extents->unbounded.height)
+ {
+ flags = NEED_CLIP_REGION | FORCE_CLIP_REGION;
+ }
+ }
+
+ info.antialias = antialias;
+ _cairo_traps_init_with_clip (&info.traps, extents->clip);
+ status = func (path, style, ctm, ctm_inverse, tolerance, &info.traps);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_traps (compositor, extents, &info, flags);
+ _cairo_traps_fini (&info.traps);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, extents->clip);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite_boxes (compositor, extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_polygon_t polygon;
+
+#if 0
+ if (extents->mask.width > extents->unbounded.width ||
+ extents->mask.height > extents->unbounded.height)
+ {
+ cairo_box_t limits;
+ _cairo_box_from_rectangle (&limits, &extents->unbounded);
+ _cairo_polygon_init (&polygon, &limits, 1);
+ }
+ else
+ {
+ _cairo_polygon_init (&polygon, NULL, 0);
+ }
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
+ extents->clip->boxes,
+ extents->clip->num_boxes);
+ }
+#else
+ _cairo_polygon_init_with_clip (&polygon, extents->clip);
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+#endif
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = clip_and_composite_polygon (compositor, extents, &polygon,
+ antialias, fill_rule, path->has_curve_to);
+ }
+ _cairo_polygon_fini (&polygon);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+composite_glyphs (const cairo_traps_compositor_t *compositor,
+ cairo_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ cairo_surface_t *src,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_composite_glyphs_info_t *info = closure;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (op == CAIRO_OPERATOR_ADD && (dst->content & CAIRO_CONTENT_COLOR) == 0)
+ info->use_mask = 0;
+
+ return compositor->composite_glyphs (dst, op, src,
+ src_x, src_y,
+ dst_x, dst_y,
+ info);
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_glyphs (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ status = compositor->check_composite (extents);
+ if (unlikely (status))
+ return status;
+
+#if ! CAIRO_HAS_TG_SURFACE
+ _cairo_scaled_font_freeze_cache (scaled_font);
+#endif
+
+ status = compositor->check_composite_glyphs (extents,
+ scaled_font, glyphs,
+ &num_glyphs);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_composite_glyphs_info_t info;
+ unsigned flags = 0;
+
+ info.font = scaled_font;
+ info.glyphs = glyphs;
+ info.num_glyphs = num_glyphs;
+ info.use_mask = overlap || ! extents->is_bounded;
+ info.extents = extents->bounded;
+
+ if (extents->mask.width > extents->bounded.width ||
+ extents->mask.height > extents->bounded.height)
+ {
+ flags |= FORCE_CLIP_REGION;
+ }
+
+ status = clip_and_composite (compositor, extents,
+ composite_glyphs, NULL, &info,
+ need_bounded_clip (extents) |
+ flags);
+ }
+
+#if ! CAIRO_HAS_TG_SURFACE
+ _cairo_scaled_font_thaw_cache (scaled_font);
+#endif
+
+ return status;
+}
+
+void
+_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor,
+ const cairo_compositor_t *delegate)
+{
+ compositor->base.delegate = delegate;
+
+ compositor->base.paint = _cairo_traps_compositor_paint;
+ compositor->base.mask = _cairo_traps_compositor_mask;
+ compositor->base.fill = _cairo_traps_compositor_fill;
+ compositor->base.stroke = _cairo_traps_compositor_stroke;
+ compositor->base.glyphs = _cairo_traps_compositor_glyphs;
+}
diff --git a/src/cairo-traps-private.h b/src/cairo-traps-private.h
new file mode 100755
index 000000000..7fef062a4
--- /dev/null
+++ b/src/cairo-traps-private.h
@@ -0,0 +1,141 @@
+/* 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_TRAPS_PRIVATE_H
+#define CAIRO_TRAPS_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_traps {
+ cairo_status_t status;
+
+ cairo_box_t bounds;
+ const cairo_box_t *limits;
+ int num_limits;
+
+ unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
+ unsigned int has_intersections : 1;
+ unsigned int is_rectilinear : 1;
+ unsigned int is_rectangular : 1;
+
+ int num_traps;
+ int traps_size;
+ cairo_trapezoid_t *traps;
+ cairo_trapezoid_t traps_embedded[16];
+};
+
+/* cairo-traps.c */
+cairo_private void
+_cairo_traps_init (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_traps_limit (cairo_traps_t *traps,
+ const cairo_box_t *boxes,
+ int num_boxes);
+
+cairo_private cairo_status_t
+_cairo_traps_init_boxes (cairo_traps_t *traps,
+ const cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_traps_clear (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_fini (cairo_traps_t *traps);
+
+#define _cairo_traps_status(T) (T)->status
+
+cairo_private void
+_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
+
+cairo_private void
+_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
+ const cairo_point_t t[3]);
+
+cairo_private void
+_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
+ const cairo_point_t q[4]);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
+ const cairo_point_t *top_left,
+ const cairo_point_t *bottom_right);
+
+cairo_private void
+_cairo_traps_add_trap (cairo_traps_t *traps,
+ cairo_fixed_t top, cairo_fixed_t bottom,
+ cairo_line_t *left, cairo_line_t *right);
+
+cairo_private int
+_cairo_traps_contain (const cairo_traps_t *traps,
+ double x, double y);
+
+cairo_private void
+_cairo_traps_extents (const cairo_traps_t *traps,
+ cairo_box_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_traps_extract_region (cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_region_t **region);
+
+cairo_private cairo_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes);
+
+cairo_private cairo_status_t
+_cairo_traps_path (const cairo_traps_t *traps,
+ cairo_path_fixed_t *path);
+
+cairo_private cairo_int_status_t
+_cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_TRAPS_PRIVATE_H */
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
new file mode 100755
index 000000000..8bdac45ab
--- /dev/null
+++ b/src/cairo-traps.c
@@ -0,0 +1,1067 @@
+/* -*- 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-box-inline.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-spans-private.h"
+
+/* private functions */
+
+void
+_cairo_traps_init (cairo_traps_t *traps)
+{
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t)));
+
+ traps->status = CAIRO_STATUS_SUCCESS;
+
+ traps->maybe_region = 1;
+ traps->is_rectilinear = 0;
+ traps->is_rectangular = 0;
+
+ traps->num_traps = 0;
+
+ traps->traps_size = ARRAY_LENGTH (traps->traps_embedded);
+ traps->traps = traps->traps_embedded;
+
+ traps->limits = NULL;
+ traps->num_limits = 0;
+ traps->has_intersections = FALSE;
+}
+
+void
+_cairo_traps_limit (cairo_traps_t *traps,
+ const cairo_box_t *limits,
+ int num_limits)
+{
+ int i;
+
+ traps->limits = limits;
+ traps->num_limits = num_limits;
+
+ traps->bounds = limits[0];
+ for (i = 1; i < num_limits; i++)
+ _cairo_box_add_box (&traps->bounds, &limits[i]);
+}
+
+void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+ const cairo_clip_t *clip)
+{
+ _cairo_traps_init (traps);
+ if (clip)
+ _cairo_traps_limit (traps, clip->boxes, clip->num_boxes);
+}
+
+void
+_cairo_traps_clear (cairo_traps_t *traps)
+{
+ traps->status = CAIRO_STATUS_SUCCESS;
+
+ traps->maybe_region = 1;
+ traps->is_rectilinear = 0;
+ traps->is_rectangular = 0;
+
+ traps->num_traps = 0;
+ traps->has_intersections = FALSE;
+}
+
+void
+_cairo_traps_fini (cairo_traps_t *traps)
+{
+ if (traps->traps != traps->traps_embedded)
+ free (traps->traps);
+
+ VG (VALGRIND_MAKE_MEM_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;
+}
+
+static void
+_cairo_traps_add_clipped_trap (cairo_traps_t *traps,
+ cairo_fixed_t _top, cairo_fixed_t _bottom,
+ cairo_line_t *_left, cairo_line_t *_right)
+{
+ /* Note: With the goofy trapezoid specification, (where an
+ * arbitrary two points on the lines can specified for the left
+ * and right edges), these limit checks would not work in
+ * general. For example, one can imagine a trapezoid entirely
+ * within the limits, but with two points used to specify the left
+ * edge entirely to the right of the limits. Fortunately, for our
+ * purposes, cairo will never generate such a crazy
+ * trapezoid. Instead, cairo always uses for its points the
+ * extreme positions of the edge that are visible on at least some
+ * trapezoid. With this constraint, it's impossible for both
+ * points to be outside the limits while the relevant edge is
+ * entirely inside the limits.
+ */
+ if (traps->num_limits) {
+ const cairo_box_t *b = &traps->bounds;
+ cairo_fixed_t top = _top, bottom = _bottom;
+ cairo_line_t left = *_left, right = *_right;
+
+ /* Trivially reject if trapezoid is entirely to the right or
+ * to the left of the limits. */
+ if (left.p1.x >= b->p2.x && left.p2.x >= b->p2.x)
+ return;
+
+ if (right.p1.x <= b->p1.x && right.p2.x <= b->p1.x)
+ return;
+
+ /* And reject if the trapezoid is entirely above or below */
+ if (top >= b->p2.y || bottom <= b->p1.y)
+ return;
+
+ /* Otherwise, clip the trapezoid to the limits. We only clip
+ * where an edge is entirely outside the limits. If we wanted
+ * to be more clever, we could handle cases where a trapezoid
+ * edge intersects the edge of the limits, but that would
+ * require slicing this trapezoid into multiple trapezoids,
+ * and I'm not sure the effort would be worth it. */
+ if (top < b->p1.y)
+ top = b->p1.y;
+
+ if (bottom > b->p2.y)
+ bottom = b->p2.y;
+
+ if (left.p1.x <= b->p1.x && left.p2.x <= b->p1.x)
+ left.p1.x = left.p2.x = b->p1.x;
+
+ if (right.p1.x >= b->p2.x && right.p2.x >= b->p2.x)
+ right.p1.x = right.p2.x = b->p2.x;
+
+ /* Trivial discards for empty trapezoids that are likely to
+ * be produced by our tessellators (most notably convex_quad
+ * when given a simple rectangle).
+ */
+ if (top >= bottom)
+ return;
+
+ /* cheap colinearity check */
+ if (right.p1.x <= left.p1.x && right.p1.y == left.p1.y &&
+ right.p2.x <= left.p2.x && right.p2.y == left.p2.y)
+ return;
+
+ _cairo_traps_add_trap (traps, top, bottom, &left, &right);
+ } else
+ _cairo_traps_add_trap (traps, _top, _bottom, _left, _right);
+}
+
+static int
+_compare_point_fixed_by_y (const void *av, const void *bv)
+{
+ const cairo_point_t *a = av, *b = bv;
+ int ret = a->y - b->y;
+ if (ret == 0)
+ ret = a->x - b->x;
+ return ret;
+}
+
+void
+_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
+ const cairo_point_t q[4])
+{
+ int a, b, c, d;
+ int i;
+ cairo_slope_t ab, ad;
+ cairo_bool_t b_left_of_d;
+ cairo_line_t left;
+ cairo_line_t right;
+
+ /* Choose a as a point with minimal y */
+ a = 0;
+ for (i = 1; i < 4; i++)
+ if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0)
+ a = i;
+
+ /* b and d are adjacent to a, while c is opposite */
+ b = (a + 1) % 4;
+ c = (a + 2) % 4;
+ d = (a + 3) % 4;
+
+ /* Choose between b and d so that b.y is less than d.y */
+ if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) {
+ b = (a + 3) % 4;
+ d = (a + 1) % 4;
+ }
+
+ /* Without freedom left to choose anything else, we have four
+ * cases to tessellate.
+ *
+ * First, we have to determine the Y-axis sort of the four
+ * vertices, (either abcd or abdc). After that we need to detemine
+ * which edges will be "left" and which will be "right" in the
+ * resulting trapezoids. This can be determined by computing a
+ * slope comparison of ab and ad to determine if b is left of d or
+ * not.
+ *
+ * Note that "left of" here is in the sense of which edges should
+ * be the left vs. right edges of the trapezoid. In particular, b
+ * left of d does *not* mean that b.x is less than d.x.
+ *
+ * This should hopefully be made clear in the lame ASCII art
+ * below. Since the same slope comparison is used in all cases, we
+ * compute it before testing for the Y-value sort. */
+
+ /* Note: If a == b then the ab slope doesn't give us any
+ * information. In that case, we can replace it with the ac (or
+ * equivalenly the bc) slope which gives us exactly the same
+ * information we need. At worst the names of the identifiers ab
+ * and b_left_of_d are inaccurate in this case, (would be ac, and
+ * c_left_of_d). */
+ if (q[a].x == q[b].x && q[a].y == q[b].y)
+ _cairo_slope_init (&ab, &q[a], &q[c]);
+ else
+ _cairo_slope_init (&ab, &q[a], &q[b]);
+
+ _cairo_slope_init (&ad, &q[a], &q[d]);
+
+ b_left_of_d = _cairo_slope_compare (&ab, &ad) > 0;
+
+ if (q[c].y <= q[d].y) {
+ if (b_left_of_d) {
+ /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad))
+ *
+ * top bot left right
+ * _a a a
+ * / / /| |\ a.y b.y ab ad
+ * b / b | b \
+ * / / | | \ \ b.y c.y bc ad
+ * c / c | c \
+ * | / \| \ \ c.y d.y cd ad
+ * d d d
+ */
+ left.p1 = q[a]; left.p2 = q[b];
+ right.p1 = q[a]; right.p2 = q[d];
+ _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+ left.p1 = q[b]; left.p2 = q[c];
+ _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right);
+ left.p1 = q[c]; left.p2 = q[d];
+ _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right);
+ } else {
+ /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad))
+ *
+ * a a a_
+ * /| |\ \ \ a.y b.y ad ab
+ * / b | b \ b
+ * / / | | \ \ b.y c.y ad bc
+ * / c | c \ c
+ * / / |/ \ | c.y d.y ad cd
+ * d d d
+ */
+ left.p1 = q[a]; left.p2 = q[d];
+ right.p1 = q[a]; right.p2 = q[b];
+ _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+ right.p1 = q[b]; right.p2 = q[c];
+ _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right);
+ right.p1 = q[c]; right.p2 = q[d];
+ _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right);
+ }
+ } else {
+ if (b_left_of_d) {
+ /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad))
+ *
+ * a a a
+ * // / \ |\ a.y b.y ab ad
+ * /b/ b \ b \
+ * / / \ \ \ \ b.y d.y bc ad
+ * /d/ \ d \ d
+ * // \ / \| d.y c.y bc dc
+ * c c c
+ */
+ left.p1 = q[a]; left.p2 = q[b];
+ right.p1 = q[a]; right.p2 = q[d];
+ _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+ left.p1 = q[b]; left.p2 = q[c];
+ _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right);
+ right.p1 = q[d]; right.p2 = q[c];
+ _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right);
+ } else {
+ /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad))
+ *
+ * a a a
+ * /| / \ \\ a.y b.y ad ab
+ * / b / b \b\
+ * / / / / \ \ b.y d.y ad bc
+ * d / d / \d\
+ * |/ \ / \\ d.y c.y dc bc
+ * c c c
+ */
+ left.p1 = q[a]; left.p2 = q[d];
+ right.p1 = q[a]; right.p2 = q[b];
+ _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
+ right.p1 = q[b]; right.p2 = q[c];
+ _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right);
+ left.p1 = q[d]; left.p2 = q[c];
+ _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right);
+ }
+ }
+}
+
+/* A triangle is simply a degenerate case of a convex
+ * quadrilateral. We would not benefit from having any distinct
+ * implementation of triangle vs. quadrilateral tessellation here. */
+void
+_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
+ const cairo_point_t t[3])
+{
+ cairo_point_t quad[4];
+
+ quad[0] = t[0];
+ quad[1] = t[0];
+ quad[2] = t[1];
+ quad[3] = t[2];
+
+ _cairo_traps_tessellate_convex_quad (traps, quad);
+}
+
+
+/**
+ * _cairo_traps_init_boxes:
+ * @traps: a #cairo_traps_t
+ * @box: an array box that will each be converted to a single trapezoid
+ * to store in @traps.
+ *
+ * Initializes a #cairo_traps_t to contain an array of rectangular
+ * trapezoids.
+ **/
+cairo_status_t
+_cairo_traps_init_boxes (cairo_traps_t *traps,
+ const cairo_boxes_t *boxes)
+{
+ cairo_trapezoid_t *trap;
+ const struct _cairo_boxes_chunk *chunk;
+
+ _cairo_traps_init (traps);
+
+ while (traps->traps_size < boxes->num_boxes) {
+ if (unlikely (! _cairo_traps_grow (traps))) {
+ _cairo_traps_fini (traps);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
+ traps->num_traps = boxes->num_boxes;
+ traps->is_rectilinear = TRUE;
+ traps->is_rectangular = TRUE;
+ traps->maybe_region = boxes->is_pixel_aligned;
+
+ trap = &traps->traps[0];
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box;
+ int i;
+
+ box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ trap->top = box->p1.y;
+ trap->bottom = box->p2.y;
+
+ trap->left.p1 = box->p1;
+ trap->left.p2.x = box->p1.x;
+ trap->left.p2.y = box->p2.y;
+
+ trap->right.p1.x = box->p2.x;
+ trap->right.p1.y = box->p1.y;
+ trap->right.p2 = box->p2;
+
+ box++, trap++;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
+ const cairo_point_t *top_left,
+ const cairo_point_t *bottom_right)
+{
+ cairo_line_t left;
+ cairo_line_t right;
+ cairo_fixed_t top, bottom;
+
+ if (top_left->y == bottom_right->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (top_left->x == bottom_right->x)
+ return CAIRO_STATUS_SUCCESS;
+
+ left.p1.x = left.p2.x = top_left->x;
+ left.p1.y = right.p1.y = top_left->y;
+ right.p1.x = right.p2.x = bottom_right->x;
+ left.p2.y = right.p2.y = bottom_right->y;
+
+ top = top_left->y;
+ bottom = bottom_right->y;
+
+ if (traps->num_limits) {
+ cairo_bool_t reversed;
+ int n;
+
+ if (top >= traps->bounds.p2.y || bottom <= traps->bounds.p1.y)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* support counter-clockwise winding for rectangular tessellation */
+ reversed = top_left->x > bottom_right->x;
+ if (reversed) {
+ right.p1.x = right.p2.x = top_left->x;
+ left.p1.x = left.p2.x = bottom_right->x;
+ }
+
+ if (left.p1.x >= traps->bounds.p2.x || right.p1.x <= traps->bounds.p1.x)
+ return CAIRO_STATUS_SUCCESS;
+
+ for (n = 0; n < traps->num_limits; n++) {
+ const cairo_box_t *limits = &traps->limits[n];
+ cairo_line_t _left, _right;
+ cairo_fixed_t _top, _bottom;
+
+ if (top >= limits->p2.y)
+ continue;
+ if (bottom <= limits->p1.y)
+ continue;
+
+ /* Trivially reject if trapezoid is entirely to the right or
+ * to the left of the limits. */
+ if (left.p1.x >= limits->p2.x)
+ continue;
+ if (right.p1.x <= limits->p1.x)
+ continue;
+
+ /* Otherwise, clip the trapezoid to the limits. */
+ _top = top;
+ if (_top < limits->p1.y)
+ _top = limits->p1.y;
+
+ _bottom = bottom;
+ if (_bottom > limits->p2.y)
+ _bottom = limits->p2.y;
+
+ if (_bottom <= _top)
+ continue;
+
+ _left = left;
+ if (_left.p1.x < limits->p1.x) {
+ _left.p1.x = limits->p1.x;
+ _left.p1.y = limits->p1.y;
+ _left.p2.x = limits->p1.x;
+ _left.p2.y = limits->p2.y;
+ }
+
+ _right = right;
+ if (_right.p1.x > limits->p2.x) {
+ _right.p1.x = limits->p2.x;
+ _right.p1.y = limits->p1.y;
+ _right.p2.x = limits->p2.x;
+ _right.p2.y = limits->p2.y;
+ }
+
+ if (left.p1.x >= right.p1.x)
+ continue;
+
+ if (reversed)
+ _cairo_traps_add_trap (traps, _top, _bottom, &_right, &_left);
+ else
+ _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right);
+ }
+ } else {
+ _cairo_traps_add_trap (traps, top, bottom, &left, &right);
+ }
+
+ return traps->status;
+}
+
+void
+_cairo_traps_translate (cairo_traps_t *traps, int x, int y)
+{
+ cairo_fixed_t xoff, yoff;
+ cairo_trapezoid_t *t;
+ int i;
+
+ /* Ugh. The cairo_composite/(Render) interface doesn't allow
+ an offset for the trapezoids. Need to manually shift all
+ the coordinates to align with the offset origin of the
+ intermediate surface. */
+
+ xoff = _cairo_fixed_from_int (x);
+ yoff = _cairo_fixed_from_int (y);
+
+ for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) {
+ t->top += yoff;
+ t->bottom += yoff;
+ t->left.p1.x += xoff;
+ t->left.p1.y += yoff;
+ t->left.p2.x += xoff;
+ t->left.p2.y += yoff;
+ t->right.p1.x += xoff;
+ t->right.p1.y += yoff;
+ t->right.p2.x += xoff;
+ t->right.p2.y += yoff;
+ }
+}
+
+void
+_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
+ cairo_trapezoid_t *src_traps,
+ int num_traps,
+ double tx, double ty,
+ double sx, double sy)
+{
+ int i;
+ cairo_fixed_t xoff = _cairo_fixed_from_double (tx);
+ cairo_fixed_t yoff = _cairo_fixed_from_double (ty);
+
+ if (sx == 1.0 && sy == 1.0) {
+ for (i = 0; i < num_traps; i++) {
+ offset_traps[i].top = src_traps[i].top + yoff;
+ offset_traps[i].bottom = src_traps[i].bottom + yoff;
+ offset_traps[i].left.p1.x = src_traps[i].left.p1.x + xoff;
+ offset_traps[i].left.p1.y = src_traps[i].left.p1.y + yoff;
+ offset_traps[i].left.p2.x = src_traps[i].left.p2.x + xoff;
+ offset_traps[i].left.p2.y = src_traps[i].left.p2.y + yoff;
+ offset_traps[i].right.p1.x = src_traps[i].right.p1.x + xoff;
+ offset_traps[i].right.p1.y = src_traps[i].right.p1.y + yoff;
+ offset_traps[i].right.p2.x = src_traps[i].right.p2.x + xoff;
+ offset_traps[i].right.p2.y = src_traps[i].right.p2.y + yoff;
+ }
+ } else {
+ cairo_fixed_t xsc = _cairo_fixed_from_double (sx);
+ cairo_fixed_t ysc = _cairo_fixed_from_double (sy);
+
+ for (i = 0; i < num_traps; i++) {
+ offset_traps[i].top = _cairo_fixed_mul (src_traps[i].top + yoff, ysc);
+ offset_traps[i].bottom = _cairo_fixed_mul (src_traps[i].bottom + yoff, ysc);
+ offset_traps[i].left.p1.x = _cairo_fixed_mul (src_traps[i].left.p1.x + xoff, xsc);
+ offset_traps[i].left.p1.y = _cairo_fixed_mul (src_traps[i].left.p1.y + yoff, ysc);
+ offset_traps[i].left.p2.x = _cairo_fixed_mul (src_traps[i].left.p2.x + xoff, xsc);
+ offset_traps[i].left.p2.y = _cairo_fixed_mul (src_traps[i].left.p2.y + yoff, ysc);
+ offset_traps[i].right.p1.x = _cairo_fixed_mul (src_traps[i].right.p1.x + xoff, xsc);
+ offset_traps[i].right.p1.y = _cairo_fixed_mul (src_traps[i].right.p1.y + yoff, ysc);
+ offset_traps[i].right.p2.x = _cairo_fixed_mul (src_traps[i].right.p2.x + xoff, xsc);
+ offset_traps[i].right.p2.y = _cairo_fixed_mul (src_traps[i].right.p2.y + yoff, ysc);
+ }
+ }
+}
+
+static cairo_bool_t
+_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt)
+{
+ cairo_slope_t slope_left, slope_pt, slope_right;
+
+ if (t->top > pt->y)
+ return FALSE;
+ if (t->bottom < pt->y)
+ return FALSE;
+
+ _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2);
+ _cairo_slope_init (&slope_pt, &t->left.p1, pt);
+
+ if (_cairo_slope_compare (&slope_left, &slope_pt) < 0)
+ return FALSE;
+
+ _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2);
+ _cairo_slope_init (&slope_pt, &t->right.p1, pt);
+
+ if (_cairo_slope_compare (&slope_pt, &slope_right) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_traps_contain (const cairo_traps_t *traps,
+ double x, double y)
+{
+ int i;
+ cairo_point_t point;
+
+ point.x = _cairo_fixed_from_double (x);
+ point.y = _cairo_fixed_from_double (y);
+
+ for (i = 0; i < traps->num_traps; i++) {
+ if (_cairo_trap_contains (&traps->traps[i], &point))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+ cairo_fixed_t y)
+{
+ return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y);
+}
+
+void
+_cairo_traps_extents (const cairo_traps_t *traps,
+ cairo_box_t *extents)
+{
+ int i;
+
+ if (traps->num_traps == 0) {
+ extents->p1.x = extents->p1.y = 0;
+ extents->p2.x = extents->p2.y = 0;
+ return;
+ }
+
+ extents->p1.x = extents->p1.y = INT32_MAX;
+ extents->p2.x = extents->p2.y = INT32_MIN;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ const cairo_trapezoid_t *trap = &traps->traps[i];
+
+ if (trap->top < extents->p1.y)
+ extents->p1.y = trap->top;
+ if (trap->bottom > extents->p2.y)
+ extents->p2.y = trap->bottom;
+
+ if (trap->left.p1.x < extents->p1.x) {
+ cairo_fixed_t x = trap->left.p1.x;
+ if (trap->top != trap->left.p1.y) {
+ x = _line_compute_intersection_x_for_y (&trap->left,
+ trap->top);
+ if (x < extents->p1.x)
+ extents->p1.x = x;
+ } else
+ extents->p1.x = x;
+ }
+ if (trap->left.p2.x < extents->p1.x) {
+ cairo_fixed_t x = trap->left.p2.x;
+ if (trap->bottom != trap->left.p2.y) {
+ x = _line_compute_intersection_x_for_y (&trap->left,
+ trap->bottom);
+ if (x < extents->p1.x)
+ extents->p1.x = x;
+ } else
+ extents->p1.x = x;
+ }
+
+ if (trap->right.p1.x > extents->p2.x) {
+ cairo_fixed_t x = trap->right.p1.x;
+ if (trap->top != trap->right.p1.y) {
+ x = _line_compute_intersection_x_for_y (&trap->right,
+ trap->top);
+ if (x > extents->p2.x)
+ extents->p2.x = x;
+ } else
+ extents->p2.x = x;
+ }
+ if (trap->right.p2.x > extents->p2.x) {
+ cairo_fixed_t x = trap->right.p2.x;
+ if (trap->bottom != trap->right.p2.y) {
+ x = _line_compute_intersection_x_for_y (&trap->right,
+ trap->bottom);
+ if (x > extents->p2.x)
+ extents->p2.x = x;
+ } else
+ extents->p2.x = x;
+ }
+ }
+}
+
+static cairo_bool_t
+_mono_edge_is_vertical (const cairo_line_t *line)
+{
+ return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
+}
+
+static cairo_bool_t
+_traps_are_pixel_aligned (cairo_traps_t *traps,
+ cairo_antialias_t antialias)
+{
+ int i;
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
+ ! _mono_edge_is_vertical (&traps->traps[i].right))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ } else {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
+ traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
+ ! _cairo_fixed_is_integer (traps->traps[i].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * _cairo_traps_extract_region:
+ * @traps: a #cairo_traps_t
+ * @region: a #cairo_region_t
+ *
+ * Determines if a set of trapezoids are exactly representable as a
+ * cairo region. If so, the passed-in region is initialized to
+ * the area representing the given traps. It should be finalized
+ * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED
+ * is returned.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED
+ * or %CAIRO_STATUS_NO_MEMORY
+ **/
+cairo_int_status_t
+_cairo_traps_extract_region (cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_region_t **region)
+{
+ cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+ cairo_rectangle_int_t *rects = stack_rects;
+ cairo_int_status_t status;
+ int i, rect_count;
+
+ /* we only treat this a hint... */
+ if (antialias != CAIRO_ANTIALIAS_NONE && ! traps->maybe_region)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _traps_are_pixel_aligned (traps, antialias)) {
+ traps->maybe_region = FALSE;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (traps->num_traps > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t));
+
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ rect_count = 0;
+ for (i = 0; i < traps->num_traps; i++) {
+ int x1, y1, x2, y2;
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ x1 = _cairo_fixed_integer_round_down (traps->traps[i].left.p1.x);
+ y1 = _cairo_fixed_integer_round_down (traps->traps[i].top);
+ x2 = _cairo_fixed_integer_round_down (traps->traps[i].right.p1.x);
+ y2 = _cairo_fixed_integer_round_down (traps->traps[i].bottom);
+ } else {
+ x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
+ y1 = _cairo_fixed_integer_part (traps->traps[i].top);
+ x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
+ y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
+ }
+
+ if (x2 > x1 && y2 > y1) {
+ rects[rect_count].x = x1;
+ rects[rect_count].y = y1;
+ rects[rect_count].width = x2 - x1;
+ rects[rect_count].height = y2 - y1;
+ rect_count++;
+ }
+ }
+
+
+ *region = cairo_region_create_rectangles (rects, rect_count);
+ status = (*region)->status;
+
+ if (rects != stack_rects)
+ free (rects);
+
+ return status;
+}
+
+cairo_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ int i;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
+ traps->traps[i].right.p1.x != traps->traps[i].right.p2.x)
+ return FALSE;
+ }
+
+ _cairo_boxes_init (boxes);
+
+ boxes->num_boxes = traps->num_traps;
+ boxes->chunks.base = (cairo_box_t *) traps->traps;
+ boxes->chunks.count = traps->num_traps;
+ boxes->chunks.size = traps->num_traps;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ boxes->chunks.base[i].p1.x = x1;
+ boxes->chunks.base[i].p1.y = y1;
+ boxes->chunks.base[i].p2.x = x2;
+ boxes->chunks.base[i].p2.y = y2;
+
+ if (boxes->is_pixel_aligned) {
+ boxes->is_pixel_aligned =
+ _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+ _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+ }
+ }
+ } else {
+ boxes->is_pixel_aligned = TRUE;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ /* round down here to match Pixman's behavior when using traps. */
+ boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+ boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+ boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+ boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+ }
+ }
+
+ return TRUE;
+}
+
+/* moves trap points such that they become the actual corners of the trapezoid */
+static void
+_sanitize_trap (cairo_trapezoid_t *t)
+{
+ cairo_trapezoid_t s = *t;
+
+#define FIX(lr, tb, p) \
+ if (t->lr.p.y != t->tb) { \
+ t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \
+ t->lr.p.y = s.tb; \
+ }
+ FIX (left, top, p1);
+ FIX (left, bottom, p2);
+ FIX (right, top, p1);
+ FIX (right, bottom, p2);
+}
+
+cairo_private cairo_status_t
+_cairo_traps_path (const cairo_traps_t *traps,
+ cairo_path_fixed_t *path)
+{
+ int i;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ cairo_status_t status;
+ cairo_trapezoid_t trap = traps->traps[i];
+
+ if (trap.top == trap.bottom)
+ continue;
+
+ _sanitize_trap (&trap);
+
+ status = _cairo_path_fixed_move_to (path, trap.left.p1.x, trap.top);
+ if (unlikely (status)) return status;
+ status = _cairo_path_fixed_line_to (path, trap.right.p1.x, trap.top);
+ if (unlikely (status)) return status;
+ status = _cairo_path_fixed_line_to (path, trap.right.p2.x, trap.bottom);
+ if (unlikely (status)) return status;
+ status = _cairo_path_fixed_line_to (path, trap.left.p2.x, trap.bottom);
+ if (unlikely (status)) return status;
+ status = _cairo_path_fixed_close_path (path);
+ if (unlikely (status)) return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps)
+{
+ cairo_box_t extents;
+ int n;
+
+#if 0
+ if (traps->has_limits) {
+ printf ("%s: limits=(%d, %d, %d, %d)\n",
+ filename,
+ traps->limits.p1.x, traps->limits.p1.y,
+ traps->limits.p2.x, traps->limits.p2.y);
+ }
+#endif
+
+ _cairo_traps_extents (traps, &extents);
+ fprintf (file, "extents=(%d, %d, %d, %d)\n",
+ extents.p1.x, extents.p1.y,
+ extents.p2.x, extents.p2.y);
+
+ for (n = 0; n < traps->num_traps; n++) {
+ fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+ traps->traps[n].top,
+ traps->traps[n].bottom,
+ traps->traps[n].left.p1.x,
+ traps->traps[n].left.p1.y,
+ traps->traps[n].left.p2.x,
+ traps->traps[n].left.p2.y,
+ traps->traps[n].right.p1.x,
+ traps->traps[n].right.p1.y,
+ traps->traps[n].right.p2.x,
+ traps->traps[n].right.p2.y);
+ }
+}
+
+struct cairo_trap_renderer {
+ cairo_span_renderer_t base;
+ cairo_traps_t *traps;
+};
+
+static cairo_status_t
+span_to_traps (void *abstract_renderer, int y, int h,
+ const cairo_half_open_span_t *spans, unsigned num_spans)
+{
+ struct cairo_trap_renderer *r = abstract_renderer;
+ cairo_fixed_t top, bot;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ top = _cairo_fixed_from_int (y);
+ bot = _cairo_fixed_from_int (y + h);
+ do {
+ if (spans[0].coverage) {
+ cairo_fixed_t x0 = _cairo_fixed_from_int(spans[0].x);
+ cairo_fixed_t x1 = _cairo_fixed_from_int(spans[1].x);
+ cairo_line_t left = { { x0, top }, { x0, bot } },
+ right = { { x1, top }, { x1, bot } };
+ _cairo_traps_add_trap (r->traps, top, bot, &left, &right);
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps)
+{
+ struct cairo_trap_renderer renderer;
+ cairo_scan_converter_t *converter;
+ cairo_int_status_t status;
+ cairo_rectangle_int_t r;
+
+ TRACE ((stderr, "%s: fill_rule=%d, antialias=%d\n",
+ __FUNCTION__, fill_rule, antialias));
+ assert(antialias == CAIRO_ANTIALIAS_NONE);
+
+ renderer.traps = traps;
+ renderer.base.render_rows = span_to_traps;
+
+ _cairo_box_round_to_rectangle (&polygon->extents, &r);
+ converter = _cairo_mono_scan_converter_create (r.x, r.y,
+ r.x + r.width,
+ r.y + r.height,
+ fill_rule);
+ status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = converter->generate (converter, &renderer.base);
+ converter->destroy (converter);
+ return status;
+}
diff --git a/src/cairo-tristrip-private.h b/src/cairo-tristrip-private.h
new file mode 100755
index 000000000..ccd28799e
--- /dev/null
+++ b/src/cairo-tristrip-private.h
@@ -0,0 +1,94 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_TRISTRIP_PRIVATE_H
+#define CAIRO_TRISTRIP_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_tristrip {
+ cairo_status_t status;
+
+ /* XXX clipping */
+
+ const cairo_box_t *limits;
+ int num_limits;
+
+ int num_points;
+ int size_points;
+ cairo_point_t *points;
+ cairo_point_t points_embedded[64];
+};
+
+cairo_private void
+_cairo_tristrip_init (cairo_tristrip_t *strip);
+
+cairo_private void
+_cairo_tristrip_limit (cairo_tristrip_t *strip,
+ const cairo_box_t *limits,
+ int num_limits);
+
+cairo_private void
+_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y);
+
+cairo_private void
+_cairo_tristrip_move_to (cairo_tristrip_t *strip,
+ const cairo_point_t *point);
+
+cairo_private void
+_cairo_tristrip_add_point (cairo_tristrip_t *strip,
+ const cairo_point_t *point);
+
+cairo_private void
+_cairo_tristrip_extents (const cairo_tristrip_t *strip,
+ cairo_box_t *extents);
+
+cairo_private void
+_cairo_tristrip_fini (cairo_tristrip_t *strip);
+
+#define _cairo_tristrip_status(T) ((T)->status)
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_TRISTRIP_PRIVATE_H */
diff --git a/src/cairo-tristrip.c b/src/cairo-tristrip.c
new file mode 100755
index 000000000..bb4972f50
--- /dev/null
+++ b/src/cairo-tristrip.c
@@ -0,0 +1,185 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilsonc.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-tristrip-private.h"
+
+void
+_cairo_tristrip_init (cairo_tristrip_t *strip)
+{
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (strip, sizeof (cairo_tristrip_t)));
+
+ strip->status = CAIRO_STATUS_SUCCESS;
+
+ strip->num_limits = 0;
+ strip->num_points = 0;
+
+ strip->size_points = ARRAY_LENGTH (strip->points_embedded);
+ strip->points = strip->points_embedded;
+}
+
+void
+_cairo_tristrip_fini (cairo_tristrip_t *strip)
+{
+ if (strip->points != strip->points_embedded)
+ free (strip->points);
+
+ VG (VALGRIND_MAKE_MEM_NOACCESS (strip, sizeof (cairo_tristrip_t)));
+}
+
+
+void
+_cairo_tristrip_limit (cairo_tristrip_t *strip,
+ const cairo_box_t *limits,
+ int num_limits)
+{
+ strip->limits = limits;
+ strip->num_limits = num_limits;
+}
+
+void
+_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip,
+ const cairo_clip_t *clip)
+{
+ _cairo_tristrip_init (strip);
+ if (clip)
+ _cairo_tristrip_limit (strip, clip->boxes, clip->num_boxes);
+}
+
+/* make room for at least one more trap */
+static cairo_bool_t
+_cairo_tristrip_grow (cairo_tristrip_t *strip)
+{
+ cairo_point_t *points;
+ int new_size = 4 * strip->size_points;
+
+ if (CAIRO_INJECT_FAULT ()) {
+ strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return FALSE;
+ }
+
+ if (strip->points == strip->points_embedded) {
+ points = _cairo_malloc_ab (new_size, sizeof (cairo_point_t));
+ if (points != NULL)
+ memcpy (points, strip->points, sizeof (strip->points_embedded));
+ } else {
+ points = _cairo_realloc_ab (strip->points,
+ new_size, sizeof (cairo_trapezoid_t));
+ }
+
+ if (unlikely (points == NULL)) {
+ strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return FALSE;
+ }
+
+ strip->points = points;
+ strip->size_points = new_size;
+ return TRUE;
+}
+
+void
+_cairo_tristrip_add_point (cairo_tristrip_t *strip,
+ const cairo_point_t *p)
+{
+ if (unlikely (strip->num_points == strip->size_points)) {
+ if (unlikely (! _cairo_tristrip_grow (strip)))
+ return;
+ }
+
+ strip->points[strip->num_points++] = *p;
+}
+
+/* Insert degenerate triangles to advance to the given point. The
+ * next point inserted must also be @p. */
+void
+_cairo_tristrip_move_to (cairo_tristrip_t *strip,
+ const cairo_point_t *p)
+{
+ if (strip->num_points == 0)
+ return;
+
+ _cairo_tristrip_add_point (strip, &strip->points[strip->num_points-1]);
+ _cairo_tristrip_add_point (strip, p);
+#if 0
+ /* and one more for luck! (to preserve cw/ccw ordering) */
+ _cairo_tristrip_add_point (strip, p);
+#endif
+}
+
+void
+_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y)
+{
+ cairo_fixed_t xoff, yoff;
+ cairo_point_t *p;
+ int i;
+
+ xoff = _cairo_fixed_from_int (x);
+ yoff = _cairo_fixed_from_int (y);
+
+ for (i = 0, p = strip->points; i < strip->num_points; i++, p++) {
+ p->x += xoff;
+ p->y += yoff;
+ }
+}
+
+void
+_cairo_tristrip_extents (const cairo_tristrip_t *strip,
+ cairo_box_t *extents)
+{
+ int i;
+
+ if (strip->num_points == 0) {
+ extents->p1.x = extents->p1.y = 0;
+ extents->p2.x = extents->p2.y = 0;
+ return;
+ }
+
+ extents->p2 = extents->p1 = strip->points[0];
+ for (i = 1; i < strip->num_points; i++) {
+ const cairo_point_t *p = &strip->points[i];
+
+ if (p->x < extents->p1.x)
+ extents->p1.x = p->x;
+ else if (p->x > extents->p2.x)
+ extents->p2.x = p->x;
+
+ if (p->y < extents->p1.y)
+ extents->p1.y = p->y;
+ else if (p->y > extents->p2.y)
+ extents->p2.y = p->y;
+ }
+}
diff --git a/src/cairo-truetype-subset-private.h b/src/cairo-truetype-subset-private.h
new file mode 100755
index 000000000..dc9573216
--- /dev/null
+++ b/src/cairo-truetype-subset-private.h
@@ -0,0 +1,212 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+#define CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+/* The structs defined here should strictly follow the TrueType
+ * specification and not be padded. We use only 16-bit integer
+ * in their definition to guarantee that. The fields of type
+ * "FIXED" in the TT spec are broken into two *_1 and *_2 16-bit
+ * parts, and 64-bit members are broken into four.
+ *
+ * The test truetype-tables in the test suite makes sure that
+ * these tables have the right size. Please update that test
+ * if you add new tables/structs that should be packed.
+ */
+
+#define MAKE_TT_TAG(a, b, c, d) (a<<24 | b<<16 | c<<8 | d)
+#define TT_TAG_CFF MAKE_TT_TAG('C','F','F',' ')
+#define TT_TAG_cmap MAKE_TT_TAG('c','m','a','p')
+#define TT_TAG_cvt MAKE_TT_TAG('c','v','t',' ')
+#define TT_TAG_fpgm MAKE_TT_TAG('f','p','g','m')
+#define TT_TAG_glyf MAKE_TT_TAG('g','l','y','f')
+#define TT_TAG_head MAKE_TT_TAG('h','e','a','d')
+#define TT_TAG_hhea MAKE_TT_TAG('h','h','e','a')
+#define TT_TAG_hmtx MAKE_TT_TAG('h','m','t','x')
+#define TT_TAG_loca MAKE_TT_TAG('l','o','c','a')
+#define TT_TAG_maxp MAKE_TT_TAG('m','a','x','p')
+#define TT_TAG_name MAKE_TT_TAG('n','a','m','e')
+#define TT_TAG_OS2 MAKE_TT_TAG('O','S','/','2')
+#define TT_TAG_post MAKE_TT_TAG('p','o','s','t')
+#define TT_TAG_prep MAKE_TT_TAG('p','r','e','p')
+
+/* All tt_* structs are big-endian */
+typedef struct _tt_cmap_index {
+ uint16_t platform;
+ uint16_t encoding;
+ uint32_t offset;
+} tt_cmap_index_t;
+
+typedef struct _tt_cmap {
+ uint16_t version;
+ uint16_t num_tables;
+ tt_cmap_index_t index[1];
+} tt_cmap_t;
+
+typedef struct _segment_map {
+ uint16_t format;
+ uint16_t length;
+ uint16_t version;
+ uint16_t segCountX2;
+ uint16_t searchRange;
+ uint16_t entrySelector;
+ uint16_t rangeShift;
+ uint16_t endCount[1];
+} tt_segment_map_t;
+
+typedef struct _tt_head {
+ int16_t version_1;
+ int16_t version_2;
+ int16_t revision_1;
+ int16_t revision_2;
+ uint16_t checksum_1;
+ uint16_t checksum_2;
+ uint16_t magic_1;
+ uint16_t magic_2;
+ uint16_t flags;
+ uint16_t units_per_em;
+ int16_t created_1;
+ int16_t created_2;
+ int16_t created_3;
+ int16_t created_4;
+ int16_t modified_1;
+ int16_t modified_2;
+ int16_t modified_3;
+ int16_t modified_4;
+ int16_t x_min; /* FWORD */
+ int16_t y_min; /* FWORD */
+ int16_t x_max; /* FWORD */
+ int16_t y_max; /* FWORD */
+ uint16_t mac_style;
+ uint16_t lowest_rec_pppem;
+ int16_t font_direction_hint;
+ int16_t index_to_loc_format;
+ int16_t glyph_data_format;
+} tt_head_t;
+
+typedef struct _tt_hhea {
+ int16_t version_1;
+ int16_t version_2;
+ int16_t ascender; /* FWORD */
+ int16_t descender; /* FWORD */
+ int16_t line_gap; /* FWORD */
+ uint16_t advance_max_width; /* UFWORD */
+ int16_t min_left_side_bearing; /* FWORD */
+ int16_t min_right_side_bearing; /* FWORD */
+ int16_t x_max_extent; /* FWORD */
+ int16_t caret_slope_rise;
+ int16_t caret_slope_run;
+ int16_t reserved[5];
+ int16_t metric_data_format;
+ uint16_t num_hmetrics;
+} tt_hhea_t;
+
+typedef struct _tt_maxp {
+ int16_t version_1;
+ int16_t version_2;
+ uint16_t num_glyphs;
+ uint16_t max_points;
+ uint16_t max_contours;
+ uint16_t max_composite_points;
+ uint16_t max_composite_contours;
+ uint16_t max_zones;
+ uint16_t max_twilight_points;
+ uint16_t max_storage;
+ uint16_t max_function_defs;
+ uint16_t max_instruction_defs;
+ uint16_t max_stack_elements;
+ uint16_t max_size_of_instructions;
+ uint16_t max_component_elements;
+ uint16_t max_component_depth;
+} tt_maxp_t;
+
+typedef struct _tt_name_record {
+ uint16_t platform;
+ uint16_t encoding;
+ uint16_t language;
+ uint16_t name;
+ uint16_t length;
+ uint16_t offset;
+} tt_name_record_t;
+
+typedef struct _tt_name {
+ uint16_t format;
+ uint16_t num_records;
+ uint16_t strings_offset;
+ tt_name_record_t records[1];
+} tt_name_t;
+
+
+/* bitmask for fsSelection field */
+#define TT_FS_SELECTION_ITALIC 1
+#define TT_FS_SELECTION_BOLD 32
+
+/* _unused fields are defined in TT spec but not used by cairo */
+typedef struct _tt_os2 {
+ uint16_t _unused1[2];
+ uint16_t usWeightClass;
+ uint16_t _unused2[28];
+ uint16_t fsSelection;
+ uint16_t _unused3[11];
+} tt_os2_t;
+
+/* composite_glyph_t flags */
+#define TT_ARG_1_AND_2_ARE_WORDS 0x0001
+#define TT_WE_HAVE_A_SCALE 0x0008
+#define TT_MORE_COMPONENTS 0x0020
+#define TT_WE_HAVE_AN_X_AND_Y_SCALE 0x0040
+#define TT_WE_HAVE_A_TWO_BY_TWO 0x0080
+
+typedef struct _tt_composite_glyph {
+ uint16_t flags;
+ uint16_t index;
+ uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */
+} tt_composite_glyph_t;
+
+typedef struct _tt_glyph_data {
+ int16_t num_contours;
+ int8_t data[8];
+ tt_composite_glyph_t glyph;
+} tt_glyph_data_t;
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
+
+#endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */
diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c
new file mode 100755
index 000000000..44d7f60f5
--- /dev/null
+++ b/src/cairo-truetype-subset.c
@@ -0,0 +1,1668 @@
+/* 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-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-truetype-subset-private.h"
+
+
+typedef struct subset_glyph subset_glyph_t;
+struct subset_glyph {
+ int parent_index;
+ unsigned long location;
+};
+
+typedef struct _cairo_truetype_font cairo_truetype_font_t;
+
+typedef struct table table_t;
+struct table {
+ unsigned long tag;
+ cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag);
+ int pos; /* position in the font directory */
+};
+
+struct _cairo_truetype_font {
+
+ cairo_scaled_font_subset_t *scaled_font_subset;
+
+ table_t truetype_tables[10];
+ int num_tables;
+
+ struct {
+ char *font_name;
+ char *ps_name;
+ 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;
+ cairo_bool_t is_pdf;
+};
+
+/*
+ * Test that the structs we define for TrueType tables have the
+ * correct size, ie. they are not padded.
+ */
+#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S))
+check (tt_head_t, 54);
+check (tt_hhea_t, 36);
+check (tt_maxp_t, 32);
+check (tt_name_record_t, 12);
+check (tt_name_t, 18);
+check (tt_name_t, 18);
+check (tt_composite_glyph_t, 16);
+check (tt_glyph_data_t, 26);
+#undef check
+
+static cairo_status_t
+cairo_truetype_font_use_glyph (cairo_truetype_font_t *font,
+ unsigned short glyph,
+ unsigned short *out);
+
+#define SFNT_VERSION 0x00010000
+#define SFNT_STRING_MAX_LENGTH 65535
+
+static cairo_status_t
+_cairo_truetype_font_set_error (cairo_truetype_font_t *font,
+ cairo_status_t status)
+{
+ if (status == CAIRO_STATUS_SUCCESS ||
+ status == (int)CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ _cairo_status_set_error (&font->status, status);
+
+ return _cairo_error (status);
+}
+
+static cairo_status_t
+_cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_bool_t is_pdf,
+ cairo_truetype_font_t **font_return)
+{
+ cairo_status_t status;
+ cairo_truetype_font_t *font;
+ const cairo_scaled_font_backend_t *backend;
+ tt_head_t head;
+ tt_hhea_t hhea;
+ tt_maxp_t maxp;
+ unsigned long size;
+
+ backend = scaled_font_subset->scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* FIXME: We should either support subsetting vertical fonts, or fail on
+ * vertical. Currently font_options_t doesn't have vertical flag, but
+ * it should be added in the future. For now, the freetype backend
+ * returns UNSUPPORTED in load_truetype_table if the font is vertical.
+ *
+ * if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font))
+ * return CAIRO_INT_STATUS_UNSUPPORTED;
+ */
+
+ /* We need to use a fallback font generated from the synthesized outlines. */
+ if (backend->is_synthetic && backend->is_synthetic (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->is_pdf = is_pdf;
+ 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);
+ 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);
+ free (font->base.font_name);
+ free (font->parent_to_subset);
+ free (font->glyphs);
+ _cairo_array_fini (&font->output);
+ free (font);
+}
+
+static cairo_status_t
+cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font,
+ size_t length,
+ unsigned char **buffer)
+{
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ status = _cairo_array_allocate (&font->output, length, (void **) buffer);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_truetype_font_write (cairo_truetype_font_t *font,
+ const void *data,
+ size_t length)
+{
+ cairo_status_t status;
+
+ if (font->status)
+ return;
+
+ status = _cairo_array_append_multiple (&font->output, data, length);
+ if (unlikely (status))
+ status = _cairo_truetype_font_set_error (font, status);
+}
+
+static void
+cairo_truetype_font_write_be16 (cairo_truetype_font_t *font,
+ uint16_t value)
+{
+ uint16_t be16_value;
+
+ if (font->status)
+ return;
+
+ be16_value = cpu_to_be16 (value);
+ cairo_truetype_font_write (font, &be16_value, sizeof be16_value);
+}
+
+static void
+cairo_truetype_font_write_be32 (cairo_truetype_font_t *font,
+ uint32_t value)
+{
+ uint32_t be32_value;
+
+ if (font->status)
+ return;
+
+ be32_value = cpu_to_be32 (value);
+ cairo_truetype_font_write (font, &be32_value, sizeof be32_value);
+}
+
+static cairo_status_t
+cairo_truetype_font_align_output (cairo_truetype_font_t *font,
+ unsigned long *aligned)
+{
+ int length, pad;
+ unsigned char *padding;
+
+ length = _cairo_array_num_elements (&font->output);
+ *aligned = (length + 3) & ~3;
+ pad = *aligned - length;
+
+ if (pad) {
+ cairo_status_t status;
+
+ status = cairo_truetype_font_allocate_write_buffer (font, pad,
+ &padding);
+ if (unlikely (status))
+ return status;
+
+ memset (padding, 0, pad);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_check_boundary (cairo_truetype_font_t *font,
+ unsigned long boundary)
+{
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH)
+ {
+ status = _cairo_array_append (&font->string_offsets,
+ &font->last_boundary);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ font->last_offset = font->last_boundary;
+ }
+ font->last_boundary = boundary;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cmap_unicode_range {
+ unsigned int start;
+ unsigned int end;
+} cmap_unicode_range_t;
+
+static cmap_unicode_range_t winansi_unicode_ranges[] = {
+ { 0x0020, 0x007f },
+ { 0x00a0, 0x00ff },
+ { 0x0152, 0x0153 },
+ { 0x0160, 0x0161 },
+ { 0x0178, 0x0178 },
+ { 0x017d, 0x017e },
+ { 0x0192, 0x0192 },
+ { 0x02c6, 0x02c6 },
+ { 0x02dc, 0x02dc },
+ { 0x2013, 0x2026 },
+ { 0x2030, 0x2030 },
+ { 0x2039, 0x203a },
+ { 0x20ac, 0x20ac },
+ { 0x2122, 0x2122 },
+};
+
+static cairo_status_t
+cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ int i;
+ unsigned int j;
+ int range_offset;
+ int num_ranges;
+ int entry_selector;
+ int length;
+
+ num_ranges = ARRAY_LENGTH (winansi_unicode_ranges);
+
+ length = 16 + (num_ranges + 1)*8;
+ for (i = 0; i < num_ranges; i++)
+ length += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2;
+
+ entry_selector = 0;
+ while ((1 << entry_selector) <= (num_ranges + 1))
+ entry_selector++;
+
+ entry_selector--;
+
+ cairo_truetype_font_write_be16 (font, 0); /* Table version */
+ cairo_truetype_font_write_be16 (font, 1); /* Num tables */
+
+ cairo_truetype_font_write_be16 (font, 3); /* Platform */
+ cairo_truetype_font_write_be16 (font, 1); /* Encoding */
+ cairo_truetype_font_write_be32 (font, 12); /* Offset to start of table */
+
+ /* Output a format 4 encoding table for the winansi encoding */
+
+ cairo_truetype_font_write_be16 (font, 4); /* Format */
+ cairo_truetype_font_write_be16 (font, length); /* Length */
+ cairo_truetype_font_write_be16 (font, 0); /* Version */
+ cairo_truetype_font_write_be16 (font, num_ranges*2 + 2); /* 2*segcount */
+ cairo_truetype_font_write_be16 (font, (1 << (entry_selector + 1))); /* searchrange */
+ cairo_truetype_font_write_be16 (font, entry_selector); /* entry selector */
+ cairo_truetype_font_write_be16 (font, num_ranges*2 + 2 - (1 << (entry_selector + 1))); /* rangeshift */
+ for (i = 0; i < num_ranges; i++)
+ cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].end); /* end count[] */
+ cairo_truetype_font_write_be16 (font, 0xffff); /* end count[] */
+
+ cairo_truetype_font_write_be16 (font, 0); /* reserved */
+
+ for (i = 0; i < num_ranges; i++)
+ cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].start); /* startCode[] */
+ cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[] */
+
+ for (i = 0; i < num_ranges; i++)
+ cairo_truetype_font_write_be16 (font, 0x0000); /* delta[] */
+ cairo_truetype_font_write_be16 (font, 1); /* delta[] */
+
+ range_offset = num_ranges*2 + 2;
+ for (i = 0; i < num_ranges; i++) {
+ cairo_truetype_font_write_be16 (font, range_offset); /* rangeOffset[] */
+ range_offset += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2 - 2;
+ }
+ cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[] */
+
+ for (i = 0; i < num_ranges; i++) {
+ for (j = winansi_unicode_ranges[i].start; j < winansi_unicode_ranges[i].end + 1; j++) {
+ int ch = _cairo_unicode_to_winansi (j);
+ int glyph;
+
+ if (ch > 0)
+ glyph = font->scaled_font_subset->latin_to_subset_glyph_index[ch];
+ else
+ glyph = 0;
+ cairo_truetype_font_write_be16 (font, glyph);
+ }
+ }
+
+ return font->status;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ cairo_status_t status;
+ unsigned char *buffer;
+ unsigned long size;
+
+ if (font->status)
+ return font->status;
+
+ size = 0;
+ status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font,
+ tag, 0, NULL, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ tag, 0, buffer, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font,
+ unsigned char *buffer,
+ unsigned long size)
+{
+ tt_glyph_data_t *glyph_data;
+ tt_composite_glyph_t *composite_glyph;
+ int num_args;
+ int has_more_components;
+ unsigned short flags;
+ unsigned short index;
+ cairo_status_t status;
+ unsigned char *end = buffer + size;
+
+ if (font->status)
+ return font->status;
+
+ glyph_data = (tt_glyph_data_t *) buffer;
+ if ((unsigned char *)(&glyph_data->data) >= end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ composite_glyph = &glyph_data->glyph;
+ do {
+ if ((unsigned char *)(&composite_glyph->args[1]) > end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ flags = be16_to_cpu (composite_glyph->flags);
+ has_more_components = flags & TT_MORE_COMPONENTS;
+ status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index);
+ if (unlikely (status))
+ return status;
+
+ composite_glyph->index = cpu_to_be16 (index);
+ num_args = 1;
+ if (flags & TT_ARG_1_AND_2_ARE_WORDS)
+ num_args += 1;
+
+ if (flags & TT_WE_HAVE_A_SCALE)
+ num_args += 1;
+ else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE)
+ num_args += 2;
+ else if (flags & TT_WE_HAVE_A_TWO_BY_TWO)
+ num_args += 4;
+
+ composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]);
+ } while (has_more_components);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ unsigned long start_offset, index, size, next;
+ tt_head_t header;
+ unsigned long begin, end;
+ unsigned char *buffer;
+ unsigned int i;
+ union {
+ unsigned char *bytes;
+ uint16_t *short_offsets;
+ uint32_t *long_offsets;
+ } u;
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ size = sizeof (tt_head_t);
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_head, 0,
+ (unsigned char*) &header, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ if (be16_to_cpu (header.index_to_loc_format) == 0)
+ size = sizeof (int16_t) * (font->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);
+ if (entry == NULL)
+ return;
+
+ 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);
+ if (checksum_location == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ goto FAIL;
+ }
+
+ *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. "cmap" is only embedded for latin fonts. The pos parameter of
+ * cairo_truetype_font_add_truetype_table() specifies the position of
+ * the table in the table directory.
+ */
+static void
+cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font)
+{
+ cairo_bool_t has_cvt = FALSE;
+ cairo_bool_t has_fpgm = FALSE;
+ cairo_bool_t has_prep = FALSE;
+ unsigned long size;
+ int pos;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_cvt, 0, NULL,
+ &size) == CAIRO_INT_STATUS_SUCCESS)
+ has_cvt = TRUE;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_fpgm, 0, NULL,
+ &size) == CAIRO_INT_STATUS_SUCCESS)
+ has_fpgm = TRUE;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_prep, 0, NULL,
+ &size) == CAIRO_INT_STATUS_SUCCESS)
+ has_prep = TRUE;
+
+ font->num_tables = 0;
+ pos = 0;
+ if (font->is_pdf && font->scaled_font_subset->is_latin)
+ pos++;
+ if (has_cvt)
+ pos++;
+ if (has_fpgm)
+ pos++;
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos);
+
+ pos = 0;
+ if (font->is_pdf && font->scaled_font_subset->is_latin)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++);
+ if (has_cvt)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++);
+ if (has_fpgm)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++);
+ pos++;
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++);
+ if (has_prep)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos);
+}
+
+static cairo_status_t
+cairo_truetype_subset_init_internal (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset,
+ cairo_bool_t is_pdf)
+{
+ cairo_truetype_font_t *font = NULL;
+ cairo_status_t status;
+ const char *data = NULL; /* squelch bogus compiler warning */
+ unsigned long length = 0; /* squelch bogus compiler warning */
+ unsigned long offsets_length;
+ unsigned int i;
+ const unsigned long *string_offsets = NULL;
+ unsigned long num_strings = 0;
+
+ status = _cairo_truetype_font_create (font_subset, is_pdf, &font);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ unsigned short parent_glyph = font->scaled_font_subset->glyphs[i];
+ status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph);
+ if (unlikely (status))
+ goto fail1;
+ }
+
+ cairo_truetype_font_create_truetype_table_list (font);
+ status = cairo_truetype_font_generate (font, &data, &length,
+ &string_offsets, &num_strings);
+ if (unlikely (status))
+ goto fail1;
+
+ truetype_subset->ps_name = strdup (font->base.ps_name);
+ if (unlikely (truetype_subset->ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ if (font->base.font_name != NULL) {
+ truetype_subset->family_name_utf8 = strdup (font->base.font_name);
+ if (unlikely (truetype_subset->family_name_utf8 == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ } else {
+ truetype_subset->family_name_utf8 = NULL;
+ }
+
+ /* The widths array returned must contain only widths for the
+ * glyphs in font_subset. Any subglyphs appended after
+ * font_subset->num_glyphs are omitted. */
+ truetype_subset->widths = calloc (sizeof (double),
+ font->scaled_font_subset->num_glyphs);
+ if (unlikely (truetype_subset->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+ truetype_subset->widths[i] = (double)font->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:
+ free (truetype_subset->family_name_utf8);
+ fail2:
+ free (truetype_subset->ps_name);
+ fail1:
+ cairo_truetype_font_destroy (font);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ return cairo_truetype_subset_init_internal (truetype_subset, font_subset, FALSE);
+}
+
+cairo_status_t
+_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ return cairo_truetype_subset_init_internal (truetype_subset, font_subset, TRUE);
+}
+
+void
+_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset)
+{
+ free (subset->ps_name);
+ free (subset->family_name_utf8);
+ free (subset->widths);
+ free (subset->data);
+ free (subset->string_offsets);
+}
+
+static cairo_int_status_t
+_cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font,
+ unsigned long table_offset,
+ unsigned long index,
+ uint32_t *ucs4)
+{
+ cairo_status_t status;
+ const cairo_scaled_font_backend_t *backend;
+ tt_segment_map_t *map;
+ 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 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]);
+
+ /* 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_int_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;
+}
+
+static cairo_status_t
+find_name (tt_name_t *name, int name_id, int platform, int encoding, int language, char **str_out)
+{
+ tt_name_record_t *record;
+ int i, len;
+ char *str;
+ char *p;
+ cairo_bool_t has_tag;
+ cairo_status_t status;
+
+ str = NULL;
+ for (i = 0; i < be16_to_cpu (name->num_records); i++) {
+ record = &(name->records[i]);
+ if (be16_to_cpu (record->name) == name_id &&
+ be16_to_cpu (record->platform) == platform &&
+ be16_to_cpu (record->encoding) == encoding &&
+ (language == -1 || be16_to_cpu (record->language) == language)) {
+
+ str = malloc (be16_to_cpu (record->length) + 1);
+ if (str == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ len = be16_to_cpu (record->length);
+ memcpy (str,
+ ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
+ len);
+ str[be16_to_cpu (record->length)] = 0;
+ break;
+ }
+ }
+ if (str == NULL) {
+ *str_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (platform == 3) { /* Win platform, unicode encoding */
+ /* convert to utf8 */
+ int size = 0;
+ char *utf8;
+ uint16_t *u = (uint16_t *) str;
+ int u_len = len/2;
+
+ for (i = 0; i < u_len; i++)
+ size += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), NULL);
+
+ utf8 = malloc (size + 1);
+ if (utf8 == NULL) {
+ status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+ p = utf8;
+ for (i = 0; i < u_len; i++)
+ p += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), p);
+ *p = 0;
+ free (str);
+ str = utf8;
+ } else if (platform == 1) { /* Mac platform, Mac Roman encoding */
+ /* Replace characters above 127 with underscores. We could use
+ * a lookup table to convert to unicode but since most fonts
+ * include a unicode name this is just a rarely used fallback. */
+ for (i = 0; i < len; i++) {
+ if ((unsigned char)str[i] > 127)
+ str[i] = '_';
+ }
+ }
+
+ /* If font name is prefixed with a PDF subset tag, strip it off. */
+ p = str;
+ len = strlen (str);
+ has_tag = FALSE;
+ if (len > 7 && p[6] == '+') {
+ has_tag = TRUE;
+ for (i = 0; i < 6; i++) {
+ if (p[i] < 'A' || p[i] > 'Z') {
+ has_tag = FALSE;
+ break;
+ }
+ }
+ }
+ if (has_tag) {
+ p = malloc (len - 6);
+ if (unlikely (p == NULL)) {
+ status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+ memcpy (p, str + 7, len - 7);
+ p[len-7] = 0;
+ free (str);
+ str = p;
+ }
+
+ *str_out = str;
+
+ return CAIRO_STATUS_SUCCESS;
+
+ fail:
+ free (str);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font,
+ char **ps_name_out,
+ char **font_name_out)
+{
+ cairo_status_t status;
+ const cairo_scaled_font_backend_t *backend;
+ tt_name_t *name;
+ unsigned long size;
+ char *ps_name = NULL;
+ char *family_name = NULL;
+
+ backend = scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_name, 0,
+ NULL,
+ &size);
+ if (status)
+ return status;
+
+ name = malloc (size);
+ if (name == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_name, 0,
+ (unsigned char *) name,
+ &size);
+ if (status)
+ goto fail;
+
+ /* Find PS Name (name_id = 6). OT spec says PS name must be one of
+ * the following two encodings */
+ status = find_name (name, 6, 3, 1, 0x409, &ps_name); /* win, unicode, english-us */
+ if (unlikely(status))
+ goto fail;
+
+ if (!ps_name) {
+ status = find_name (name, 6, 1, 0, 0, &ps_name); /* mac, roman, english */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ /* Find Family name (name_id = 1) */
+ status = find_name (name, 1, 3, 1, 0x409, &family_name); /* win, unicode, english-us */
+ if (unlikely(status))
+ goto fail;
+
+ if (!family_name) {
+ status = find_name (name, 1, 3, 0, 0x409, &family_name); /* win, symbol, english-us */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ if (!family_name) {
+ status = find_name (name, 1, 1, 0, 0, &family_name); /* mac, roman, english */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ if (!family_name) {
+ status = find_name (name, 1, 3, 1, -1, &family_name); /* win, unicode, any language */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ free (name);
+
+ /* Ensure PS name is a valid PDF/PS name object. In PDF names are
+ * treated as UTF8 and non ASCII bytes, ' ', and '#' are encoded
+ * as '#' followed by 2 hex digits that encode the byte. By also
+ * encoding the characters in the reserved string we ensure the
+ * name is also PS compatible. */
+ if (ps_name) {
+ static const char *reserved = "()<>[]{}/%#\\";
+ char buf[128]; /* max name length is 127 bytes */
+ char *src = ps_name;
+ char *dst = buf;
+
+ while (*src && dst < buf + 127) {
+ unsigned char c = *src;
+ if (c < 0x21 || c > 0x7e || strchr (reserved, c)) {
+ if (dst + 4 > buf + 127)
+ break;
+
+ snprintf (dst, 4, "#%02X", c);
+ src++;
+ dst += 3;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ *dst = 0;
+ free (ps_name);
+ ps_name = strdup (buf);
+ if (ps_name == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+ }
+
+ *ps_name_out = ps_name;
+ *font_name_out = family_name;
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail:
+ free (name);
+ free (ps_name);
+ free (family_name);
+ *ps_name_out = NULL;
+ *font_name_out = NULL;
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_get_style (cairo_scaled_font_t *scaled_font,
+ int *weight,
+ cairo_bool_t *bold,
+ cairo_bool_t *italic)
+{
+ cairo_status_t status;
+ const cairo_scaled_font_backend_t *backend;
+ tt_os2_t os2;
+ unsigned long size;
+ uint16_t selection;
+
+ backend = scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_OS2, 0,
+ NULL,
+ &size);
+ if (status)
+ return status;
+
+ if (size < sizeof(os2))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = sizeof (os2);
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_OS2, 0,
+ (unsigned char *) &os2,
+ &size);
+ if (status)
+ return status;
+
+ *weight = be16_to_cpu (os2.usWeightClass);
+ selection = be16_to_cpu (os2.fsSelection);
+ *bold = (selection & TT_FS_SELECTION_BOLD) ? TRUE : FALSE;
+ *italic = (selection & TT_FS_SELECTION_ITALIC) ? TRUE : FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type1-fallback.c b/src/cairo-type1-fallback.c
new file mode 100755
index 000000000..d7d6eae12
--- /dev/null
+++ b/src/cairo-type1-fallback.c
@@ -0,0 +1,907 @@
+/* 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-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-type1-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-output-stream-private.h"
+
+typedef enum {
+ CAIRO_CHARSTRING_TYPE1,
+ CAIRO_CHARSTRING_TYPE2
+} cairo_charstring_type_t;
+
+typedef struct _cairo_type1_font {
+ int *widths;
+
+ cairo_scaled_font_subset_t *scaled_font_subset;
+ cairo_scaled_font_t *type1_scaled_font;
+
+ cairo_array_t contents;
+
+ double x_min, y_min, x_max, y_max;
+
+ const char *data;
+ unsigned long header_size;
+ unsigned long data_size;
+ unsigned long trailer_size;
+ int bbox_position;
+ int bbox_max_chars;
+
+ cairo_output_stream_t *output;
+
+ unsigned short eexec_key;
+ cairo_bool_t hex_encode;
+ int hex_column;
+} cairo_type1_font_t;
+
+static cairo_status_t
+cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_type1_font_t **subset_return,
+ cairo_bool_t hex_encode)
+{
+ cairo_type1_font_t *font;
+ cairo_font_face_t *font_face;
+ cairo_matrix_t font_matrix;
+ cairo_matrix_t ctm;
+ cairo_font_options_t font_options;
+ cairo_status_t status;
+
+ font = calloc (1, sizeof (cairo_type1_font_t));
+ if (unlikely (font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int));
+ if (unlikely (font->widths == NULL)) {
+ free (font);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ font->scaled_font_subset = scaled_font_subset;
+ font->hex_encode = hex_encode;
+
+ font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font);
+
+ cairo_matrix_init_scale (&font_matrix, 1000, -1000);
+ cairo_matrix_init_identity (&ctm);
+
+ _cairo_font_options_init_default (&font_options);
+ cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
+ cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
+
+ font->type1_scaled_font = cairo_scaled_font_create (font_face,
+ &font_matrix,
+ &ctm,
+ &font_options);
+ status = font->type1_scaled_font->status;
+ if (unlikely (status))
+ goto fail;
+
+ _cairo_array_init (&font->contents, sizeof (unsigned char));
+ font->output = NULL;
+
+ *subset_return = font;
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail:
+ free (font->widths);
+ free (font);
+
+ return status;
+}
+
+/* Charstring commands. If the high byte is 0 the command is encoded
+ * with a single byte. */
+#define CHARSTRING_sbw 0x0c07
+#define CHARSTRING_rmoveto 0x0015
+#define CHARSTRING_rlineto 0x0005
+#define CHARSTRING_rcurveto 0x0008
+#define CHARSTRING_closepath 0x0009
+#define CHARSTRING_endchar 0x000e
+
+/* Before calling this function, the caller must allocate sufficient
+ * space in data (see _cairo_array_grow_by). The maximum number of
+ * bytes that will be used is 2.
+ */
+static void
+charstring_encode_command (cairo_array_t *data, int command)
+{
+ cairo_status_t status;
+ unsigned int orig_size;
+ unsigned char buf[5];
+ unsigned char *p = buf;
+
+ if (command & 0xff00)
+ *p++ = command >> 8;
+ *p++ = command & 0x00ff;
+
+ /* Ensure the array doesn't grow, which allows this function to
+ * have no possibility of failure. */
+ orig_size = _cairo_array_size (data);
+ status = _cairo_array_append_multiple (data, buf, p - buf);
+
+ assert (status == CAIRO_STATUS_SUCCESS);
+ assert (_cairo_array_size (data) == orig_size);
+}
+
+/* Before calling this function, the caller must allocate sufficient
+ * space in data (see _cairo_array_grow_by). The maximum number of
+ * bytes that will be used is 5.
+ */
+static void
+charstring_encode_integer (cairo_array_t *data,
+ int i,
+ cairo_charstring_type_t type)
+{
+ cairo_status_t status;
+ unsigned int orig_size;
+ unsigned char buf[10];
+ unsigned char *p = buf;
+
+ if (i >= -107 && i <= 107) {
+ *p++ = i + 139;
+ } else if (i >= 108 && i <= 1131) {
+ i -= 108;
+ *p++ = (i >> 8)+ 247;
+ *p++ = i & 0xff;
+ } else if (i >= -1131 && i <= -108) {
+ i = -i - 108;
+ *p++ = (i >> 8)+ 251;
+ *p++ = i & 0xff;
+ } else {
+ if (type == CAIRO_CHARSTRING_TYPE1) {
+ *p++ = 0xff;
+ *p++ = i >> 24;
+ *p++ = (i >> 16) & 0xff;
+ *p++ = (i >> 8) & 0xff;
+ *p++ = i & 0xff;
+ } else {
+ *p++ = 0xff;
+ *p++ = (i >> 8) & 0xff;
+ *p++ = i & 0xff;
+ *p++ = 0;
+ *p++ = 0;
+ }
+ }
+
+ /* Ensure the array doesn't grow, which allows this function to
+ * have no possibility of failure. */
+ orig_size = _cairo_array_size (data);
+ status = _cairo_array_append_multiple (data, buf, p - buf);
+
+ assert (status == CAIRO_STATUS_SUCCESS);
+ assert (_cairo_array_size (data) == orig_size);
+}
+
+typedef struct _ps_path_info {
+ cairo_array_t *data;
+ int current_x, current_y;
+ cairo_charstring_type_t type;
+} t1_path_info_t;
+
+static cairo_status_t
+_charstring_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ t1_path_info_t *path_info = (t1_path_info_t *) closure;
+ int dx, dy;
+ cairo_status_t status;
+
+ status = _cairo_array_grow_by (path_info->data, 12);
+ if (unlikely (status))
+ return status;
+
+ dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
+ dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
+ charstring_encode_integer (path_info->data, dx, path_info->type);
+ charstring_encode_integer (path_info->data, dy, path_info->type);
+ path_info->current_x += dx;
+ path_info->current_y += dy;
+
+ charstring_encode_command (path_info->data, CHARSTRING_rmoveto);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_charstring_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ t1_path_info_t *path_info = (t1_path_info_t *) closure;
+ int dx, dy;
+ cairo_status_t status;
+
+ status = _cairo_array_grow_by (path_info->data, 12);
+ if (unlikely (status))
+ return status;
+
+ dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
+ dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
+ charstring_encode_integer (path_info->data, dx, path_info->type);
+ charstring_encode_integer (path_info->data, dy, path_info->type);
+ path_info->current_x += dx;
+ path_info->current_y += dy;
+
+ charstring_encode_command (path_info->data, CHARSTRING_rlineto);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_charstring_curve_to (void *closure,
+ const cairo_point_t *point1,
+ const cairo_point_t *point2,
+ const cairo_point_t *point3)
+{
+ t1_path_info_t *path_info = (t1_path_info_t *) closure;
+ int dx1, dy1, dx2, dy2, dx3, dy3;
+ cairo_status_t status;
+
+ status = _cairo_array_grow_by (path_info->data, 32);
+ if (unlikely (status))
+ return status;
+
+ dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x;
+ dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y;
+ dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1;
+ dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1;
+ dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2;
+ dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2;
+ charstring_encode_integer (path_info->data, dx1, path_info->type);
+ charstring_encode_integer (path_info->data, dy1, path_info->type);
+ charstring_encode_integer (path_info->data, dx2, path_info->type);
+ charstring_encode_integer (path_info->data, dy2, path_info->type);
+ charstring_encode_integer (path_info->data, dx3, path_info->type);
+ charstring_encode_integer (path_info->data, dy3, path_info->type);
+ path_info->current_x += dx1 + dx2 + dx3;
+ path_info->current_y += dy1 + dy2 + dy3;
+ charstring_encode_command (path_info->data, CHARSTRING_rcurveto);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_charstring_close_path (void *closure)
+{
+ cairo_status_t status;
+ t1_path_info_t *path_info = (t1_path_info_t *) closure;
+
+ if (path_info->type == CAIRO_CHARSTRING_TYPE2)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_array_grow_by (path_info->data, 2);
+ if (unlikely (status))
+ return status;
+
+ charstring_encode_command (path_info->data, CHARSTRING_closepath);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+charstring_encrypt (cairo_array_t *data)
+{
+ unsigned char *d, *end;
+ uint16_t c, p, r;
+
+ r = CAIRO_TYPE1_CHARSTRING_KEY;
+ d = (unsigned char *) _cairo_array_index (data, 0);
+ if (d == NULL)
+ return;
+
+ end = d + _cairo_array_num_elements (data);
+ while (d < end) {
+ p = *d;
+ c = p ^ (r >> 8);
+ r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+ *d++ = c;
+ }
+}
+
+static cairo_int_status_t
+cairo_type1_font_create_charstring (cairo_type1_font_t *font,
+ int subset_index,
+ int glyph_index,
+ cairo_charstring_type_t type,
+ cairo_array_t *data)
+{
+ cairo_int_status_t status;
+ cairo_scaled_glyph_t *scaled_glyph;
+ t1_path_info_t path_info;
+ cairo_text_extents_t *metrics;
+ cairo_bool_t emit_path = TRUE;
+
+ /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */
+ status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS|
+ CAIRO_SCALED_GLYPH_INFO_PATH,
+ &scaled_glyph);
+
+ /* It is ok for the .notdef glyph to not have a path available. We
+ * just need the metrics to emit an empty glyph. */
+ if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ emit_path = FALSE;
+ status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ }
+ if (unlikely (status))
+ return status;
+
+ metrics = &scaled_glyph->metrics;
+ if (subset_index == 0) {
+ font->x_min = metrics->x_bearing;
+ font->y_min = metrics->y_bearing;
+ font->x_max = metrics->x_bearing + metrics->width;
+ font->y_max = metrics->y_bearing + metrics->height;
+ } else {
+ if (metrics->x_bearing < font->x_min)
+ font->x_min = metrics->x_bearing;
+ if (metrics->y_bearing < font->y_min)
+ font->y_min = metrics->y_bearing;
+ if (metrics->x_bearing + metrics->width > font->x_max)
+ font->x_max = metrics->x_bearing + metrics->width;
+ if (metrics->y_bearing + metrics->height > font->y_max)
+ font->y_max = metrics->y_bearing + metrics->height;
+ }
+ font->widths[subset_index] = metrics->x_advance;
+
+ status = _cairo_array_grow_by (data, 30);
+ if (unlikely (status))
+ return status;
+
+ if (type == CAIRO_CHARSTRING_TYPE1) {
+ charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type);
+ charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type);
+ charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
+ charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type);
+ charstring_encode_command (data, CHARSTRING_sbw);
+
+ path_info.current_x = (int) scaled_glyph->metrics.x_bearing;
+ path_info.current_y = (int) scaled_glyph->metrics.y_bearing;
+ } else {
+ charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
+
+ path_info.current_x = 0;
+ path_info.current_y = 0;
+ }
+ path_info.data = data;
+ path_info.type = type;
+ if (emit_path) {
+ status = _cairo_path_fixed_interpret (scaled_glyph->path,
+ _charstring_move_to,
+ _charstring_line_to,
+ _charstring_curve_to,
+ _charstring_close_path,
+ &path_info);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_array_grow_by (data, 1);
+ if (unlikely (status))
+ return status;
+ charstring_encode_command (path_info.data, CHARSTRING_endchar);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_type1_font_write_charstrings (cairo_type1_font_t *font,
+ cairo_output_stream_t *encrypted_output)
+{
+ cairo_status_t status;
+ unsigned char zeros[] = { 0, 0, 0, 0 };
+ cairo_array_t data;
+ unsigned int i;
+ int length;
+
+ _cairo_array_init (&data, sizeof (unsigned char));
+ status = _cairo_array_grow_by (&data, 1024);
+ if (unlikely (status))
+ goto fail;
+
+ _cairo_output_stream_printf (encrypted_output,
+ "2 index /CharStrings %d dict dup begin\n",
+ font->scaled_font_subset->num_glyphs + 1);
+
+ _cairo_scaled_font_freeze_cache (font->type1_scaled_font);
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ _cairo_array_truncate (&data, 0);
+ /* four "random" bytes required by encryption algorithm */
+ status = _cairo_array_append_multiple (&data, zeros, 4);
+ if (unlikely (status))
+ break;
+
+ status = cairo_type1_font_create_charstring (font, i,
+ font->scaled_font_subset->glyphs[i],
+ CAIRO_CHARSTRING_TYPE1,
+ &data);
+ if (unlikely (status))
+ break;
+
+ charstring_encrypt (&data);
+ length = _cairo_array_num_elements (&data);
+ if (font->scaled_font_subset->glyph_names != NULL) {
+ _cairo_output_stream_printf (encrypted_output, "/%s %d RD ",
+ font->scaled_font_subset->glyph_names[i],
+ length);
+ } else if (i == 0) {
+ _cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length);
+ } else {
+ _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length);
+ }
+ _cairo_output_stream_write (encrypted_output,
+ _cairo_array_index (&data, 0),
+ length);
+ _cairo_output_stream_printf (encrypted_output, " ND\n");
+ }
+ _cairo_scaled_font_thaw_cache (font->type1_scaled_font);
+
+fail:
+ _cairo_array_fini (&data);
+ return status;
+}
+
+static void
+cairo_type1_font_write_header (cairo_type1_font_t *font,
+ const char *name)
+{
+ unsigned int i;
+ const char spaces[50] = " ";
+
+ _cairo_output_stream_printf (font->output,
+ "%%!FontType1-1.1 %s 1.0\n"
+ "11 dict begin\n"
+ "/FontName /%s def\n"
+ "/PaintType 0 def\n"
+ "/FontType 1 def\n"
+ "/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n",
+ name,
+ name);
+
+ /* We don't know the bbox values until after the charstrings have
+ * been generated. Reserve some space and fill in the bbox
+ * later. */
+
+ /* Worst case for four signed ints with spaces between each number */
+ font->bbox_max_chars = 50;
+
+ _cairo_output_stream_printf (font->output, "/FontBBox {");
+ font->bbox_position = _cairo_output_stream_get_position (font->output);
+ _cairo_output_stream_write (font->output, spaces, font->bbox_max_chars);
+
+ _cairo_output_stream_printf (font->output,
+ "} readonly def\n"
+ "/Encoding 256 array\n"
+ "0 1 255 {1 index exch /.notdef put} for\n");
+ if (font->scaled_font_subset->is_latin) {
+ for (i = 1; i < 256; i++) {
+ int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i];
+
+ if (subset_glyph > 0) {
+ if (font->scaled_font_subset->glyph_names != NULL) {
+ _cairo_output_stream_printf (font->output, "dup %d /%s put\n",
+ i, font->scaled_font_subset->glyph_names[subset_glyph]);
+ } else {
+ _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, subset_glyph);
+ }
+ }
+ }
+ } else {
+ for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+ if (font->scaled_font_subset->glyph_names != NULL) {
+ _cairo_output_stream_printf (font->output, "dup %d /%s put\n",
+ i, font->scaled_font_subset->glyph_names[i]);
+ } else {
+ _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i);
+ }
+ }
+ }
+ _cairo_output_stream_printf (font->output,
+ "readonly def\n"
+ "currentdict end\n"
+ "currentfile eexec\n");
+}
+
+static cairo_status_t
+cairo_type1_write_stream_encrypted (void *closure,
+ const unsigned char *data,
+ unsigned int length)
+{
+ const unsigned char *in, *end;
+ uint16_t c, p;
+ static const char hex_digits[16] = "0123456789abcdef";
+ char digits[3];
+ cairo_type1_font_t *font = closure;
+
+ in = (const unsigned char *) data;
+ end = (const unsigned char *) data + length;
+ while (in < end) {
+ p = *in++;
+ c = p ^ (font->eexec_key >> 8);
+ font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+
+ if (font->hex_encode) {
+ digits[0] = hex_digits[c >> 4];
+ digits[1] = hex_digits[c & 0x0f];
+ digits[2] = '\n';
+ font->hex_column += 2;
+
+ if (font->hex_column == 78) {
+ _cairo_output_stream_write (font->output, digits, 3);
+ font->hex_column = 0;
+ } else {
+ _cairo_output_stream_write (font->output, digits, 2);
+ }
+ } else {
+ digits[0] = c;
+ _cairo_output_stream_write (font->output, digits, 1);
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_type1_font_write_private_dict (cairo_type1_font_t *font,
+ const char *name)
+{
+ cairo_int_status_t status;
+ cairo_status_t status2;
+ cairo_output_stream_t *encrypted_output;
+
+ font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
+ font->hex_column = 0;
+ encrypted_output = _cairo_output_stream_create (
+ cairo_type1_write_stream_encrypted,
+ NULL,
+ font);
+ if (_cairo_output_stream_get_status (encrypted_output))
+ return _cairo_output_stream_destroy (encrypted_output);
+
+ /* Note: the first four spaces at the start of this private dict
+ * are the four "random" bytes of plaintext required by the
+ * encryption algorithm */
+ _cairo_output_stream_printf (encrypted_output,
+ " dup /Private 9 dict dup begin\n"
+ "/RD {string currentfile exch readstring pop}"
+ " bind executeonly def\n"
+ "/ND {noaccess def} executeonly def\n"
+ "/NP {noaccess put} executeonly def\n"
+ "/BlueValues [] def\n"
+ "/MinFeature {16 16} def\n"
+ "/lenIV 4 def\n"
+ "/password 5839 def\n");
+
+ status = cairo_type1_font_write_charstrings (font, encrypted_output);
+ if (unlikely (status))
+ goto fail;
+
+ _cairo_output_stream_printf (encrypted_output,
+ "end\n"
+ "end\n"
+ "readonly put\n"
+ "noaccess put\n"
+ "dup /FontName get exch definefont pop\n"
+ "mark currentfile closefile\n");
+
+ fail:
+ status2 = _cairo_output_stream_destroy (encrypted_output);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = status2;
+
+ return status;
+}
+
+static void
+cairo_type1_font_write_trailer(cairo_type1_font_t *font)
+{
+ int i;
+ static const char zeros[65] =
+ "0000000000000000000000000000000000000000000000000000000000000000\n";
+
+ for (i = 0; i < 8; i++)
+ _cairo_output_stream_write (font->output, zeros, sizeof zeros);
+
+ _cairo_output_stream_printf (font->output, "cleartomark\n");
+}
+
+static cairo_status_t
+cairo_type1_write_stream (void *closure,
+ const unsigned char *data,
+ unsigned int length)
+{
+ cairo_type1_font_t *font = closure;
+
+ return _cairo_array_append_multiple (&font->contents, data, length);
+}
+
+static cairo_int_status_t
+cairo_type1_font_write (cairo_type1_font_t *font,
+ const char *name)
+{
+ cairo_int_status_t status;
+
+ cairo_type1_font_write_header (font, name);
+ font->header_size = _cairo_output_stream_get_position (font->output);
+
+ status = cairo_type1_font_write_private_dict (font, name);
+ if (unlikely (status))
+ return status;
+
+ font->data_size = _cairo_output_stream_get_position (font->output) -
+ font->header_size;
+
+ cairo_type1_font_write_trailer (font);
+ font->trailer_size =
+ _cairo_output_stream_get_position (font->output) -
+ font->header_size - font->data_size;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_type1_font_generate (cairo_type1_font_t *font, const char *name)
+{
+ cairo_int_status_t status;
+
+ status = _cairo_array_grow_by (&font->contents, 4096);
+ if (unlikely (status))
+ return status;
+
+ font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font);
+ if (_cairo_output_stream_get_status (font->output))
+ return _cairo_output_stream_destroy (font->output);
+
+ status = cairo_type1_font_write (font, name);
+ if (unlikely (status))
+ return status;
+
+ font->data = _cairo_array_index (&font->contents, 0);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_destroy (cairo_type1_font_t *font)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ free (font->widths);
+ cairo_scaled_font_destroy (font->type1_scaled_font);
+ _cairo_array_fini (&font->contents);
+ if (font->output)
+ status = _cairo_output_stream_destroy (font->output);
+ free (font);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_bool_t hex_encode)
+{
+ cairo_type1_font_t *font;
+ cairo_status_t status;
+ unsigned long length;
+ unsigned int i, len;
+
+ status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_generate (font, name);
+ if (unlikely (status))
+ goto fail1;
+
+ type1_subset->base_font = strdup (name);
+ if (unlikely (type1_subset->base_font == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
+ if (unlikely (type1_subset->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+ type1_subset->widths[i] = (double)font->widths[i]/1000;
+
+ type1_subset->x_min = (double)font->x_min/1000;
+ type1_subset->y_min = (double)font->y_min/1000;
+ type1_subset->x_max = (double)font->x_max/1000;
+ type1_subset->y_max = (double)font->y_max/1000;
+ type1_subset->ascent = (double)font->y_max/1000;
+ type1_subset->descent = (double)font->y_min/1000;
+
+ length = font->header_size + font->data_size +
+ font->trailer_size;
+ type1_subset->data = malloc (length);
+ if (unlikely (type1_subset->data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+ memcpy (type1_subset->data,
+ _cairo_array_index (&font->contents, 0), length);
+
+ len = snprintf(type1_subset->data + font->bbox_position,
+ font->bbox_max_chars,
+ "%d %d %d %d",
+ (int)font->x_min,
+ (int)font->y_min,
+ (int)font->x_max,
+ (int)font->y_max);
+ type1_subset->data[font->bbox_position + len] = ' ';
+
+ type1_subset->header_length = font->header_size;
+ type1_subset->data_length = font->data_size;
+ type1_subset->trailer_length = font->trailer_size;
+
+ return cairo_type1_font_destroy (font);
+
+ fail3:
+ free (type1_subset->widths);
+ fail2:
+ free (type1_subset->base_font);
+ fail1:
+ /* status is already set, ignore further errors */
+ cairo_type1_font_destroy (font);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *scaled_font_subset)
+{
+ return _cairo_type1_fallback_init_internal (type1_subset,
+ name,
+ scaled_font_subset, FALSE);
+}
+
+cairo_status_t
+_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *scaled_font_subset)
+{
+ return _cairo_type1_fallback_init_internal (type1_subset,
+ name,
+ scaled_font_subset, TRUE);
+}
+
+void
+_cairo_type1_fallback_fini (cairo_type1_subset_t *subset)
+{
+ free (subset->base_font);
+ free (subset->widths);
+ free (subset->data);
+}
+
+cairo_status_t
+_cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset,
+ cairo_scaled_font_subset_t *scaled_font_subset)
+{
+ cairo_type1_font_t *font;
+ cairo_status_t status;
+ unsigned int i;
+ cairo_array_t charstring;
+
+ status = cairo_type1_font_create (scaled_font_subset, &font, FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t));
+
+ type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs);
+ if (unlikely (type2_subset->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ _cairo_scaled_font_freeze_cache (font->type1_scaled_font);
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ _cairo_array_init (&charstring, sizeof (unsigned char));
+ status = _cairo_array_grow_by (&charstring, 32);
+ if (unlikely (status))
+ goto fail2;
+
+ status = cairo_type1_font_create_charstring (font, i,
+ font->scaled_font_subset->glyphs[i],
+ CAIRO_CHARSTRING_TYPE2,
+ &charstring);
+ if (unlikely (status))
+ goto fail2;
+
+ status = _cairo_array_append (&type2_subset->charstrings, &charstring);
+ if (unlikely (status))
+ goto fail2;
+ }
+ _cairo_scaled_font_thaw_cache (font->type1_scaled_font);
+
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+ type2_subset->widths[i] = font->widths[i];
+
+ type2_subset->x_min = (int) font->x_min;
+ type2_subset->y_min = (int) font->y_min;
+ type2_subset->x_max = (int) font->x_max;
+ type2_subset->y_max = (int) font->y_max;
+ type2_subset->ascent = (int) font->y_max;
+ type2_subset->descent = (int) font->y_min;
+
+ return cairo_type1_font_destroy (font);
+
+fail2:
+ _cairo_scaled_font_thaw_cache (font->type1_scaled_font);
+ _cairo_array_fini (&charstring);
+ _cairo_type2_charstrings_fini (type2_subset);
+fail1:
+ cairo_type1_font_destroy (font);
+ return status;
+}
+
+void
+_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset)
+{
+ unsigned int i, num_charstrings;
+ cairo_array_t *charstring;
+
+ num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings);
+ for (i = 0; i < num_charstrings; i++) {
+ charstring = _cairo_array_index (&type2_subset->charstrings, i);
+ if (charstring)
+ _cairo_array_fini (charstring);
+ }
+ _cairo_array_fini (&type2_subset->charstrings);
+
+ free (type2_subset->widths);
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type1-glyph-names.c b/src/cairo-type1-glyph-names.c
new file mode 100755
index 000000000..80ccb9626
--- /dev/null
+++ b/src/cairo-type1-glyph-names.c
@@ -0,0 +1,410 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-type1-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+#if 0
+/*
+ * The three tables that follow are generated using this perl code:
+ */
+
+@ps_standard_encoding = (
+ # 0
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ # 16
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ # 32
+ "space", "exclam", "quotedbl", "numbersign",
+ "dollar", "percent", "ampersand", "quoteright",
+ "parenleft", "parenright", "asterisk", "plus",
+ "comma", "hyphen", "period", "slash",
+ # 48
+ "zero", "one", "two", "three",
+ "four", "five", "six", "seven",
+ "eight", "nine", "colon", "semicolon",
+ "less", "equal", "greater", "question",
+ # 64
+ "at", "A", "B", "C",
+ "D", "E", "F", "G",
+ "H", "I", "J", "K",
+ "L", "M", "N", "O",
+ # 80
+ "P", "Q", "R", "S",
+ "T", "U", "V", "W",
+ "X", "Y", "Z", "bracketleft",
+ "backslash", "bracketright", "asciicircum", "underscore",
+ # 96
+ "quoteleft", "a", "b", "c",
+ "d", "e", "f", "g",
+ "h", "i", "j", "k",
+ "l", "m", "n", "o",
+ # 112
+ "p", "q", "r", "s",
+ "t", "u", "v", "w",
+ "x", "y", "z", "braceleft",
+ "bar", "braceright", "asciitilde", NULL,
+ # 128
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ # 144
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ # 160
+ NULL, "exclamdown", "cent", "sterling",
+ "fraction", "yen", "florin", "section",
+ "currency", "quotesingle", "quotedblleft", "guillemotleft",
+ "guilsinglleft","guilsinglright","fi", "fl",
+ # 176
+ NULL, "endash", "dagger", "daggerdbl",
+ "periodcentered",NULL, "paragraph", "bullet",
+ "quotesinglbase","quotedblbase","quotedblright","guillemotright",
+ "ellipsis", "perthousand", NULL, "questiondown",
+ # 192
+ NULL, "grave", "acute", "circumflex",
+ "tilde", "macron", "breve", "dotaccent",
+ "dieresis", NULL, "ring", "cedilla",
+ NULL, "hungarumlaut", "ogonek", "caron",
+ # 208
+ "emdash", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ # 224
+ NULL, "AE", NULL, "ordfeminine",
+ NULL, NULL, NULL, NULL,
+ "Lslash", "Oslash", "OE", "ordmasculine",
+ NULL, NULL, NULL, NULL,
+ # 240
+ NULL, "ae", NULL, NULL,
+ NULL, "dotlessi", NULL, NULL,
+ "lslash", "oslash", "oe", "germandbls",
+ NULL, NULL, NULL, NULL
+ );
+
+@winansi_encoding = (
+ # 0
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ # 16
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ # 32
+ "space", "exclam", "quotedbl", "numbersign",
+ "dollar", "percent", "ampersand", "quotesingle",
+ "parenleft", "parenright", "asterisk", "plus",
+ "comma", "hyphen", "period", "slash",
+ # 48
+ "zero", "one", "two", "three",
+ "four", "five", "six", "seven",
+ "eight", "nine", "colon", "semicolon",
+ "less", "equal", "greater", "question",
+ # 64
+ "at", "A", "B", "C",
+ "D", "E", "F", "G",
+ "H", "I", "J", "K",
+ "L", "M", "N", "O",
+ # 80
+ "P", "Q", "R", "S",
+ "T", "U", "V", "W",
+ "X", "Y", "Z", "bracketleft",
+ "backslash", "bracketright", "asciicircum", "underscore",
+ # 96
+ "grave", "a", "b", "c",
+ "d", "e", "f", "g",
+ "h", "i", "j", "k",
+ "l", "m", "n", "o",
+ # 112
+ "p", "q", "r", "s",
+ "t", "u", "v", "w",
+ "x", "y", "z", "braceleft",
+ "bar", "braceright", "asciitilde", NULL,
+ # 128
+ "Euro", NULL, "quotesinglbase","florin",
+ "quotedblbase", "ellipsis", "dagger", "daggerdbl",
+ "circumflex", "perthousand", "Scaron", "guilsinglleft",
+ "OE", NULL, "Zcaron", NULL,
+ # 144
+ NULL, "quoteleft", "quoteright", "quotedblleft",
+ "quotedblright","bullet", "endash", "emdash",
+ "tilde", "trademark", "scaron", "guilsinglright",
+ "oe", NULL, "zcaron", "Ydieresis",
+ # 160
+ NULL, "exclamdown", "cent", "sterling",
+ "currency", "yen", "brokenbar", "section",
+ "dieresis", "copyright", "ordfeminine", "guillemotleft",
+ # 173 is also "hyphen" but we leave this NULL to avoid duplicate names
+ "logicalnot", NULL, "registered", "macron",
+ # 176
+ "degree", "plusminus", "twosuperior", "threesuperior",
+ "acute", "mu", "paragraph", "periodcentered",
+ "cedilla", "onesuperior", "ordmasculine", "guillemotright",
+ "onequarter", "onehalf", "threequarters","questiondown",
+ # 192
+ "Agrave", "Aacute", "Acircumflex", "Atilde",
+ "Adieresis", "Aring", "AE", "Ccedilla",
+ "Egrave", "Eacute", "Ecircumflex", "Edieresis",
+ "Igrave", "Iacute", "Icircumflex", "Idieresis",
+ # 208
+ "Eth", "Ntilde", "Ograve", "Oacute",
+ "Ocircumflex", "Otilde", "Odieresis", "multiply",
+ "Oslash", "Ugrave", "Uacute", "Ucircumflex",
+ "Udieresis", "Yacute", "Thorn", "germandbls",
+ # 224
+ "agrave", "aacute", "acircumflex", "atilde",
+ "adieresis", "aring", "ae", "ccedilla",
+ "egrave", "eacute", "ecircumflex", "edieresis",
+ "igrave", "iacute", "icircumflex", "idieresis",
+ # 240
+ "eth", "ntilde", "ograve", "oacute",
+ "ocircumflex", "otilde", "odieresis", "divide",
+ "oslash", "ugrave", "uacute", "ucircumflex",
+ "udieresis", "yacute", "thorn", "ydieresis"
+);
+
+sub print_offsets {
+ $s = qq();
+ for $sym (@_) {
+ if (! ($sym eq NULL)) {
+ $ss = qq( $hash{$sym}/*$sym*/,);
+ } else {
+ $ss = qq( 0,);
+ }
+ if (length($s) + length($ss) > 78) {
+ print qq( $s\n);
+ $s = "";
+ }
+ $s .= $ss;
+ }
+ print qq( $s\n);
+}
+
+@combined = (@ps_standard_encoding, @winansi_encoding);
+print "static const char glyph_name_symbol[] = {\n";
+%hash = ();
+$s = qq( "\\0");
+$offset = 1;
+for $sym (@combined) {
+ if (! ($sym eq NULL)) {
+ if (! exists $hash{$sym}) {
+ $hash{$sym} = $offset;
+ $offset += length($sym) + 1;
+ $ss = qq( "$sym\\0");
+ if (length($s) + length($ss) > 78) {
+ print qq( $s\n);
+ $s = "";
+ }
+ $s .= $ss;
+ }
+ }
+}
+print qq( $s\n);
+print "};\n\n";
+
+print "static const int16_t ps_standard_encoding_offset[256] = {\n";
+print_offsets(@ps_standard_encoding);
+print "};\n";
+
+print "static const int16_t winansi_encoding_offset[256] = {\n";
+print_offsets(@winansi_encoding);
+print "};\n";
+
+exit;
+#endif
+
+static const char glyph_name_symbol[] = {
+ "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0"
+ "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0"
+ "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0"
+ "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0"
+ "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0"
+ "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0"
+ "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0"
+ "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0"
+ "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0"
+ "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0"
+ "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0"
+ "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0"
+ "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0"
+ "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0"
+ "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0"
+ "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0"
+ "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0"
+ "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0"
+ "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0"
+ "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0"
+ "oslash\0" "oe\0" "germandbls\0" "Euro\0" "Scaron\0" "Zcaron\0" "trademark\0"
+ "scaron\0" "zcaron\0" "Ydieresis\0" "brokenbar\0" "copyright\0"
+ "logicalnot\0" "registered\0" "degree\0" "plusminus\0" "twosuperior\0"
+ "threesuperior\0" "mu\0" "onesuperior\0" "onequarter\0" "onehalf\0"
+ "threequarters\0" "Agrave\0" "Aacute\0" "Acircumflex\0" "Atilde\0"
+ "Adieresis\0" "Aring\0" "Ccedilla\0" "Egrave\0" "Eacute\0" "Ecircumflex\0"
+ "Edieresis\0" "Igrave\0" "Iacute\0" "Icircumflex\0" "Idieresis\0" "Eth\0"
+ "Ntilde\0" "Ograve\0" "Oacute\0" "Ocircumflex\0" "Otilde\0" "Odieresis\0"
+ "multiply\0" "Ugrave\0" "Uacute\0" "Ucircumflex\0" "Udieresis\0" "Yacute\0"
+ "Thorn\0" "agrave\0" "aacute\0" "acircumflex\0" "atilde\0" "adieresis\0"
+ "aring\0" "ccedilla\0" "egrave\0" "eacute\0" "ecircumflex\0" "edieresis\0"
+ "igrave\0" "iacute\0" "icircumflex\0" "idieresis\0" "eth\0" "ntilde\0"
+ "ograve\0" "oacute\0" "ocircumflex\0" "otilde\0" "odieresis\0" "divide\0"
+ "ugrave\0" "uacute\0" "ucircumflex\0" "udieresis\0" "yacute\0" "thorn\0"
+ "ydieresis\0"
+};
+
+static const int16_t ps_standard_encoding_offset[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
+ 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/,
+ 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
+ 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
+ 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
+ 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
+ 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
+ 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
+ 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
+ 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
+ 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
+ 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/,
+ 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
+ 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
+ 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
+ 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
+ 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/,
+ 474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/,
+ 510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/,
+ 551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/,
+ 586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/,
+ 628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/,
+ 670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0,
+ 706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/,
+ 742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/,
+ 0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/,
+ 813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/,
+ 855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0,
+ 874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/,
+ 900/*germandbls*/, 0, 0, 0, 0,
+};
+
+static const int16_t winansi_encoding_offset[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
+ 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 498/*quotesingle*/,
+ 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
+ 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
+ 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
+ 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
+ 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
+ 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
+ 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
+ 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
+ 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
+ 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 719/*grave*/,
+ 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
+ 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
+ 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
+ 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
+ 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 911/*Euro*/, 0,
+ 628/*quotesinglbase*/, 474/*florin*/, 643/*quotedblbase*/, 685/*ellipsis*/,
+ 579/*dagger*/, 586/*daggerdbl*/, 731/*circumflex*/, 694/*perthousand*/,
+ 916/*Scaron*/, 537/*guilsinglleft*/, 855/*OE*/, 0, 923/*Zcaron*/, 0, 0,
+ 338/*quoteleft*/, 59/*quoteright*/, 510/*quotedblleft*/,
+ 656/*quotedblright*/, 621/*bullet*/, 572/*endash*/, 819/*emdash*/,
+ 742/*tilde*/, 930/*trademark*/, 940/*scaron*/, 551/*guilsinglright*/,
+ 897/*oe*/, 0, 947/*zcaron*/, 954/*Ydieresis*/, 0, 436/*exclamdown*/,
+ 447/*cent*/, 452/*sterling*/, 489/*currency*/, 470/*yen*/, 964/*brokenbar*/,
+ 481/*section*/, 771/*dieresis*/, 974/*copyright*/, 829/*ordfeminine*/,
+ 523/*guillemotleft*/, 984/*logicalnot*/, 0, 995/*registered*/, 748/*macron*/,
+ 1006/*degree*/, 1013/*plusminus*/, 1023/*twosuperior*/,
+ 1035/*threesuperior*/, 725/*acute*/, 1049/*mu*/, 611/*paragraph*/,
+ 596/*periodcentered*/, 785/*cedilla*/, 1052/*onesuperior*/,
+ 858/*ordmasculine*/, 670/*guillemotright*/, 1064/*onequarter*/,
+ 1075/*onehalf*/, 1083/*threequarters*/, 706/*questiondown*/, 1097/*Agrave*/,
+ 1104/*Aacute*/, 1111/*Acircumflex*/, 1123/*Atilde*/, 1130/*Adieresis*/,
+ 1140/*Aring*/, 826/*AE*/, 1146/*Ccedilla*/, 1155/*Egrave*/, 1162/*Eacute*/,
+ 1169/*Ecircumflex*/, 1181/*Edieresis*/, 1191/*Igrave*/, 1198/*Iacute*/,
+ 1205/*Icircumflex*/, 1217/*Idieresis*/, 1227/*Eth*/, 1231/*Ntilde*/,
+ 1238/*Ograve*/, 1245/*Oacute*/, 1252/*Ocircumflex*/, 1264/*Otilde*/,
+ 1271/*Odieresis*/, 1281/*multiply*/, 848/*Oslash*/, 1290/*Ugrave*/,
+ 1297/*Uacute*/, 1304/*Ucircumflex*/, 1316/*Udieresis*/, 1326/*Yacute*/,
+ 1333/*Thorn*/, 900/*germandbls*/, 1339/*agrave*/, 1346/*aacute*/,
+ 1353/*acircumflex*/, 1365/*atilde*/, 1372/*adieresis*/, 1382/*aring*/,
+ 871/*ae*/, 1388/*ccedilla*/, 1397/*egrave*/, 1404/*eacute*/,
+ 1411/*ecircumflex*/, 1423/*edieresis*/, 1433/*igrave*/, 1440/*iacute*/,
+ 1447/*icircumflex*/, 1459/*idieresis*/, 1469/*eth*/, 1473/*ntilde*/,
+ 1480/*ograve*/, 1487/*oacute*/, 1494/*ocircumflex*/, 1506/*otilde*/,
+ 1513/*odieresis*/, 1523/*divide*/, 890/*oslash*/, 1530/*ugrave*/,
+ 1537/*uacute*/, 1544/*ucircumflex*/, 1556/*udieresis*/, 1566/*yacute*/,
+ 1573/*thorn*/, 1579/*ydieresis*/,
+};
+
+const char *
+_cairo_ps_standard_encoding_to_glyphname (int glyph)
+{
+ if (ps_standard_encoding_offset[glyph])
+ return glyph_name_symbol + ps_standard_encoding_offset[glyph];
+ else
+ return NULL;
+}
+
+const char *
+_cairo_winansi_to_glyphname (int glyph)
+{
+ if (winansi_encoding_offset[glyph])
+ return glyph_name_symbol + winansi_encoding_offset[glyph];
+ else
+ return NULL;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type1-private.h b/src/cairo-type1-private.h
new file mode 100755
index 000000000..1630397bc
--- /dev/null
+++ b/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/src/cairo-type1-subset.c b/src/cairo-type1-subset.c
new file mode 100755
index 000000000..8453cbd7d
--- /dev/null
+++ b/src/cairo-type1-subset.c
@@ -0,0 +1,1825 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <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-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-type1-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-output-stream-private.h"
+
+#include <ctype.h>
+#include <locale.h>
+
+#define TYPE1_STACKSIZE 24 /* Defined in Type 1 Font Format */
+
+
+typedef struct {
+ int subset_index;
+ double width;
+ const char *encrypted_charstring;
+ int encrypted_charstring_length;
+} glyph_data_t;
+
+typedef struct _cairo_type1_font_subset {
+ cairo_scaled_font_subset_t *scaled_font_subset;
+
+ struct {
+ unsigned int font_id;
+ char *base_font;
+ unsigned int num_glyphs;
+ double x_min, y_min, x_max, y_max;
+ double ascent, descent;
+ double units_per_em;
+
+ const char *data;
+ unsigned long header_size;
+ unsigned long data_size;
+ unsigned long trailer_size;
+ } base;
+
+ int num_glyphs;
+
+ /* The glyphs and glyph_names arrays are indexed by the order of
+ * the Charstrings in the font. This is not necessarily the same
+ * order as the glyph index. The index_to_glyph_name() font backend
+ * function is used to map the glyph index to the glyph order in
+ * the Charstrings. */
+
+ glyph_data_t *glyphs;
+ char **glyph_names;
+ cairo_array_t glyphs_array;
+ cairo_array_t glyph_names_array;
+
+ int num_subrs;
+ cairo_bool_t subset_subrs;
+ struct {
+ const char *subr_string;
+ int subr_length;
+ const char *np;
+ int np_length;
+ cairo_bool_t used;
+ } *subrs;
+
+ /* Indexed by subset_index this maps to the glyph order in the
+ * glyph_names and glyphs arrays. Has font->num_golyphs
+ * elements. */
+ int *subset_index_to_glyphs;
+
+ cairo_output_stream_t *output;
+ cairo_array_t contents;
+
+ const char *rd, *nd, *np;
+
+ int lenIV;
+
+ char *type1_data;
+ unsigned int type1_length;
+ char *type1_end;
+
+ char *header_segment;
+ 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;
+
+ struct {
+ double stack[TYPE1_STACKSIZE];
+ int sp;
+ } build_stack;
+
+ struct {
+ int stack[TYPE1_STACKSIZE];
+ int sp;
+ } ps_stack;
+
+
+} cairo_type1_font_subset_t;
+
+
+static cairo_status_t
+_cairo_type1_font_subset_init (cairo_type1_font_subset_t *font,
+ cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_bool_t hex_encode)
+{
+ memset (font, 0, sizeof (*font));
+ font->scaled_font_subset = scaled_font_subset;
+
+ _cairo_array_init (&font->glyphs_array, sizeof (glyph_data_t));
+ _cairo_array_init (&font->glyph_names_array, sizeof (char *));
+ font->subset_index_to_glyphs = NULL;
+ font->base.num_glyphs = 0;
+ font->num_subrs = 0;
+ font->subset_subrs = TRUE;
+ font->subrs = NULL;
+
+ font->hex_encode = hex_encode;
+ font->num_glyphs = 0;
+
+ _cairo_array_init (&font->contents, sizeof (char));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph)
+{
+ if (font->glyphs[glyph].subset_index >= 0)
+ return;
+
+ font->glyphs[glyph].subset_index = font->num_glyphs;
+ font->subset_index_to_glyphs[font->num_glyphs] = glyph;
+ font->num_glyphs++;
+}
+
+static cairo_bool_t
+is_ps_delimiter(int c)
+{
+ static const char delimiters[] = "()[]{}<>/% \t\r\n";
+
+ return strchr (delimiters, c) != NULL;
+}
+
+static const char *
+find_token (const char *buffer, const char *end, const char *token)
+{
+ int i, length;
+ /* FIXME: find substring really must be find_token */
+
+ if (buffer == NULL)
+ return NULL;
+
+ length = strlen (token);
+ for (i = 0; buffer + i < end - length + 1; i++)
+ if (memcmp (buffer + i, token, length) == 0)
+ if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) &&
+ (buffer + i == end - length || is_ps_delimiter(buffer[i + length])))
+ return buffer + i;
+
+ return NULL;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font)
+{
+ unsigned char *p;
+ const char *eexec_token;
+ 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_get_matrix (cairo_type1_font_subset_t *font,
+ const char *name,
+ double *a,
+ double *b,
+ double *c,
+ double *d)
+{
+ const char *start, *end, *segment_end;
+ int ret, s_max, i, j;
+ char *s;
+ struct lconv *locale_data;
+ const char *decimal_point;
+ int decimal_point_len;
+
+ locale_data = localeconv ();
+ decimal_point = locale_data->decimal_point;
+ decimal_point_len = strlen (decimal_point);
+
+ assert (decimal_point_len != 0);
+
+ segment_end = font->header_segment + font->header_segment_size;
+ start = find_token (font->header_segment, segment_end, name);
+ if (start == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ end = find_token (start, segment_end, "def");
+ if (end == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ s_max = end - start + 5*decimal_point_len + 1;
+ s = malloc (s_max);
+ if (unlikely (s == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ i = 0;
+ j = 0;
+ while (i < end - start && j < s_max - decimal_point_len) {
+ if (start[i] == '.') {
+ strncpy(s + j, decimal_point, decimal_point_len);
+ i++;
+ j += decimal_point_len;
+ } else {
+ s[j++] = start[i++];
+ }
+ }
+ s[j] = 0;
+
+ start = strpbrk (s, "{[");
+ if (!start) {
+ free (s);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ start++;
+ ret = 0;
+ if (*start)
+ ret = sscanf(start, "%lf %lf %lf %lf", a, b, c, d);
+
+ free (s);
+
+ if (ret != 4)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_get_bbox (cairo_type1_font_subset_t *font)
+{
+ cairo_status_t status;
+ double x_min, y_min, x_max, y_max;
+ double xx, yx, xy, yy;
+
+ status = cairo_type1_font_subset_get_matrix (font, "/FontBBox",
+ &x_min,
+ &y_min,
+ &x_max,
+ &y_max);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_subset_get_matrix (font, "/FontMatrix",
+ &xx, &yx, &xy, &yy);
+ if (unlikely (status))
+ return status;
+
+ if (yy == 0.0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Freetype uses 1/yy to get units per EM */
+ font->base.units_per_em = 1.0/yy;
+
+ font->base.x_min = x_min / font->base.units_per_em;
+ font->base.y_min = y_min / font->base.units_per_em;
+ font->base.x_max = x_max / font->base.units_per_em;
+ font->base.y_max = y_max / font->base.units_per_em;
+ font->base.ascent = font->base.y_max;
+ font->base.descent = font->base.y_min;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font)
+{
+ const char *start, *end, *segment_end;
+ char *s;
+ int i;
+
+ segment_end = font->header_segment + font->header_segment_size;
+ start = find_token (font->header_segment, segment_end, "/FontName");
+ if (start == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ start += strlen ("/FontName");
+
+ end = find_token (start, segment_end, "def");
+ if (end == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ s = malloc (end - start + 1);
+ if (unlikely (s == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ strncpy (s, start, end - start);
+ s[end - start] = 0;
+
+ start = strchr (s, '/');
+ if (!start++ || !start) {
+ free (s);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* If font name is prefixed with a subset tag, strip it off. */
+ if (strlen(start) > 7 && start[6] == '+') {
+ for (i = 0; i < 6; i++) {
+ if (start[i] < 'A' || start[i] > 'Z')
+ break;
+ }
+ if (i == 6)
+ start += 7;
+ }
+
+ font->base.base_font = strdup (start);
+ free (s);
+ if (unlikely (font->base.base_font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ s = font->base.base_font;
+ while (*s && !is_ps_delimiter(*s))
+ s++;
+
+ *s = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font,
+ const char *name)
+{
+ const char *start, *end, *segment_end;
+ unsigned int i;
+
+ /* FIXME:
+ * This function assumes that /FontName always appears
+ * before /Encoding. This appears to always be the case with Type1
+ * fonts.
+ *
+ * The more recently added code for removing the UniqueID and XUID
+ * keys can not make any assumptions about the position of the
+ * keys in the dictionary so it is implemented by overwriting the
+ * key definition with spaces before we start copying the font to
+ * the output.
+ *
+ * This code should be rewritten to not make any assumptions about
+ * the order of dictionary keys. This will allow UniqueID to be
+ * stripped out instead of leaving a bunch of spaces in the
+ * output.
+ */
+ cairo_type1_font_erase_dict_key (font, "/UniqueID");
+ cairo_type1_font_erase_dict_key (font, "/XUID");
+
+ segment_end = font->header_segment + font->header_segment_size;
+
+ /* Type 1 fonts created by Fontforge have some PostScript code at
+ * the start of the font that skips the font if the printer has a
+ * cached copy of the font with the same unique id. This breaks
+ * our subsetted font so we disable it by searching for the
+ * PostScript operator "known" when used to check for the
+ * "/UniqueID" dictionary key. We append " pop false " after it to
+ * pop the result of this check off the stack and replace it with
+ * "false" to make the PostScript code think "/UniqueID" does not
+ * exist.
+ */
+ end = font->header_segment;
+ start = find_token (font->header_segment, segment_end, "/UniqueID");
+ if (start) {
+ start += 9;
+ while (start < segment_end && _cairo_isspace (*start))
+ start++;
+ if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) {
+ _cairo_output_stream_write (font->output, font->header_segment,
+ start + 5 - font->header_segment);
+ _cairo_output_stream_printf (font->output, " pop false ");
+ end = start + 5;
+ }
+ }
+
+ start = find_token (end, segment_end, "/FontName");
+ if (start == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_output_stream_write (font->output, end,
+ start - end);
+
+ _cairo_output_stream_printf (font->output, "/FontName /%s def", name);
+
+ end = find_token (start, segment_end, "def");
+ if (end == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ end += 3;
+
+ start = find_token (end, segment_end, "/Encoding");
+ if (start == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ _cairo_output_stream_write (font->output, end, start - end);
+
+ _cairo_output_stream_printf (font->output,
+ "/Encoding 256 array\n"
+ "0 1 255 {1 index exch /.notdef put} for\n");
+ if (font->scaled_font_subset->is_latin) {
+ for (i = 1; i < 256; i++) {
+ int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i];
+
+ if (subset_glyph > 0) {
+ _cairo_output_stream_printf (font->output,
+ "dup %d /%s put\n",
+ i,
+ _cairo_winansi_to_glyphname (i));
+ }
+ }
+ } else {
+ for (i = 0; i < font->base.num_glyphs; i++) {
+ if (font->glyphs[i].subset_index <= 0)
+ continue;
+ _cairo_output_stream_printf (font->output,
+ "dup %d /%s put\n",
+ font->glyphs[i].subset_index,
+ font->glyph_names[i]);
+ }
+ }
+ _cairo_output_stream_printf (font->output, "readonly def");
+
+ end = find_token (start, segment_end, "def");
+ if (end == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ end += 3;
+
+ /* There are some buggy fonts that contain more than one /Encoding */
+ if (find_token (end, segment_end, "/Encoding"))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_output_stream_write (font->output, end, segment_end - end);
+
+ return font->output->status;
+}
+
+static int
+hex_to_int (int ch)
+{
+ if (ch <= '9')
+ return ch - '0';
+ else if (ch <= 'F')
+ return ch - 'A' + 10;
+ else
+ return ch - 'a' + 10;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font,
+ const char *data, unsigned int length)
+{
+ const unsigned char *in, *end;
+ int c, p;
+ static const char hex_digits[16] = "0123456789abcdef";
+ char digits[3];
+
+ in = (const unsigned char *) data;
+ end = (const unsigned char *) data + length;
+ while (in < end) {
+ p = *in++;
+ c = p ^ (font->eexec_key >> 8);
+ font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+
+ if (font->hex_encode) {
+ digits[0] = hex_digits[c >> 4];
+ digits[1] = hex_digits[c & 0x0f];
+ digits[2] = '\n';
+ font->hex_column += 2;
+
+ if (font->hex_column == 78) {
+ _cairo_output_stream_write (font->output, digits, 3);
+ font->hex_column = 0;
+ } else {
+ _cairo_output_stream_write (font->output, digits, 2);
+ }
+ } else {
+ digits[0] = c;
+ _cairo_output_stream_write (font->output, digits, 1);
+ }
+ }
+
+ return font->output->status;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font)
+{
+ unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY;
+ unsigned char *in, *end;
+ char *out;
+ int c, p;
+ int i;
+
+ in = (unsigned char *) font->eexec_segment;
+ end = (unsigned char *) in + font->eexec_segment_size;
+
+ font->cleartext = malloc (font->eexec_segment_size + 1);
+ if (unlikely (font->cleartext == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ out = font->cleartext;
+ while (in < end) {
+ if (font->eexec_segment_is_ascii) {
+ c = *in++;
+ if (_cairo_isspace (c))
+ continue;
+ c = (hex_to_int (c) << 4) | hex_to_int (*in++);
+ } else {
+ c = *in++;
+ }
+ p = c ^ (r >> 8);
+ r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+
+ *out++ = p;
+ }
+ font->cleartext_end = out;
+
+ /* Overwrite random bytes with spaces.
+ *
+ * The first 4 bytes of the cleartext are the random bytes
+ * required by the encryption algorithm. When encrypting the
+ * cleartext, the first ciphertext byte must not be a white space
+ * character and the first 4 bytes must not be an ASCII Hex
+ * character. Some fonts do not check that their randomly chosen
+ * bytes results in ciphertext that complies with this
+ * restriction. This may cause problems for some PDF consumers. By
+ * replacing the random bytes with spaces, the first four bytes of
+ * ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies
+ * with this restriction. Using spaces also means we don't have to
+ * skip over the random bytes when parsing the cleartext.
+ */
+ for (i = 0; i < 4 && i < font->eexec_segment_size; i++)
+ font->cleartext[i] = ' ';
+
+ /* Ensure strtol() can not scan past the end of the cleartext */
+ font->cleartext[font->eexec_segment_size] = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const char *
+skip_token (const char *p, const char *end)
+{
+ while (p < end && _cairo_isspace(*p))
+ p++;
+
+ while (p < end && !_cairo_isspace(*p))
+ p++;
+
+ if (p == end)
+ return NULL;
+
+ return p;
+}
+
+static void
+cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out)
+{
+ unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY;
+ int c, p, i;
+
+ for (i = 0; i < size; i++) {
+ c = *in++;
+ p = c ^ (r >> 8);
+ r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+ *out++ = p;
+ }
+}
+
+static const unsigned char *
+cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer)
+{
+ if (*p <= 246) {
+ *integer = *p++ - 139;
+ } else if (*p <= 250) {
+ *integer = (p[0] - 247) * 256 + p[1] + 108;
+ p += 2;
+ } else if (*p <= 254) {
+ *integer = -(p[0] - 251) * 256 - p[1] - 108;
+ p += 2;
+ } else {
+ *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
+ p += 5;
+ }
+
+ return p;
+}
+
+static cairo_status_t
+use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index)
+{
+ const char *glyph_name;
+ unsigned int i;
+
+ if (index < 0 || index > 255)
+ return CAIRO_STATUS_SUCCESS;
+
+ glyph_name = _cairo_ps_standard_encoding_to_glyphname (index);
+ if (glyph_name == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ for (i = 0; i < font->base.num_glyphs; i++) {
+ if (font->glyph_names[i] && strcmp (font->glyph_names[i], glyph_name) == 0) {
+ cairo_type1_font_subset_use_glyph (font, i);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+
+#define TYPE1_CHARSTRING_COMMAND_HSTEM 0x01
+#define TYPE1_CHARSTRING_COMMAND_VSTEM 0x03
+#define TYPE1_CHARSTRING_COMMAND_VMOVETO 0x04
+#define TYPE1_CHARSTRING_COMMAND_RLINETO 0x05
+#define TYPE1_CHARSTRING_COMMAND_HLINETO 0x06
+#define TYPE1_CHARSTRING_COMMAND_VLINETO 0x07
+#define TYPE1_CHARSTRING_COMMAND_RRCURVETO 0x08
+#define TYPE1_CHARSTRING_COMMAND_CLOSEPATH 0x09
+#define TYPE1_CHARSTRING_COMMAND_CALLSUBR 0x0a
+#define TYPE1_CHARSTRING_COMMAND_RETURN 0x0b
+#define TYPE1_CHARSTRING_COMMAND_ESCAPE 0x0c
+#define TYPE1_CHARSTRING_COMMAND_HSBW 0x0d
+#define TYPE1_CHARSTRING_COMMAND_ENDCHAR 0x0e
+#define TYPE1_CHARSTRING_COMMAND_RMOVETO 0x15
+#define TYPE1_CHARSTRING_COMMAND_HMOVETO 0x16
+#define TYPE1_CHARSTRING_COMMAND_VHCURVETO 0x1e
+#define TYPE1_CHARSTRING_COMMAND_HVCURVETO 0x1f
+#define TYPE1_CHARSTRING_COMMAND_DOTSECTION 0x0c00
+#define TYPE1_CHARSTRING_COMMAND_VSTEM3 0x0c01
+#define TYPE1_CHARSTRING_COMMAND_HSTEM3 0x0c02
+#define TYPE1_CHARSTRING_COMMAND_SEAC 0x0c06
+#define TYPE1_CHARSTRING_COMMAND_SBW 0x0c07
+#define TYPE1_CHARSTRING_COMMAND_DIV 0x0c0c
+#define TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR 0x0c10
+#define TYPE1_CHARSTRING_COMMAND_POP 0x0c11
+#define TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT 0x0c21
+
+/* Parse the charstring, including recursing into subroutines. Find
+ * the glyph width, subroutines called, and glyphs required by the
+ * SEAC operator. */
+static cairo_status_t
+cairo_type1_font_subset_parse_charstring (cairo_type1_font_subset_t *font,
+ int glyph,
+ const char *encrypted_charstring,
+ int encrypted_charstring_length)
+{
+ cairo_status_t status;
+ unsigned char *charstring;
+ const unsigned char *end;
+ const unsigned char *p;
+ int command;
+
+ charstring = malloc (encrypted_charstring_length);
+ if (unlikely (charstring == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ cairo_type1_font_subset_decrypt_charstring ((const unsigned char *)
+ encrypted_charstring,
+ encrypted_charstring_length,
+ charstring);
+ end = charstring + encrypted_charstring_length;
+ p = charstring + font->lenIV;
+ status = CAIRO_STATUS_SUCCESS;
+ while (p < end) {
+ if (*p < 32) {
+ command = *p++;
+ switch (command) {
+ case TYPE1_CHARSTRING_COMMAND_HSTEM:
+ case TYPE1_CHARSTRING_COMMAND_VSTEM:
+ case TYPE1_CHARSTRING_COMMAND_VMOVETO:
+ case TYPE1_CHARSTRING_COMMAND_RLINETO:
+ case TYPE1_CHARSTRING_COMMAND_HLINETO:
+ case TYPE1_CHARSTRING_COMMAND_VLINETO:
+ case TYPE1_CHARSTRING_COMMAND_RRCURVETO:
+ case TYPE1_CHARSTRING_COMMAND_CLOSEPATH:
+ case TYPE1_CHARSTRING_COMMAND_RMOVETO:
+ case TYPE1_CHARSTRING_COMMAND_HMOVETO:
+ case TYPE1_CHARSTRING_COMMAND_VHCURVETO:
+ case TYPE1_CHARSTRING_COMMAND_HVCURVETO:
+ case TYPE1_CHARSTRING_COMMAND_RETURN:
+ case TYPE1_CHARSTRING_COMMAND_ENDCHAR:
+ default:
+ /* stack clearing operator */
+ font->build_stack.sp = 0;
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_CALLSUBR:
+ if (font->subset_subrs && font->build_stack.sp > 0) {
+ double int_val;
+ if (modf(font->build_stack.stack[--font->build_stack.sp], &int_val) == 0.0) {
+ int subr_num = int_val;
+ if (subr_num >= 0 && subr_num < font->num_subrs) {
+ font->subrs[subr_num].used = TRUE;
+ status = cairo_type1_font_subset_parse_charstring (
+ font,
+ glyph,
+ font->subrs[subr_num].subr_string,
+ font->subrs[subr_num].subr_length);
+ break;
+ }
+ }
+ }
+ font->subset_subrs = FALSE;
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_HSBW:
+ if (font->build_stack.sp < 2) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ font->glyphs[glyph].width = font->build_stack.stack[1]/font->base.units_per_em;
+ font->build_stack.sp = 0;
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_ESCAPE:
+ command = command << 8 | *p++;
+ switch (command) {
+ case TYPE1_CHARSTRING_COMMAND_DOTSECTION:
+ case TYPE1_CHARSTRING_COMMAND_VSTEM3:
+ case TYPE1_CHARSTRING_COMMAND_HSTEM3:
+ case TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT:
+ default:
+ /* stack clearing operator */
+ font->build_stack.sp = 0;
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_SEAC:
+ /* The seac command takes five integer arguments. The
+ * last two are glyph indices into the PS standard
+ * encoding give the names of the glyphs that this
+ * glyph is composed from. All we need to do is to
+ * make sure those glyphs are present in the subset
+ * under their standard names. */
+ if (font->build_stack.sp < 5) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ status = use_standard_encoding_glyph (font, font->build_stack.stack[3]);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = use_standard_encoding_glyph (font, font->build_stack.stack[4]);
+ if (unlikely (status))
+ goto cleanup;
+
+ font->build_stack.sp = 0;
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_SBW:
+ if (font->build_stack.sp < 4) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ font->glyphs[glyph].width = font->build_stack.stack[2]/font->base.units_per_em;
+ font->build_stack.sp = 0;
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_DIV:
+ if (font->build_stack.sp < 2) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ } else {
+ double num1 = font->build_stack.stack[font->build_stack.sp - 2];
+ double num2 = font->build_stack.stack[font->build_stack.sp - 1];
+ font->build_stack.sp--;
+ if (num2 == 0.0) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+ font->build_stack.stack[font->build_stack.sp - 1] = num1/num2;
+ }
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR:
+ if (font->build_stack.sp < 1) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ font->build_stack.sp--;
+ font->ps_stack.sp = 0;
+ while (font->build_stack.sp)
+ font->ps_stack.stack[font->ps_stack.sp++] = font->build_stack.stack[--font->build_stack.sp];
+
+ break;
+
+ case TYPE1_CHARSTRING_COMMAND_POP:
+ if (font->ps_stack.sp < 1) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ if (font->ps_stack.sp >= TYPE1_STACKSIZE) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ /* T1 spec states that if the interpreter does not
+ * support executing the callothersub, the results
+ * must be taken from the callothersub arguments. */
+ font->build_stack.stack[font->build_stack.sp++] = font->ps_stack.stack[--font->ps_stack.sp];
+ break;
+ }
+ break;
+ }
+ } else {
+ /* integer argument */
+ if (font->build_stack.sp < TYPE1_STACKSIZE) {
+ int val;
+ p = cairo_type1_font_subset_decode_integer (p, &val);
+ font->build_stack.stack[font->build_stack.sp++] = val;
+ } else {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ free (charstring);
+
+ return status;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_build_subr_list (cairo_type1_font_subset_t *font,
+ int subr_number,
+ const char *encrypted_charstring, int encrypted_charstring_length,
+ const char *np, int np_length)
+{
+
+ font->subrs[subr_number].subr_string = encrypted_charstring;
+ font->subrs[subr_number].subr_length = encrypted_charstring_length;
+ font->subrs[subr_number].np = np;
+ font->subrs[subr_number].np_length = np_length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+write_used_subrs (cairo_type1_font_subset_t *font,
+ int subr_number,
+ const char *subr_string, int subr_string_length,
+ const char *np, int np_length)
+{
+ cairo_status_t status;
+ char buffer[256];
+ int length;
+
+ if (!font->subrs[subr_number].used)
+ return CAIRO_STATUS_SUCCESS;
+
+ length = snprintf (buffer, sizeof buffer,
+ "dup %d %d %s ",
+ subr_number, subr_string_length, font->rd);
+ status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_subset_write_encrypted (font,
+ subr_string,
+ subr_string_length);
+ if (unlikely (status))
+ return status;
+
+ if (np) {
+ status = cairo_type1_font_subset_write_encrypted (font, np, np_length);
+ } else {
+ length = snprintf (buffer, sizeof buffer, "%s\n", font->np);
+ status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+ }
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_status_t (*subr_func_t) (cairo_type1_font_subset_t *font,
+ int subr_number,
+ const char *subr_string, int subr_string_length,
+ const char *np, int np_length);
+
+static cairo_status_t
+cairo_type1_font_for_each_subr (cairo_type1_font_subset_t *font,
+ const char *array_start,
+ const char *cleartext_end,
+ subr_func_t func,
+ const char **array_end)
+{
+ const char *p, *subr_string;
+ char *end;
+ int subr_num, subr_length;
+ const char *np;
+ int np_length;
+ cairo_status_t status;
+
+ /* We're looking at "dup" at the start of the first subroutine. The subroutines
+ * definitions are on the form:
+ *
+ * dup 5 23 RD <23 binary bytes> NP
+ *
+ * or alternatively using -| and |- instead of RD and ND.
+ * The first number is the subroutine number.
+ */
+
+ p = array_start;
+ while (p + 3 < cleartext_end && strncmp (p, "dup", 3) == 0) {
+ p = skip_token (p, cleartext_end);
+
+ /* get subr number */
+ subr_num = strtol (p, &end, 10);
+ if (p == end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (subr_num < 0 || subr_num >= font->num_subrs)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* get subr length */
+ p = end;
+ subr_length = strtol (p, &end, 10);
+ if (p == end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Skip past -| or RD to binary data. There is exactly one space
+ * between the -| or RD token and the encrypted data, thus '+ 1'. */
+ subr_string = skip_token (end, cleartext_end) + 1;
+
+ np = NULL;
+ np_length = 0;
+
+ /* Skip binary data and | or NP token. */
+ p = skip_token (subr_string + subr_length, cleartext_end);
+ while (p < cleartext_end && _cairo_isspace(*p))
+ p++;
+
+ /* Some fonts have "noaccess put" instead of "NP" */
+ if (p + 3 < cleartext_end && strncmp (p, "put", 3) == 0) {
+ p = skip_token (p, cleartext_end);
+ while (p < cleartext_end && _cairo_isspace(*p))
+ p++;
+
+ np = subr_string + subr_length;
+ np_length = p - np;
+ }
+
+ status = func (font, subr_num,
+ subr_string, subr_length, np, np_length);
+ if (unlikely (status))
+ return status;
+
+ }
+
+ *array_end = (char *) p;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_build_glyph_list (cairo_type1_font_subset_t *font,
+ int glyph_number,
+ const char *name, int name_length,
+ const char *encrypted_charstring, int encrypted_charstring_length)
+{
+ char *s;
+ glyph_data_t glyph;
+ cairo_status_t status;
+
+ s = malloc (name_length + 1);
+ if (unlikely (s == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ strncpy (s, name, name_length);
+ s[name_length] = 0;
+
+ status = _cairo_array_append (&font->glyph_names_array, &s);
+ if (unlikely (status))
+ return status;
+
+ glyph.subset_index = -1;
+ glyph.width = 0;
+ glyph.encrypted_charstring = encrypted_charstring;
+ glyph.encrypted_charstring_length = encrypted_charstring_length;
+ status = _cairo_array_append (&font->glyphs_array, &glyph);
+
+ return status;
+}
+
+static cairo_status_t
+write_used_glyphs (cairo_type1_font_subset_t *font,
+ int glyph_number,
+ const char *name, int name_length,
+ const char *charstring, int charstring_length)
+{
+ cairo_status_t status;
+ char buffer[256];
+ int length;
+ int subset_id;
+ int ch;
+
+ if (font->glyphs[glyph_number].subset_index < 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (font->scaled_font_subset->is_latin) {
+ /* When using the WinAnsi encoding in PDF, the /Encoding array
+ * is ignored and instead glyphs are keyed by glyph names. To
+ * ensure correct rendering we replace the glyph name in the
+ * font with the standard name.
+ **/
+ subset_id = font->glyphs[glyph_number].subset_index;
+ if (subset_id > 0) {
+ ch = font->scaled_font_subset->to_latin_char[subset_id];
+ name = _cairo_winansi_to_glyphname (ch);
+ if (name == NULL)
+ return CAIRO_STATUS_NULL_POINTER;
+ name_length = strlen(name);
+ }
+ }
+
+ length = snprintf (buffer, sizeof buffer,
+ "/%.*s %d %s ",
+ name_length, name, charstring_length, font->rd);
+ status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_subset_write_encrypted (font,
+ charstring,
+ charstring_length);
+ if (unlikely (status))
+ return status;
+
+ length = snprintf (buffer, sizeof buffer, "%s\n", font->nd);
+ status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font,
+ int glyph_number,
+ const char *name, int name_length,
+ const char *charstring, int charstring_length);
+
+static cairo_status_t
+cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font,
+ const char *dict_start,
+ const char *dict_end,
+ glyph_func_t func,
+ const char **dict_out)
+{
+ int charstring_length, name_length;
+ const char *p, *charstring, *name;
+ char *end;
+ cairo_status_t status;
+ int glyph_count;
+
+ /* We're looking at '/' in the name of the first glyph. The glyph
+ * definitions are on the form:
+ *
+ * /name 23 RD <23 binary bytes> ND
+ *
+ * or alternatively using -| and |- instead of RD and ND.
+ *
+ * We parse the glyph name and see if it is in the subset. If it
+ * is, we call the specified callback with the glyph name and
+ * glyph data, otherwise we just skip it. We need to parse
+ * through a glyph definition; we can't just find the next '/',
+ * since the binary data could contain a '/'.
+ */
+
+ p = dict_start;
+ glyph_count = 0;
+ while (*p == '/') {
+ name = p + 1;
+ p = skip_token (p, dict_end);
+ name_length = p - name;
+
+ charstring_length = strtol (p, &end, 10);
+ if (p == end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Skip past -| or RD to binary data. There is exactly one space
+ * between the -| or RD token and the encrypted data, thus '+ 1'. */
+ charstring = skip_token (end, dict_end) + 1;
+
+ /* Skip binary data and |- or ND token. */
+ p = skip_token (charstring + charstring_length, dict_end);
+ while (p < dict_end && _cairo_isspace(*p))
+ p++;
+
+ /* In case any of the skip_token() calls above reached EOF, p will
+ * be equal to dict_end. */
+ if (p == dict_end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = func (font, glyph_count++,
+ name, name_length,
+ charstring, charstring_length);
+ if (unlikely (status))
+ return status;
+ }
+
+ *dict_out = p;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font,
+ const char *name)
+{
+ cairo_status_t status;
+ const char *p, *subrs, *charstrings, *array_start, *array_end, *dict_start, *dict_end;
+ const char *lenIV_start, *lenIV_end, *closefile_token;
+ char buffer[32], *lenIV_str, *subr_count_end, *glyph_count_end;
+ int ret, lenIV, length;
+ const cairo_scaled_font_backend_t *backend;
+ unsigned int i;
+ int glyph, j;
+
+ /* The private dict holds hint information, common subroutines and
+ * the actual glyph definitions (charstrings).
+ *
+ * What we do here is scan directly to the /Subrs token, which
+ * marks the beginning of the subroutines. We then read in all the
+ * subroutines then move on to the /CharString token, which marks
+ * the beginning of the glyph definitions, and read in the chastrings.
+ *
+ * The charstrings are parsed to extracts glyph widths, work out
+ * which subroutines are called, and too see if any extra glyphs
+ * need to be included due to the use of the seac glyph combining
+ * operator.
+ *
+ * Finally the private dict is copied to the subset font minus the
+ * subroutines and charstrings not required.
+ */
+
+ /* Determine lenIV, the number of random characters at the start of
+ each encrypted charstring. The defaults is 4, but this can be
+ overridden in the private dict. */
+ font->lenIV = 4;
+ if ((lenIV_start = find_token (font->cleartext, font->cleartext_end, "/lenIV")) != NULL) {
+ lenIV_start += 6;
+ lenIV_end = find_token (lenIV_start, font->cleartext_end, "def");
+ if (lenIV_end == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ lenIV_str = malloc (lenIV_end - lenIV_start + 1);
+ if (unlikely (lenIV_str == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ strncpy (lenIV_str, lenIV_start, lenIV_end - lenIV_start);
+ lenIV_str[lenIV_end - lenIV_start] = 0;
+
+ ret = sscanf(lenIV_str, "%d", &lenIV);
+ free(lenIV_str);
+
+ if (unlikely (ret <= 0))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Apparently some fonts signal unencrypted charstrings with a negative lenIV,
+ though this is not part of the Type 1 Font Format specification. See, e.g.
+ http://lists.gnu.org/archive/html/freetype-devel/2000-06/msg00064.html. */
+ if (unlikely (lenIV < 0))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ font->lenIV = lenIV;
+ }
+
+ /* Find start of Subrs */
+ subrs = find_token (font->cleartext, font->cleartext_end, "/Subrs");
+ if (subrs == NULL) {
+ font->subset_subrs = FALSE;
+ p = font->cleartext;
+ goto skip_subrs;
+ }
+
+ /* Scan past /Subrs and get the array size. */
+ p = subrs + strlen ("/Subrs");
+ font->num_subrs = strtol (p, &subr_count_end, 10);
+ if (subr_count_end == p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (font->num_subrs <= 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ font->subrs = calloc (font->num_subrs, sizeof (font->subrs[0]));
+ if (unlikely (font->subrs == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* look for "dup" which marks the beginning of the first subr */
+ array_start = find_token (subr_count_end, font->cleartext_end, "dup");
+
+ /* Read in the subroutines */
+ status = cairo_type1_font_for_each_subr (font,
+ array_start,
+ font->cleartext_end,
+ cairo_type1_font_subset_build_subr_list,
+ &array_end);
+ if (unlikely(status))
+ return status;
+
+ p = array_end;
+skip_subrs:
+
+ /* Find start of CharStrings */
+ charstrings = find_token (p, font->cleartext_end, "/CharStrings");
+ if (charstrings == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Scan past /CharStrings and the integer following it. */
+ p = charstrings + strlen ("/CharStrings");
+ strtol (p, &glyph_count_end, 10);
+ if (p == glyph_count_end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Look for a '/' which marks the beginning of the first glyph
+ * definition. */
+ for (p = glyph_count_end; p < font->cleartext_end; p++)
+ if (*p == '/')
+ break;
+ if (p == font->cleartext_end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ dict_start = p;
+
+ /* Now that we have the private dictionary broken down in
+ * sections, do the first pass through the glyph definitions to
+ * build a list of glyph names and charstrings. */
+ status = cairo_type1_font_subset_for_each_glyph (font,
+ dict_start,
+ font->cleartext_end,
+ cairo_type1_font_subset_build_glyph_list,
+ &dict_end);
+ if (unlikely(status))
+ return status;
+
+ font->glyphs = _cairo_array_index (&font->glyphs_array, 0);
+ if (font->glyphs == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ font->glyph_names = _cairo_array_index (&font->glyph_names_array, 0);
+ if (font->glyph_names == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ font->base.num_glyphs = _cairo_array_num_elements (&font->glyphs_array);
+ font->subset_index_to_glyphs = calloc (font->base.num_glyphs, sizeof font->subset_index_to_glyphs[0]);
+ if (unlikely (font->subset_index_to_glyphs == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ backend = font->scaled_font_subset->scaled_font->backend;
+ if (!backend->index_to_glyph_name)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Find the glyph number corresponding to each glyph in the subset
+ * and mark it as in use */
+
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ unsigned long index;
+
+ status = backend->index_to_glyph_name (font->scaled_font_subset->scaled_font,
+ font->glyph_names,
+ font->base.num_glyphs,
+ font->scaled_font_subset->glyphs[i],
+ &index);
+ if (unlikely(status))
+ return status;
+
+ cairo_type1_font_subset_use_glyph (font, index);
+ }
+
+ /* Go through the charstring of each glyph in use, get the glyph
+ * width and figure out which extra glyphs may be required by the
+ * seac operator (which may cause font->num_glyphs to increase
+ * while this loop is executing). Also subset the Subrs. */
+ for (j = 0; j < font->num_glyphs; j++) {
+ glyph = font->subset_index_to_glyphs[j];
+ font->build_stack.sp = 0;
+ font->ps_stack.sp = 0;
+ status = cairo_type1_font_subset_parse_charstring (font,
+ glyph,
+ font->glyphs[glyph].encrypted_charstring,
+ font->glyphs[glyph].encrypted_charstring_length);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Always include the first five subroutines in case the Flex/hint mechanism is
+ * being used. */
+ for (j = 0; j < MIN (font->num_subrs, 5); j++) {
+ font->subrs[j].used = TRUE;
+ }
+
+ closefile_token = find_token (dict_end, font->cleartext_end, "closefile");
+ if (closefile_token == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* We're ready to start outputting. First write the header,
+ * i.e. the public part of the font dict.*/
+ status = cairo_type1_font_subset_write_header (font, name);
+ if (unlikely (status))
+ return status;
+
+ font->base.header_size = _cairo_output_stream_get_position (font->output);
+
+ /* Start outputting the private dict */
+ if (font->subset_subrs) {
+ /* First output everything up to the start of the Subrs array. */
+ status = cairo_type1_font_subset_write_encrypted (font, font->cleartext,
+ array_start - font->cleartext);
+ if (unlikely (status))
+ return status;
+
+ /* Write out the subr definitions for each of the glyphs in
+ * the subset. */
+ status = cairo_type1_font_for_each_subr (font,
+ array_start,
+ font->cleartext_end,
+ write_used_subrs,
+ &p);
+ if (unlikely (status))
+ return status;
+ } else {
+ p = font->cleartext;
+ }
+
+ /* If subr subsetting, output everything from end of subrs to
+ * start of /CharStrings token. If not subr subsetting, output
+ * everything start of private dict to start of /CharStrings
+ * token. */
+ status = cairo_type1_font_subset_write_encrypted (font, p, charstrings - p);
+ if (unlikely (status))
+ return status;
+
+ /* Write out new charstring count */
+ length = snprintf (buffer, sizeof buffer,
+ "/CharStrings %d", font->num_glyphs);
+ status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+ if (unlikely (status))
+ return status;
+
+ /* Write out text between the charstring count and the first
+ * charstring definition */
+ status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end,
+ dict_start - glyph_count_end);
+ if (unlikely (status))
+ return status;
+
+ /* Write out the charstring definitions for each of the glyphs in
+ * the subset. */
+ status = cairo_type1_font_subset_for_each_glyph (font,
+ dict_start,
+ font->cleartext_end,
+ write_used_glyphs,
+ &p);
+ if (unlikely (status))
+ return status;
+
+ /* Output what's left between the end of the glyph definitions and
+ * the end of the private dict to the output. */
+ status = cairo_type1_font_subset_write_encrypted (font, p,
+ closefile_token - p + strlen ("closefile") + 1);
+ if (unlikely (status))
+ return status;
+
+ if (font->hex_encode)
+ _cairo_output_stream_write (font->output, "\n", 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font)
+{
+ const char *cleartomark_token;
+ int i;
+ static const char zeros[65] =
+ "0000000000000000000000000000000000000000000000000000000000000000\n";
+
+
+ for (i = 0; i < 8; i++)
+ _cairo_output_stream_write (font->output, zeros, sizeof zeros);
+
+ cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark");
+ if (cleartomark_token) {
+ /* Some fonts have conditional save/restore around the entire
+ * font dict, so we need to retain whatever postscript code
+ * that may come after 'cleartomark'. */
+
+ _cairo_output_stream_write (font->output, cleartomark_token,
+ font->type1_end - cleartomark_token);
+ if (*(font->type1_end - 1) != '\n')
+ _cairo_output_stream_printf (font->output, "\n");
+
+ } else if (!font->eexec_segment_is_ascii) {
+ /* Fonts embedded in PDF may omit the fixed-content portion
+ * that includes the 'cleartomark' operator. Type 1 in PDF is
+ * always binary. */
+
+ _cairo_output_stream_printf (font->output, "cleartomark\n");
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* some fonts do not have a newline at the end of the last line */
+ _cairo_output_stream_printf (font->output, "\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+type1_font_write (void *closure, const unsigned char *data, unsigned int length)
+{
+ cairo_type1_font_subset_t *font = closure;
+
+ return _cairo_array_append_multiple (&font->contents, data, length);
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write (cairo_type1_font_subset_t *font,
+ const char *name)
+{
+ cairo_status_t status;
+
+ status = cairo_type1_font_subset_find_segments (font);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_subset_decrypt_eexec_segment (font);
+ if (unlikely (status))
+ return status;
+
+ /* Determine which glyph definition delimiters to use. */
+ if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) {
+ font->rd = "-|";
+ font->nd = "|-";
+ font->np = "|";
+ } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) {
+ font->rd = "RD";
+ font->nd = "ND";
+ font->np = "NP";
+ } else {
+ /* Don't know *what* kind of font this is... */
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
+ font->hex_column = 0;
+
+ status = cairo_type1_font_subset_get_bbox (font);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_subset_get_fontname (font);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_subset_write_private_dict (font, name);
+ if (unlikely (status))
+ return status;
+
+ font->base.data_size = _cairo_output_stream_get_position (font->output) -
+ font->base.header_size;
+
+ status = cairo_type1_font_subset_write_trailer (font);
+ if (unlikely (status))
+ return status;
+
+ font->base.trailer_size =
+ _cairo_output_stream_get_position (font->output) -
+ font->base.header_size - font->base.data_size;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+check_fontdata_is_type1 (const unsigned char *data, long length)
+{
+ /* Test for Type 1 Binary (PFB) */
+ if (length > 2 && data[0] == 0x80 && data[1] == 0x01)
+ return TRUE;
+
+ /* Test for Type 1 1 ASCII (PFA) */
+ if (length > 2 && data[0] == '%' && data[1] == '!')
+ return TRUE;
+
+ return FALSE;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_generate (void *abstract_font,
+ const char *name)
+
+{
+ cairo_type1_font_subset_t *font = abstract_font;
+ cairo_scaled_font_t *scaled_font;
+ cairo_status_t status;
+ unsigned long data_length;
+
+ scaled_font = font->scaled_font_subset->scaled_font;
+ if (!scaled_font->backend->load_type1_data)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &data_length);
+ if (status)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ font->type1_length = data_length;
+ font->type1_data = malloc (font->type1_length);
+ if (unlikely (font->type1_data == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = scaled_font->backend->load_type1_data (scaled_font, 0,
+ (unsigned char *) font->type1_data,
+ &data_length);
+ if (unlikely (status))
+ return status;
+
+ if (!check_fontdata_is_type1 ((unsigned char *)font->type1_data, data_length))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_array_grow_by (&font->contents, 4096);
+ if (unlikely (status))
+ return status;
+
+ font->output = _cairo_output_stream_create (type1_font_write, NULL, font);
+ if (unlikely ((status = font->output->status)))
+ return status;
+
+ status = cairo_type1_font_subset_write (font, name);
+ if (unlikely (status))
+ return status;
+
+ font->base.data = _cairo_array_index (&font->contents, 0);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ unsigned int i;
+
+ /* If the subset generation failed, some of the pointers below may
+ * be NULL depending on at which point the error occurred. */
+
+ _cairo_array_fini (&font->contents);
+
+ free (font->type1_data);
+ for (i = 0; i < _cairo_array_num_elements (&font->glyph_names_array); i++) {
+ char **s;
+
+ s = _cairo_array_index (&font->glyph_names_array, i);
+ free (*s);
+ }
+ _cairo_array_fini (&font->glyph_names_array);
+ _cairo_array_fini (&font->glyphs_array);
+
+ free (font->subrs);
+
+ if (font->output != NULL)
+ status = _cairo_output_stream_destroy (font->output);
+
+ free (font->base.base_font);
+
+ free (font->subset_index_to_glyphs);
+
+ free (font->cleartext);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_type1_subset_init (cairo_type1_subset_t *type1_subset,
+ const char *name,
+ cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_bool_t hex_encode)
+{
+ cairo_type1_font_subset_t font;
+ cairo_status_t status;
+ unsigned long length;
+ unsigned int i;
+ char buf[30];
+
+ /* We need to use a fallback font generated from the synthesized outlines. */
+ if (scaled_font_subset->scaled_font->backend->is_synthetic &&
+ scaled_font_subset->scaled_font->backend->is_synthetic (scaled_font_subset->scaled_font))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_type1_font_subset_init (&font, scaled_font_subset, hex_encode);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_type1_font_subset_generate (&font, name);
+ if (unlikely (status))
+ goto fail1;
+
+ if (font.base.base_font) {
+ type1_subset->base_font = strdup (font.base.base_font);
+ } else {
+ snprintf(buf, sizeof (buf), "CairoFont-%u-%u",
+ scaled_font_subset->font_id, scaled_font_subset->subset_id);
+ type1_subset->base_font = strdup (buf);
+ }
+ if (unlikely (type1_subset->base_font == NULL))
+ goto fail1;
+
+ type1_subset->widths = calloc (sizeof (double), font.num_glyphs);
+ if (unlikely (type1_subset->widths == NULL))
+ goto fail2;
+ for (i = 0; i < font.base.num_glyphs; i++) {
+ if (font.glyphs[i].subset_index < 0)
+ continue;
+ type1_subset->widths[font.glyphs[i].subset_index] =
+ font.glyphs[i].width;
+ }
+
+ type1_subset->x_min = font.base.x_min;
+ type1_subset->y_min = font.base.y_min;
+ type1_subset->x_max = font.base.x_max;
+ type1_subset->y_max = font.base.y_max;
+ type1_subset->ascent = font.base.ascent;
+ type1_subset->descent = font.base.descent;
+
+ length = font.base.header_size +
+ font.base.data_size +
+ font.base.trailer_size;
+ type1_subset->data = malloc (length);
+ if (unlikely (type1_subset->data == NULL))
+ goto fail3;
+
+ memcpy (type1_subset->data,
+ _cairo_array_index (&font.contents, 0), length);
+
+ type1_subset->header_length = font.base.header_size;
+ type1_subset->data_length = font.base.data_size;
+ type1_subset->trailer_length = font.base.trailer_size;
+
+ return _cairo_type1_font_subset_fini (&font);
+
+ fail3:
+ free (type1_subset->widths);
+ fail2:
+ free (type1_subset->base_font);
+ fail1:
+ _cairo_type1_font_subset_fini (&font);
+
+ return status;
+}
+
+void
+_cairo_type1_subset_fini (cairo_type1_subset_t *subset)
+{
+ free (subset->base_font);
+ free (subset->widths);
+ free (subset->data);
+}
+
+cairo_bool_t
+_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font)
+{
+ cairo_status_t status;
+ unsigned long length;
+ unsigned char buf[64];
+
+ if (!scaled_font->backend->load_type1_data)
+ return FALSE;
+
+ status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &length);
+ if (status)
+ return FALSE;
+
+ /* We only need a few bytes to test for Type 1 */
+ if (length > sizeof (buf))
+ length = sizeof (buf);
+
+ status = scaled_font->backend->load_type1_data (scaled_font, 0, buf, &length);
+ if (status)
+ return FALSE;
+
+ return check_fontdata_is_type1 (buf, length);
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type3-glyph-surface-private.h b/src/cairo-type3-glyph-surface-private.h
new file mode 100755
index 000000000..b4abcf604
--- /dev/null
+++ b/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/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c
new file mode 100755
index 000000000..dc5dbdf61
--- /dev/null
+++ b/src/cairo-type3-glyph-surface.c
@@ -0,0 +1,573 @@
+/* -*- 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-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+
+static const cairo_surface_backend_t cairo_type3_glyph_surface_backend;
+
+static cairo_status_t
+_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper,
+ cairo_type3_glyph_surface_t,
+ clipper);
+
+ if (path == NULL) {
+ _cairo_output_stream_printf (surface->stream, "Q q\n");
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_pdf_operators_clip (&surface->pdf_operators,
+ path,
+ fill_rule);
+}
+
+cairo_surface_t *
+_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font,
+ cairo_output_stream_t *stream,
+ cairo_type3_glyph_surface_emit_image_t emit_image,
+ cairo_scaled_font_subsets_t *font_subsets)
+{
+ cairo_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)) {
+ cairo_surface_destroy (&image->base);
+ return status;
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ "q %f %f %f %f %f %f cm\n",
+ image_matrix->xx,
+ image_matrix->xy,
+ image_matrix->yx,
+ image_matrix->yy,
+ image_matrix->x0,
+ image_matrix->y0);
+
+ status = surface->emit_image (image, surface->stream);
+ cairo_surface_destroy (&image->base);
+
+ _cairo_output_stream_printf (surface->stream,
+ "Q\n");
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface,
+ cairo_image_surface_t *image,
+ const cairo_matrix_t *pattern_matrix)
+{
+ cairo_matrix_t mat, upside_down;
+ cairo_status_t status;
+
+ if (image->width == 0 || image->height == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ mat = *pattern_matrix;
+
+ /* Get the pattern space to user space matrix */
+ status = cairo_matrix_invert (&mat);
+
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ /* Make this a pattern space to Type 3 font space matrix */
+ cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf);
+
+ /* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by
+ * 1 image upside down to convert to flip the Y-axis going from
+ * cairo to PDF. Then scale the image up to the required size. */
+ cairo_matrix_scale (&mat, image->width, image->height);
+ cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1);
+ cairo_matrix_multiply (&mat, &upside_down, &mat);
+
+ return _cairo_type3_glyph_surface_emit_image (surface, image, &mat);
+}
+
+static cairo_status_t
+_cairo_type3_glyph_surface_finish (void *abstract_surface)
+{
+ cairo_type3_glyph_surface_t *surface = abstract_surface;
+
+ return _cairo_pdf_operators_fini (&surface->pdf_operators);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_type3_glyph_surface_t *surface = abstract_surface;
+ const cairo_surface_pattern_t *pattern;
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+
+ if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ status = _cairo_surface_acquire_source_image (pattern->surface,
+ &image, &image_extra);
+ if (unlikely (status))
+ goto fail;
+
+ status = _cairo_type3_glyph_surface_emit_image_pattern (surface,
+ image,
+ &pattern->base.matrix);
+
+fail:
+ _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ return _cairo_type3_glyph_surface_paint (abstract_surface,
+ op, mask,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_type3_glyph_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_pdf_operators_stroke (&surface->pdf_operators,
+ path,
+ style,
+ ctm,
+ ctm_inverse);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_type3_glyph_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_pdf_operators_fill (&surface->pdf_operators,
+ path,
+ fill_rule);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_type3_glyph_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ cairo_scaled_font_t *font;
+ cairo_matrix_t new_ctm, 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,
+ _cairo_type3_glyph_surface_finish,
+
+ _cairo_default_context_create, /* XXX usable through a context? */
+
+ NULL, /* create similar */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ NULL, /* source */
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* copy page */
+ NULL, /* show page */
+
+ NULL, /* _cairo_type3_glyph_surface_get_extents */
+ NULL, /* _cairo_type3_glyph_surface_get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_type3_glyph_surface_paint,
+ _cairo_type3_glyph_surface_mask,
+ _cairo_type3_glyph_surface_stroke,
+ _cairo_type3_glyph_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_type3_glyph_surface_show_glyphs,
+};
+
+static void
+_cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface,
+ cairo_output_stream_t *stream)
+{
+ surface->stream = stream;
+ _cairo_pdf_operators_set_stream (&surface->pdf_operators, stream);
+}
+
+static cairo_status_t
+_cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface,
+ unsigned long glyph_index)
+{
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_status_t status;
+ cairo_image_surface_t *image;
+ cairo_matrix_t mat;
+ double x, y;
+
+ status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS |
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (unlikely (status))
+ return status;
+
+ image = scaled_glyph->surface;
+ if (image->width == 0 || image->height == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x);
+ y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y);
+ 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_int_status_t status, status2;
+ cairo_output_stream_t *null_stream;
+
+ if (unlikely (surface->base.status))
+ return surface->base.status;
+
+ null_stream = _cairo_null_stream_create ();
+ if (unlikely (null_stream->status)) {
+ status = null_stream->status;
+ _cairo_output_stream_destroy (null_stream);
+ return status;
+ }
+
+ _cairo_type3_glyph_surface_set_stream (surface, null_stream);
+
+ _cairo_scaled_font_freeze_cache (surface->scaled_font);
+ status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
+ &scaled_glyph);
+
+ if (_cairo_int_status_is_error (status))
+ goto cleanup;
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = CAIRO_INT_STATUS_SUCCESS;
+ goto cleanup;
+ }
+
+ status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
+ &surface->base);
+ if (unlikely (status))
+ goto cleanup;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
+ status = CAIRO_INT_STATUS_SUCCESS;
+
+cleanup:
+ _cairo_scaled_font_thaw_cache (surface->scaled_font);
+
+ status2 = _cairo_output_stream_destroy (null_stream);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = status2;
+
+ return status;
+}
+
+cairo_status_t
+_cairo_type3_glyph_surface_emit_glyph (void *abstract_surface,
+ cairo_output_stream_t *stream,
+ unsigned long glyph_index,
+ cairo_box_t *bbox,
+ double *width)
+{
+ cairo_type3_glyph_surface_t *surface = abstract_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_int_status_t status, status2;
+ double x_advance, y_advance;
+ cairo_matrix_t font_matrix_inverse;
+
+ if (unlikely (surface->base.status))
+ return surface->base.status;
+
+ _cairo_type3_glyph_surface_set_stream (surface, stream);
+
+ _cairo_scaled_font_freeze_cache (surface->scaled_font);
+ status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS |
+ CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
+ &scaled_glyph);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
+ }
+ if (_cairo_int_status_is_error (status)) {
+ _cairo_scaled_font_thaw_cache (surface->scaled_font);
+ return status;
+ }
+
+ x_advance = scaled_glyph->metrics.x_advance;
+ y_advance = scaled_glyph->metrics.y_advance;
+ font_matrix_inverse = surface->scaled_font->font_matrix;
+ status2 = cairo_matrix_invert (&font_matrix_inverse);
+
+ /* The invertability of font_matrix is tested in
+ * pdf_operators_show_glyphs before any glyphs are mapped to the
+ * subset. */
+ assert (status2 == CAIRO_INT_STATUS_SUCCESS);
+
+ cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance);
+ *width = x_advance;
+
+ *bbox = scaled_glyph->bbox;
+ _cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse,
+ bbox, NULL);
+
+ _cairo_output_stream_printf (surface->stream,
+ "%f 0 %f %f %f %f d1\n",
+ x_advance,
+ _cairo_fixed_to_double (bbox->p1.x),
+ - _cairo_fixed_to_double (bbox->p2.y),
+ _cairo_fixed_to_double (bbox->p2.x),
+ - _cairo_fixed_to_double (bbox->p1.y));
+
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ cairo_output_stream_t *mem_stream;
+
+ mem_stream = _cairo_memory_stream_create ();
+ status = mem_stream->status;
+ if (unlikely (status)) {
+ _cairo_output_stream_destroy (mem_stream);
+ goto FAIL;
+ }
+
+ _cairo_type3_glyph_surface_set_stream (surface, mem_stream);
+
+ _cairo_output_stream_printf (surface->stream, "q\n");
+ status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
+ &surface->base);
+
+ status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = status2;
+
+ _cairo_output_stream_printf (surface->stream, "Q\n");
+
+ _cairo_type3_glyph_surface_set_stream (surface, stream);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ _cairo_memory_stream_copy (mem_stream, stream);
+
+ status2 = _cairo_output_stream_destroy (mem_stream);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = status2;
+ }
+
+ if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
+ status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index);
+
+ FAIL:
+ _cairo_scaled_font_thaw_cache (surface->scaled_font);
+
+ return status;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
new file mode 100755
index 000000000..5050fd552
--- /dev/null
+++ b/src/cairo-types-private.h
@@ -0,0 +1,453 @@
+/* -*- 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"
+
+CAIRO_BEGIN_DECLS
+
+/**
+ * SECTION:cairo-types
+ * @Title: Types
+ * @Short_Description: Generic data types
+ *
+ * This section lists generic data types used in the cairo API.
+ **/
+
+typedef struct _cairo_array cairo_array_t;
+typedef struct _cairo_backend cairo_backend_t;
+typedef struct _cairo_boxes_t cairo_boxes_t;
+typedef struct _cairo_cache cairo_cache_t;
+typedef struct _cairo_composite_rectangles cairo_composite_rectangles_t;
+typedef struct _cairo_clip cairo_clip_t;
+typedef struct _cairo_clip_path cairo_clip_path_t;
+typedef struct _cairo_color cairo_color_t;
+typedef struct _cairo_color_stop cairo_color_stop_t;
+typedef struct _cairo_contour cairo_contour_t;
+typedef struct _cairo_contour_chain cairo_contour_chain_t;
+typedef struct _cairo_contour_iter cairo_contour_iter_t;
+typedef struct _cairo_damage cairo_damage_t;
+typedef struct _cairo_device_backend cairo_device_backend_t;
+typedef struct _cairo_font_face_backend cairo_font_face_backend_t;
+typedef struct _cairo_gstate cairo_gstate_t;
+typedef struct _cairo_gstate_backend cairo_gstate_backend_t;
+typedef struct _cairo_glyph_text_info cairo_glyph_text_info_t;
+typedef struct _cairo_hash_entry cairo_hash_entry_t;
+typedef struct _cairo_hash_table cairo_hash_table_t;
+typedef struct _cairo_image_surface cairo_image_surface_t;
+typedef struct _cairo_mime_data cairo_mime_data_t;
+typedef struct _cairo_observer cairo_observer_t;
+typedef struct _cairo_output_stream cairo_output_stream_t;
+typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t;
+typedef struct _cairo_path_fixed cairo_path_fixed_t;
+typedef struct _cairo_rectangle_int16 cairo_glyph_size_t;
+typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t;
+typedef struct _cairo_solid_pattern cairo_solid_pattern_t;
+typedef struct _cairo_surface_attributes cairo_surface_attributes_t;
+typedef struct _cairo_surface_backend cairo_surface_backend_t;
+typedef struct _cairo_surface_observer cairo_surface_observer_t;
+typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t;
+typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t;
+typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t;
+typedef struct _cairo_traps cairo_traps_t;
+typedef struct _cairo_tristrip cairo_tristrip_t;
+typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t;
+typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t;
+
+typedef cairo_array_t cairo_user_data_array_t;
+
+typedef struct _cairo_scaled_font_private cairo_scaled_font_private_t;
+typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t;
+typedef struct _cairo_scaled_glyph cairo_scaled_glyph_t;
+typedef struct _cairo_scaled_glyph_private cairo_scaled_glyph_private_t;
+
+typedef struct cairo_compositor cairo_compositor_t;
+typedef struct cairo_fallback_compositor cairo_fallback_compositor_t;
+typedef struct cairo_mask_compositor cairo_mask_compositor_t;
+typedef struct cairo_traps_compositor cairo_traps_compositor_t;
+typedef struct cairo_spans_compositor cairo_spans_compositor_t;
+
+struct _cairo_observer {
+ cairo_list_t link;
+ void (*callback) (cairo_observer_t *self, void *arg);
+};
+
+/**
+ * cairo_hash_entry_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 responsible for initializing
+ * my_entry->base.hash with a hash code derived from the key. The
+ * essential property of the hash code is that keys_equal must never
+ * return %TRUE for two keys that have different hashes. The best hash
+ * code will reduce the frequency of two keys with the same code for
+ * which keys_equal returns %FALSE.
+ *
+ * Which parts of the entry make up the "key" and which part make up
+ * the value are entirely up to the caller, (as determined by the
+ * computation going into base.hash as well as the keys_equal
+ * function). A few of the #cairo_hash_table_t functions accept an entry
+ * which will be used exclusively as a "key", (indicated by a
+ * parameter name of key). In these cases, the value-related fields of
+ * the entry need not be initialized if so desired.
+ **/
+struct _cairo_hash_entry {
+ unsigned long hash;
+};
+
+struct _cairo_array {
+ unsigned int size;
+ unsigned int num_elements;
+ unsigned int element_size;
+ char *elements;
+};
+
+/**
+ * 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;
+ cairo_font_color_t color;
+};
+
+struct _cairo_glyph_text_info {
+ const char *utf8;
+ int utf8_len;
+
+ const cairo_text_cluster_t *clusters;
+ int num_clusters;
+ cairo_text_cluster_flags_t cluster_flags;
+};
+
+
+/* XXX: Right now, the _cairo_color structure puts unpremultiplied
+ color in the doubles and premultiplied color in the shorts. Yes,
+ this is crazy insane, (but at least we don't export this
+ madness). I'm still working on a cleaner API, but in the meantime,
+ at least this does prevent precision loss in color when changing
+ alpha. */
+struct _cairo_color {
+ double red;
+ double green;
+ double blue;
+ double alpha;
+
+ unsigned short red_short;
+ unsigned short green_short;
+ unsigned short blue_short;
+ unsigned short alpha_short;
+};
+
+struct _cairo_color_stop {
+ /* unpremultiplied */
+ double red;
+ double green;
+ double blue;
+ double alpha;
+
+ /* unpremultipled, for convenience */
+ uint16_t red_short;
+ uint16_t green_short;
+ uint16_t blue_short;
+ uint16_t alpha_short;
+};
+
+typedef enum _cairo_paginated_mode {
+ CAIRO_PAGINATED_MODE_ANALYZE, /* analyze page regions */
+ CAIRO_PAGINATED_MODE_RENDER, /* render page contents */
+ CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */
+} cairo_paginated_mode_t;
+
+typedef enum _cairo_internal_surface_type {
+ CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000,
+ CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
+ CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
+ CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
+ CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
+ CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
+ CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING,
+ CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+ CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH
+} cairo_internal_surface_type_t;
+
+typedef enum _cairo_internal_device_type {
+ CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER = 0x1000,
+} cairo_device_surface_type_t;
+
+#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1
+
+typedef struct _cairo_slope {
+ cairo_fixed_t dx;
+ cairo_fixed_t dy;
+} cairo_slope_t, cairo_distance_t;
+
+typedef struct _cairo_point_double {
+ double x;
+ double y;
+} cairo_point_double_t;
+
+typedef struct _cairo_circle_double {
+ cairo_point_double_t center;
+ double radius;
+} cairo_circle_double_t;
+
+typedef struct _cairo_distance_double {
+ double dx;
+ double dy;
+} cairo_distance_double_t;
+
+typedef struct _cairo_box_double {
+ cairo_point_double_t p1;
+ cairo_point_double_t p2;
+} cairo_box_double_t;
+
+typedef struct _cairo_line {
+ cairo_point_t p1;
+ cairo_point_t p2;
+} cairo_line_t, cairo_box_t;
+
+typedef struct _cairo_trapezoid {
+ cairo_fixed_t top, bottom;
+ cairo_line_t left, right;
+} cairo_trapezoid_t;
+
+typedef struct _cairo_point_int {
+ int x, y;
+} cairo_point_int_t;
+
+#define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS)
+#define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS)
+
+typedef enum _cairo_direction {
+ CAIRO_DIRECTION_FORWARD,
+ CAIRO_DIRECTION_REVERSE
+} cairo_direction_t;
+
+typedef struct _cairo_edge {
+ cairo_line_t line;
+ int top, bottom;
+ int dir;
+} cairo_edge_t;
+
+typedef struct _cairo_polygon {
+ cairo_status_t status;
+
+ cairo_box_t extents;
+ cairo_box_t limit;
+ const cairo_box_t *limits;
+ int num_limits;
+
+ int num_edges;
+ int edges_size;
+ cairo_edge_t *edges;
+ cairo_edge_t edges_embedded[32];
+} cairo_polygon_t;
+
+typedef cairo_warn cairo_status_t
+(*cairo_spline_add_point_func_t) (void *closure,
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent);
+
+typedef struct _cairo_spline_knots {
+ cairo_point_t a, b, c, d;
+} cairo_spline_knots_t;
+
+typedef struct _cairo_spline {
+ cairo_spline_add_point_func_t add_point_func;
+ void *closure;
+
+ cairo_spline_knots_t knots;
+
+ cairo_slope_t initial_slope;
+ cairo_slope_t final_slope;
+
+ cairo_bool_t has_point;
+ cairo_point_t last_point;
+} cairo_spline_t;
+
+typedef struct _cairo_pen_vertex {
+ cairo_point_t point;
+
+ cairo_slope_t slope_ccw;
+ cairo_slope_t slope_cw;
+} cairo_pen_vertex_t;
+
+typedef struct _cairo_pen {
+ double radius;
+ double tolerance;
+
+ int num_vertices;
+ cairo_pen_vertex_t *vertices;
+ cairo_pen_vertex_t vertices_embedded[32];
+} cairo_pen_t;
+
+typedef struct _cairo_stroke_style {
+ double line_width;
+ cairo_line_cap_t line_cap;
+ cairo_line_join_t line_join;
+ double miter_limit;
+ double *dash;
+ unsigned int num_dashes;
+ double dash_offset;
+} cairo_stroke_style_t;
+
+typedef struct _cairo_format_masks {
+ int bpp;
+ unsigned long alpha_mask;
+ unsigned long red_mask;
+ unsigned long green_mask;
+ unsigned long blue_mask;
+} cairo_format_masks_t;
+
+typedef enum {
+ CAIRO_STOCK_WHITE,
+ CAIRO_STOCK_BLACK,
+ CAIRO_STOCK_TRANSPARENT,
+ CAIRO_STOCK_NUM_COLORS,
+} cairo_stock_t;
+
+typedef enum _cairo_image_transparency {
+ CAIRO_IMAGE_IS_OPAQUE,
+ CAIRO_IMAGE_HAS_BILEVEL_ALPHA,
+ CAIRO_IMAGE_HAS_ALPHA,
+ CAIRO_IMAGE_UNKNOWN
+} cairo_image_transparency_t;
+
+typedef enum _cairo_image_color {
+ CAIRO_IMAGE_IS_COLOR,
+ CAIRO_IMAGE_IS_GRAYSCALE,
+ CAIRO_IMAGE_IS_MONOCHROME,
+ CAIRO_IMAGE_UNKNOWN_COLOR
+} cairo_image_color_t;
+
+
+struct _cairo_mime_data {
+ cairo_reference_count_t ref_count;
+ unsigned char *data;
+ unsigned long length;
+ cairo_destroy_func_t destroy;
+ void *closure;
+};
+
+/*
+ * A #cairo_unscaled_font_t is just an opaque handle we use in the
+ * glyph cache.
+ */
+typedef struct _cairo_unscaled_font {
+ cairo_hash_entry_t hash_entry;
+ cairo_reference_count_t ref_count;
+ const cairo_unscaled_font_backend_t *backend;
+} cairo_unscaled_font_t;
+
+typedef struct _cairo_shadow {
+ double x_offset;
+ double y_offset;
+ cairo_shadow_type_t type;
+ double x_blur;
+ double y_blur;
+ cairo_color_t color;
+ cairo_bool_t path_is_fill_with_spread;
+ cairo_bool_t enable_cache;
+ cairo_bool_t draw_shadow_only;
+} cairo_shadow_t;
+
+typedef struct _cairo_shadow_cache {
+ cairo_surface_t *surface;
+ unsigned long hash;
+ unsigned long size;
+ double x_blur;
+ double y_blur;
+ double scale;
+ cairo_list_t link;
+} cairo_shadow_cache_t;
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_TYPES_PRIVATE_H */
diff --git a/src/cairo-unicode.c b/src/cairo-unicode.c
new file mode 100755
index 000000000..88de39516
--- /dev/null
+++ b/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/src/cairo-uninstalled.pc.in b/src/cairo-uninstalled.pc.in
new file mode 100755
index 000000000..9dc3231ae
--- /dev/null
+++ b/src/cairo-uninstalled.pc.in
@@ -0,0 +1,8 @@
+Name: cairo
+Description: Multi-platform 2D graphics library
+Version: @VERSION@
+
+@PKGCONFIG_REQUIRES@: @CAIRO_REQUIRES@
+Libs: ${pc_top_builddir}/${pcfiledir}/src/libcairo.la
+Libs.private: @CAIRO_NONPKGCONFIG_LIBS@
+Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@/src
diff --git a/src/cairo-user-font-private.h b/src/cairo-user-font-private.h
new file mode 100755
index 000000000..d54ef78b4
--- /dev/null
+++ b/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/src/cairo-user-font.c b/src/cairo-user-font.c
new file mode 100755
index 000000000..297f21c91
--- /dev/null
+++ b/src/cairo-user-font.c
@@ -0,0 +1,831 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006, 2008 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <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_INT_STATUS_SUCCESS)
+ status = cairo_status (cr);
+
+ cairo_destroy (cr);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (recording_surface);
+ return status;
+ }
+ }
+
+ _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
+ &scaled_font->base,
+ recording_surface);
+
+
+ /* set metrics */
+
+ if (extents.width == 0.) {
+ cairo_box_t bbox;
+ double x1, y1, x2, y2;
+ double x_scale, y_scale;
+
+ /* Compute extents.x/y/width/height from recording_surface,
+ * in font space.
+ */
+ status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+ &bbox,
+ &scaled_font->extent_scale);
+ if (unlikely (status))
+ return status;
+
+ _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2);
+
+ x_scale = scaled_font->extent_x_scale;
+ y_scale = scaled_font->extent_y_scale;
+ extents.x_bearing = x1 * x_scale;
+ extents.y_bearing = y1 * y_scale;
+ extents.width = (x2 - x1) * x_scale;
+ extents.height = (y2 - y1) * y_scale;
+ }
+
+ if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) {
+ extents.x_advance = _cairo_lround (extents.x_advance / scaled_font->snap_x_scale) * scaled_font->snap_x_scale;
+ extents.y_advance = _cairo_lround (extents.y_advance / scaled_font->snap_y_scale) * scaled_font->snap_y_scale;
+ }
+
+ _cairo_scaled_glyph_set_metrics (scaled_glyph,
+ &scaled_font->base,
+ &extents);
+ }
+
+ if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) {
+ cairo_surface_t *surface;
+ cairo_format_t format;
+ int width, height;
+
+ /* TODO
+ * extend the glyph cache to support argb glyphs.
+ * need to figure out the semantics and interaction with subpixel
+ * rendering first.
+ */
+
+ width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) -
+ _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+ height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) -
+ _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+
+ switch (scaled_font->base.options.antialias) {
+ default:
+ case CAIRO_ANTIALIAS_DEFAULT:
+ case CAIRO_ANTIALIAS_FAST:
+ case CAIRO_ANTIALIAS_GOOD:
+ case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break;
+ case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break;
+ case CAIRO_ANTIALIAS_BEST:
+ case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break;
+ }
+ surface = cairo_image_surface_create (format, width, height);
+
+ cairo_surface_set_device_offset (surface,
+ - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x),
+ - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y));
+ status = _cairo_recording_surface_replay (recording_surface, surface);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy(surface);
+ return status;
+ }
+
+ _cairo_scaled_glyph_set_surface (scaled_glyph,
+ &scaled_font->base,
+ (cairo_image_surface_t *) surface);
+ }
+
+ if (info & CAIRO_SCALED_GLYPH_INFO_PATH) {
+ cairo_path_fixed_t *path = _cairo_path_fixed_create ();
+ if (!path)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_recording_surface_get_path (recording_surface, path);
+ if (unlikely (status)) {
+ _cairo_path_fixed_destroy (path);
+ return status;
+ }
+
+ _cairo_scaled_glyph_set_path (scaled_glyph,
+ &scaled_font->base,
+ path);
+ }
+
+ return status;
+}
+
+static unsigned long
+_cairo_user_ucs4_to_index (void *abstract_font,
+ uint32_t ucs4)
+{
+ cairo_user_scaled_font_t *scaled_font = abstract_font;
+ cairo_user_font_face_t *face =
+ (cairo_user_font_face_t *) scaled_font->base.font_face;
+ unsigned long glyph = 0;
+
+ if (face->scaled_font_methods.unicode_to_glyph) {
+ cairo_status_t status;
+
+ status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base,
+ ucs4, &glyph);
+
+ if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
+ goto not_implemented;
+
+ if (status != CAIRO_STATUS_SUCCESS) {
+ status = _cairo_scaled_font_set_error (&scaled_font->base, status);
+ glyph = 0;
+ }
+
+ } else {
+not_implemented:
+ glyph = ucs4;
+ }
+
+ return glyph;
+}
+
+static cairo_int_status_t
+_cairo_user_text_to_glyphs (void *abstract_font,
+ double x,
+ double y,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs,
+ cairo_text_cluster_t **clusters,
+ int *num_clusters,
+ cairo_text_cluster_flags_t *cluster_flags)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cairo_user_scaled_font_t *scaled_font = abstract_font;
+ cairo_user_font_face_t *face =
+ (cairo_user_font_face_t *) scaled_font->base.font_face;
+
+ if (face->scaled_font_methods.text_to_glyphs) {
+ int i;
+ cairo_glyph_t *orig_glyphs = *glyphs;
+ int orig_num_glyphs = *num_glyphs;
+
+ status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters, cluster_flags);
+
+ if (status != CAIRO_INT_STATUS_SUCCESS &&
+ status != CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED)
+ return status;
+
+ if (status == CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED ||
+ *num_glyphs < 0) {
+ if (orig_glyphs != *glyphs) {
+ cairo_glyph_free (*glyphs);
+ *glyphs = orig_glyphs;
+ }
+ *num_glyphs = orig_num_glyphs;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* Convert from font space to user space and add x,y */
+ for (i = 0; i < *num_glyphs; i++) {
+ double gx = (*glyphs)[i].x;
+ double gy = (*glyphs)[i].y;
+
+ cairo_matrix_transform_point (&scaled_font->base.font_matrix,
+ &gx, &gy);
+
+ (*glyphs)[i].x = gx + x;
+ (*glyphs)[i].y = gy + y;
+ }
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_user_font_face_scaled_font_create (void *abstract_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **scaled_font);
+
+static cairo_status_t
+_cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
+ cairo_font_face_t **font_face)
+{
+ return _cairo_font_face_twin_create_for_toy (toy_face, font_face);
+}
+
+static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = {
+ CAIRO_FONT_TYPE_USER,
+ NULL, /* scaled_font_fini */
+ _cairo_user_scaled_glyph_init,
+ _cairo_user_text_to_glyphs,
+ _cairo_user_ucs4_to_index,
+ NULL, /* show_glyphs */
+ NULL, /* load_truetype_table */
+ NULL /* index_to_ucs4 */
+};
+
+/* #cairo_user_font_face_t */
+
+static cairo_status_t
+_cairo_user_font_face_scaled_font_create (void *abstract_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **scaled_font)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_user_font_face_t *font_face = abstract_face;
+ cairo_user_scaled_font_t *user_scaled_font = NULL;
+ cairo_font_extents_t font_extents = {1., 0., 1., 1., 0.};
+
+ font_face->immutable = TRUE;
+
+ user_scaled_font = 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/src/cairo-version.c b/src/cairo-version.c
new file mode 100755
index 000000000..d9ad240bf
--- /dev/null
+++ b/src/cairo-version.c
@@ -0,0 +1,260 @@
+/* -*- 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-version.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><screen>
+ * _____ 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
+ * </screen></informalexample>
+ *
+ * Here are a few examples of versions that one might see.
+ * <informalexample><screen>
+ * Releases
+ * --------
+ * 1.0.0 - A major release
+ * 1.0.2 - A subsequent maintenance release
+ * 1.2.0 - Another major release
+ * &nbsp;
+ * Snapshots
+ * ---------
+ * 1.1.2 - A snapshot (working toward the 1.2.0 release)
+ * &nbsp;
+ * 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)
+ * </screen></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><screen>
+ * Compile-time
+ * ------------
+ * CAIRO_VERSION_STRING Human-readable
+ * CAIRO_VERSION Encoded, suitable for comparison
+ * &nbsp;
+ * Run-time
+ * --------
+ * cairo_version_string() Human-readable
+ * cairo_version() Encoded, suitable for comparison
+ * </screen></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().
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * CAIRO_VERSION_MAJOR:
+ *
+ * The major component of the version of cairo available at compile-time.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * CAIRO_VERSION_MINOR:
+ *
+ * The minor component of the version of cairo available at compile-time.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * CAIRO_VERSION_MICRO:
+ *
+ * The micro component of the version of cairo available at compile-time.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * CAIRO_VERSION_STRING:
+ *
+ * A human-readable string literal containing the version of cairo available
+ * at compile-time, in the form of "X.Y.Z".
+ *
+ * Since: 1.8
+ **/
+
+/**
+ * CAIRO_VERSION_ENCODE:
+ * @major: the major component of the version number
+ * @minor: the minor component of the version number
+ * @micro: the micro component of the version number
+ *
+ * This macro encodes the given cairo version into an integer. The numbers
+ * returned by %CAIRO_VERSION and cairo_version() are encoded using this macro.
+ * Two encoded version numbers can be compared as integers. The encoding ensures
+ * that later versions compare greater than earlier versions.
+ *
+ * Returns: the encoded version.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * CAIRO_VERSION_STRINGIZE:
+ * @major: the major component of the version number
+ * @minor: the minor component of the version number
+ * @micro: the micro component of the version number
+ *
+ * This macro encodes the given cairo version into an string. The numbers
+ * returned by %CAIRO_VERSION_STRING and cairo_version_string() are encoded using this macro.
+ * The parameters to this macro must expand to numerical literals.
+ *
+ * Returns: a string literal containing the version.
+ *
+ * Since: 1.8
+ **/
+
+/**
+ * cairo_version:
+ *
+ * Returns the version of the cairo library encoded in a single
+ * integer as per %CAIRO_VERSION_ENCODE. The encoding ensures that
+ * later versions compare greater than earlier versions.
+ *
+ * A run-time comparison to check that cairo's version is greater than
+ * or equal to version X.Y.Z could be performed as follows:
+ *
+ * <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.
+ *
+ * Since: 1.0
+ **/
+int
+cairo_version (void)
+{
+ return CAIRO_VERSION;
+}
+
+/**
+ * cairo_version_string:
+ *
+ * Returns the version of the cairo library as a human-readable string
+ * of the form "X.Y.Z".
+ *
+ * See also cairo_version() as well as the compile-time equivalents
+ * %CAIRO_VERSION_STRING and %CAIRO_VERSION.
+ *
+ * Return value: a string containing the version.
+ *
+ * Since: 1.0
+ **/
+const char*
+cairo_version_string (void)
+{
+ return CAIRO_VERSION_STRING;
+}
+slim_hidden_def (cairo_version_string);
diff --git a/src/cairo-version.h b/src/cairo-version.h
new file mode 100755
index 000000000..51008003f
--- /dev/null
+++ b/src/cairo-version.h
@@ -0,0 +1,14 @@
+/* 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
+
+#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
diff --git a/src/cairo-vg-surface.c b/src/cairo-vg-surface.c
new file mode 100755
index 000000000..6e0d9a0ed
--- /dev/null
+++ b/src/cairo-vg-surface.c
@@ -0,0 +1,1853 @@
+/* -*- 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-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-surface-clipper-private.h"
+
+#include <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 PIXMAN_r8g8b8x8;
+ case VG_sRGBA_8888: *needs_premult_fixup = TRUE; return PIXMAN_r8g8b8a8;
+ case VG_sRGBA_8888_PRE: return PIXMAN_r8g8b8a8;
+ case VG_sRGB_565: return PIXMAN_r5g6b5;
+ case VG_sRGBA_5551: return 0;
+ case VG_sRGBA_4444: return 0;
+ case VG_sL_8: return PIXMAN_g8;
+ case VG_lRGBX_8888: return 0;
+ case VG_lRGBA_8888: return 0;
+ case VG_lRGBA_8888_PRE: return 0;
+ case VG_lL_8: return 0;
+ case VG_A_8: return PIXMAN_a8;
+ case VG_BW_1: return PIXMAN_a1;
+ case VG_A_1: return PIXMAN_a1;
+ case VG_A_4: return PIXMAN_a4;
+
+ /* {A,X}RGB channel ordering */
+ case VG_sXRGB_8888: return PIXMAN_x8r8g8b8;
+ case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8;
+ case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8;
+ case VG_sARGB_1555: return 0;
+ case VG_sARGB_4444: return 0;
+ case VG_lXRGB_8888: return 0;
+ case VG_lARGB_8888: return 0;
+ case VG_lARGB_8888_PRE: return 0;
+
+ /* BGR{A,X} channel ordering */
+ case VG_sBGRX_8888: return PIXMAN_b8g8r8x8;
+ case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8;
+ case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8;
+ case VG_sBGR_565: return PIXMAN_b5g6r5;
+ case VG_sBGRA_5551: return 0;
+ case VG_sBGRA_4444: return 0;
+ case VG_lBGRX_8888: return 0;
+ case VG_lBGRA_8888: return 0;
+ case VG_lBGRA_8888_PRE: return 0;
+
+ /* {A,X}BGR channel ordering */
+ case VG_sXBGR_8888: return PIXMAN_x8b8g8r8;
+ case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8;
+ case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8;
+ case VG_sABGR_1555: return 0;
+ case VG_sABGR_4444: return 0;
+ case VG_lXBGR_8888: return 0;
+ case VG_lABGR_8888: return 0;
+ case VG_lABGR_8888_PRE: return 0;
+ default: return 0;
+ }
+}
+
+static pixman_format_code_t
+_vg_format_to_content (VGImageFormat format)
+{
+ /* XXX could use more simple bit tests */
+ switch (format) {
+ /* RGB{A,X} channel ordering */
+ case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR;
+ case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sRGB_565: return CAIRO_CONTENT_COLOR;
+ case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sL_8: return CAIRO_CONTENT_ALPHA;
+ case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR;
+ case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lL_8: return CAIRO_CONTENT_ALPHA;
+ case VG_A_8: return CAIRO_CONTENT_ALPHA;
+ case VG_A_4: return CAIRO_CONTENT_ALPHA;
+ case VG_A_1: return CAIRO_CONTENT_ALPHA;
+ case VG_BW_1: return CAIRO_CONTENT_ALPHA;
+
+ /* {A,X}RGB channel ordering */
+ case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR;
+ case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR;
+ case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+
+ /* BGR{A,X} channel ordering */
+ case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR;
+ case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sBGR_565: return CAIRO_CONTENT_COLOR;
+ case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR;
+ case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+
+ /* {A,X}BGR channel ordering */
+ case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR;
+ case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR;
+ case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+ case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+ default: return 0;
+ }
+}
+
+static VGImageFormat
+_vg_format_from_pixman (pixman_format_code_t format)
+{
+ /* XXX _PRE needs fixup */
+ switch ((int) format) {
+ case PIXMAN_r5g6b5: return VG_sRGB_565;
+ case PIXMAN_g8: return VG_sL_8;
+ case PIXMAN_a8: return VG_A_8;
+ case PIXMAN_a1: return VG_BW_1;
+ case PIXMAN_x8r8g8b8: return VG_sXRGB_8888;
+ case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE
+ case PIXMAN_b8g8r8x8: return VG_sBGRX_8888;
+ case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE
+ case PIXMAN_b5g6r5: return VG_sBGR_565;
+ case PIXMAN_x8b8g8r8: return VG_sXBGR_8888;
+ case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE
+ default: return 0;
+ }
+}
+
+static VGImageFormat
+_vg_format_for_content (cairo_content_t content)
+{
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA: return VG_A_8;
+ case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888;
+ default: ASSERT_NOT_REACHED;
+ case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE
+ }
+}
+
+static cairo_surface_t *
+_vg_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+
+ if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+ height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+ {
+ return NULL;
+ }
+
+ return cairo_vg_surface_create (surface->context, content, width, height);
+}
+
+static cairo_status_t
+_vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_vg_surface_t *surface = cairo_container_of (clipper,
+ cairo_vg_surface_t,
+ clipper);
+ cairo_vg_surface_t *mask;
+ cairo_status_t status;
+
+ if (path == NULL) {
+ vgMask (VG_INVALID_HANDLE,
+ VG_FILL_MASK, 0, 0, surface->width, surface->height);
+ vgSeti (VG_MASKING, VG_FALSE);
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ mask = (cairo_vg_surface_t *)
+ _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA,
+ surface->width, surface->height);
+ if (unlikely (mask == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (unlikely (mask->base.status))
+ return mask->base.status;
+
+ status = _cairo_surface_fill (&mask->base,
+ CAIRO_OPERATOR_SOURCE,
+ &_cairo_pattern_white.base,
+ path, fill_rule, tolerance, antialias,
+ NULL);
+ if (status) {
+ cairo_surface_destroy (&mask->base);
+ return status;
+ }
+
+ vgSeti (VG_MASKING, VG_TRUE);
+ vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height);
+
+ cairo_surface_destroy (&mask->base);
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_vg_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+
+ extents->x = 0;
+ extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+
+ return TRUE;
+}
+
+#define MAX_SEG 16 /* max number of knots to upload in a batch */
+
+typedef struct _vg_path {
+ VGPath path;
+ const cairo_matrix_t *ctm_inverse;
+
+ VGubyte gseg[MAX_SEG];
+ VGfloat gdata[MAX_SEG*3*2];
+ int dcount;
+ int scount;
+} vg_path_t;
+
+static cairo_status_t
+_vg_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ vg_path_t *path = closure;
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ if (path->ctm_inverse)
+ cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
+
+ path->gseg[path->scount++] = VG_MOVE_TO;
+ path->gdata[path->dcount++] = x;
+ path->gdata[path->dcount++] = y;
+
+ if (path->scount >= MAX_SEG-1) {
+ vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+ path->scount = 0;
+ path->dcount = 0;
+ }
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ vg_path_t *path = closure;
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ if (path->ctm_inverse)
+ cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
+
+ path->gseg[path->scount++] = VG_LINE_TO;
+ path->gdata[path->dcount++] = x;
+ path->gdata[path->dcount++] = y;
+
+ if (path->scount >= MAX_SEG-1) {
+ vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+ path->scount = 0;
+ path->dcount = 0;
+ }
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_curve_to (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ vg_path_t *path = closure;
+ double x0 = _cairo_fixed_to_double (p0->x);
+ double y0 = _cairo_fixed_to_double (p0->y);
+ double x1 = _cairo_fixed_to_double (p1->x);
+ double y1 = _cairo_fixed_to_double (p1->y);
+ double x2 = _cairo_fixed_to_double (p2->x);
+ double y2 = _cairo_fixed_to_double (p2->y);
+
+ if (path->ctm_inverse) {
+ cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0);
+ cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1);
+ cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2);
+ }
+
+ path->gseg[path->scount++] = VG_CUBIC_TO;
+ path->gdata[path->dcount++] = x0;
+ path->gdata[path->dcount++] = y0;
+ path->gdata[path->dcount++] = x1;
+ path->gdata[path->dcount++] = y1;
+ path->gdata[path->dcount++] = x2;
+ path->gdata[path->dcount++] = y2;
+
+ if (path->scount >= MAX_SEG-1) {
+ vgAppendPathData(path->path, path->scount, path->gseg, path->gdata);
+ path->scount = 0;
+ path->dcount = 0;
+ }
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_close_path (void *closure)
+{
+ vg_path_t *path = closure;
+
+ path->gseg[path->scount++] = VG_CLOSE_PATH;
+
+ if (path->scount >= MAX_SEG-1) {
+ vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+ path->scount = 0;
+ path->dcount = 0;
+ }
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_path_from_cairo (vg_path_t *vg_path,
+ const cairo_path_fixed_t *path)
+{
+ cairo_status_t status;
+
+ vg_path->scount = 0;
+ vg_path->dcount = 0;
+
+ status = _cairo_path_fixed_interpret (path,
+ _vg_move_to,
+ _vg_line_to,
+ _vg_curve_to,
+ _vg_close_path,
+ vg_path);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ vgAppendPathData (vg_path->path,
+ vg_path->scount, vg_path->gseg, vg_path->gdata);
+ CHECK_VG_ERRORS();
+}
+
+static cairo_bool_t
+_vg_is_supported_operator (cairo_operator_t op)
+{
+ switch ((int) op) {
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static VGBlendMode
+_vg_operator (cairo_operator_t op)
+{
+ switch ((int) op) {
+ case CAIRO_OPERATOR_SOURCE:
+ return VG_BLEND_SRC;
+ case CAIRO_OPERATOR_OVER:
+ return VG_BLEND_SRC_OVER;
+ case CAIRO_OPERATOR_IN:
+ return VG_BLEND_SRC_IN;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return VG_BLEND_DST_OVER;
+ case CAIRO_OPERATOR_DEST_IN:
+ return VG_BLEND_DST_IN;
+ case CAIRO_OPERATOR_ADD:
+ return VG_BLEND_ADDITIVE;
+ default:
+ ASSERT_NOT_REACHED;
+ return VG_BLEND_SRC_OVER;
+ }
+}
+
+static VGFillRule
+_vg_fill_rule_from_cairo (cairo_fill_rule_t rule)
+{
+ switch (rule) {
+ case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD;
+ case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO;
+ }
+
+ ASSERT_NOT_REACHED;
+ return VG_NON_ZERO;
+}
+
+static VGRenderingQuality
+_vg_rendering_quality_from_cairo (cairo_antialias_t aa)
+{
+ switch (aa) {
+ case CAIRO_ANTIALIAS_DEFAULT:
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ case CAIRO_ANTIALIAS_GOOD:
+ case CAIRO_ANTIALIAS_BEST:
+ return VG_RENDERING_QUALITY_BETTER;
+
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_FAST:
+ return VG_RENDERING_QUALITY_FASTER;
+
+ case CAIRO_ANTIALIAS_NONE:
+ return VG_RENDERING_QUALITY_NONANTIALIASED;
+ }
+
+ ASSERT_NOT_REACHED;
+ return VG_RENDERING_QUALITY_BETTER;
+}
+
+static VGCapStyle
+_vg_line_cap_from_cairo (cairo_line_cap_t cap)
+{
+ switch (cap) {
+ case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT;
+ case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND;
+ case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE;
+ }
+
+ ASSERT_NOT_REACHED;
+ return VG_CAP_BUTT;
+}
+
+static VGJoinStyle
+_vg_line_join_from_cairo (cairo_line_join_t join)
+{
+ switch (join) {
+ case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER;
+ case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND;
+ case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL;
+ }
+
+ ASSERT_NOT_REACHED;
+ return VG_JOIN_MITER;
+}
+
+static void
+_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src)
+{
+ dst[0] = /* sx */ src->xx;
+ dst[1] = /* shy */ src->yx;
+ dst[2] = /* w0 */ 0;
+ dst[3] = /* shx */ src->xy;
+ dst[4] = /* sy */ src->yy;
+ dst[5] = /* w1 */ 0;
+ dst[6] = /* tx */ src->x0;
+ dst[7] = /* ty */ src->y0;
+ dst[8] = /* w2 */ 0;
+}
+
+static cairo_status_t
+_vg_setup_gradient_stops (cairo_vg_context_t *context,
+ const cairo_gradient_pattern_t *pattern)
+{
+ VGint numstops = pattern->n_stops;
+ VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)];
+ int i;
+
+ if (numstops*5 < ARRAY_LENGTH (stack_stops)) {
+ stops = stack_stops;
+ } else {
+ stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat));
+ if (unlikely (stops == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < numstops; i++) {
+ stops[i*5 + 0] = pattern->stops[i].offset;
+ stops[i*5 + 1] = pattern->stops[i].color.red;
+ stops[i*5 + 2] = pattern->stops[i].color.green;
+ stops[i*5 + 3] = pattern->stops[i].color.blue;
+ stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha;
+ }
+
+ vgSetParameterfv (context->paint,
+ VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops);
+
+ if (stops != stack_stops)
+ free (stops);
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_set_source_matrix (const cairo_pattern_t *pat)
+{
+ cairo_matrix_t mat;
+ cairo_status_t status;
+ VGfloat vmat[9];
+
+ mat = pat->matrix;
+ status = cairo_matrix_invert (&mat);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _vg_matrix_from_cairo (vmat, &mat);
+
+ vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
+ vgLoadMatrix (vmat);
+ vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
+ vgLoadMatrix (vmat);
+ vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+
+ CHECK_VG_ERRORS();
+}
+
+static cairo_status_t
+_vg_setup_linear_source (cairo_vg_context_t *context,
+ const cairo_linear_pattern_t *lpat)
+{
+ VGfloat linear[4];
+
+ linear[0] = lpat->pd1.x;
+ linear[1] = lpat->pd1.y;
+ linear[2] = lpat->pd2.x;
+ linear[3] = lpat->pd2.y;
+
+ vgSetParameteri (context->paint,
+ VG_PAINT_COLOR_RAMP_SPREAD_MODE,
+ VG_COLOR_RAMP_SPREAD_PAD);
+ vgSetParameteri (context->paint,
+ VG_PAINT_TYPE,
+ VG_PAINT_TYPE_LINEAR_GRADIENT);
+ vgSetParameterfv (context->paint,
+ VG_PAINT_LINEAR_GRADIENT, 4, linear);
+
+ _vg_set_source_matrix (&lpat->base.base);
+
+ CHECK_VG_ERRORS();
+ return _vg_setup_gradient_stops (context, &lpat->base);
+
+}
+
+static cairo_status_t
+_vg_setup_radial_source (cairo_vg_context_t *context,
+ const cairo_radial_pattern_t *rpat)
+{
+ VGfloat radial[5];
+
+ radial[0] = rpat->cd1.center.x;
+ radial[1] = rpat->cd1.center.y;
+ radial[2] = rpat->cd2.center.x;
+ radial[3] = rpat->cd2.center.y;
+ radial[4] = rpat->cd2.radius;
+
+ vgSetParameteri (context->paint,
+ VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD);
+ vgSetParameteri (context->paint,
+ VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
+ vgSetParameterfv (context->paint,
+ VG_PAINT_RADIAL_GRADIENT, 5, radial);
+
+ _vg_set_source_matrix (&rpat->base.base);
+
+ /* FIXME: copy/adapt fixes from SVG backend to add inner radius */
+
+ CHECK_VG_ERRORS();
+ return _vg_setup_gradient_stops (context, &rpat->base);
+}
+
+static cairo_status_t
+_vg_setup_solid_source (cairo_vg_context_t *context,
+ const cairo_solid_pattern_t *spat)
+{
+ VGfloat color[] = {
+ spat->color.red,
+ spat->color.green,
+ spat->color.blue,
+ spat->color.alpha * context->alpha
+ };
+
+ vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color);
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_vg_surface_t *
+_vg_clone_recording_surface (cairo_vg_context_t *context,
+ cairo_surface_t *surface)
+{
+ VGImage vg_image;
+ VGImageFormat format;
+ cairo_status_t status;
+ cairo_rectangle_int_t extents;
+ cairo_vg_surface_t *clone;
+
+ status = _cairo_surface_get_extents (surface, &extents);
+ if (status)
+ return NULL;
+
+ if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+ extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+ {
+ return NULL;
+ }
+
+ format = _vg_format_for_content (surface->content);
+
+ /* NONALIASED, FASTER, BETTER */
+ vg_image = vgCreateImage (format,
+ extents.width, extents.height,
+ VG_IMAGE_QUALITY_FASTER);
+ clone = (cairo_vg_surface_t *)
+ _vg_surface_create_internal (context, vg_image, format,
+ extents.width, extents.height);
+ cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y);
+
+ status = _cairo_recording_surface_replay (surface, &clone->base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->base);
+ return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
+ }
+
+ return clone;
+}
+
+static cairo_vg_surface_t *
+_vg_clone_image_surface (cairo_vg_context_t *context,
+ cairo_surface_t *surface)
+{
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+ VGImage vg_image;
+ VGImageFormat format;
+ cairo_rectangle_int_t extents;
+ cairo_vg_surface_t *clone;
+
+ if (surface->backend->acquire_source_image == NULL)
+ return NULL;
+
+ status = _cairo_surface_get_extents (surface, &extents);
+ if (status)
+ return NULL;
+
+ if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+ extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+ {
+ return NULL;
+ }
+
+ status = _cairo_surface_acquire_source_image (surface,
+ &image, &image_extra);
+ if (unlikely (status))
+ return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
+
+ format = _vg_format_from_pixman (image->pixman_format);
+ if (format == 0)
+ format = _vg_format_for_content (image->base.content);
+
+ /* NONALIASED, FASTER, BETTER */
+ vg_image = vgCreateImage (format,
+ image->width, image->height,
+ VG_IMAGE_QUALITY_FASTER);
+ clone = (cairo_vg_surface_t *)
+ _vg_surface_create_internal (context, vg_image, format,
+ image->width, image->height);
+ if (unlikely (clone->base.status))
+ return clone;
+
+ vgImageSubData (clone->image,
+ image->data, image->stride,
+ format, 0, 0, image->width, image->height);
+
+ _cairo_surface_release_source_image (surface, image, image_extra);
+
+ return clone;
+}
+
+static void
+_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface)
+{
+ cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface;
+
+ if (surface->snapshot_cache_entry.hash) {
+ cairo_vg_context_t *context;
+
+ context = _vg_context_lock (surface->context);
+ _cairo_cache_remove (&context->snapshot_cache,
+ &surface->snapshot_cache_entry);
+ _vg_context_unlock (context);
+
+ surface->snapshot_cache_entry.hash = 0;
+ }
+}
+
+static cairo_status_t
+_vg_setup_surface_source (cairo_vg_context_t *context,
+ const cairo_surface_pattern_t *spat)
+{
+ cairo_surface_t *snapshot;
+ cairo_vg_surface_t *clone;
+ cairo_status_t status;
+
+ snapshot = _cairo_surface_has_snapshot (spat->surface,
+ &cairo_vg_surface_backend);
+ if (snapshot != NULL) {
+ clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot);
+ goto DONE;
+ }
+
+ if (_cairo_surface_is_recording (spat->surface))
+ clone = _vg_clone_recording_surface (context, spat->surface);
+ else
+ clone = _vg_clone_image_surface (context, spat->surface);
+ if (clone == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (unlikely (clone->base.status))
+ return clone->base.status;
+
+ clone->snapshot_cache_entry.hash = clone->base.unique_id;
+ status = _cairo_cache_insert (&context->snapshot_cache,
+ &clone->snapshot_cache_entry);
+ if (unlikely (status)) {
+ clone->snapshot_cache_entry.hash = 0;
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+
+ _cairo_surface_attach_snapshot (spat->surface, &clone->base,
+ _vg_surface_remove_from_cache);
+
+DONE:
+ cairo_surface_destroy (&context->source->base);
+ context->source = clone;
+
+ vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
+
+ switch (spat->base.extend) {
+ case CAIRO_EXTEND_PAD:
+ vgSetParameteri (context->paint,
+ VG_PAINT_PATTERN_TILING_MODE,
+ VG_TILE_PAD);
+ break;
+
+ case CAIRO_EXTEND_NONE:
+ vgSetParameteri (context->paint,
+ VG_PAINT_PATTERN_TILING_MODE,
+ VG_TILE_FILL);
+ {
+ VGfloat color[] = {0,0,0,0};
+ vgSetfv (VG_TILE_FILL_COLOR, 4, color);
+ }
+ break;
+
+ case CAIRO_EXTEND_REPEAT:
+ vgSetParameteri (context->paint,
+ VG_PAINT_PATTERN_TILING_MODE,
+ VG_TILE_REPEAT);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_EXTEND_REFLECT:
+ vgSetParameteri (context->paint,
+ VG_PAINT_PATTERN_TILING_MODE,
+ VG_TILE_REFLECT);
+ break;
+ }
+ vgPaintPattern (context->paint, context->source->image);
+
+ _vg_set_source_matrix (&spat->base);
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+setup_source (cairo_vg_context_t *context,
+ const cairo_pattern_t *source)
+{
+ switch (source->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _vg_setup_solid_source (context,
+ (cairo_solid_pattern_t *) source);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _vg_setup_linear_source (context,
+ (cairo_linear_pattern_t *) source);
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _vg_setup_radial_source (context,
+ (cairo_radial_pattern_t *) source);
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _vg_setup_surface_source (context,
+ (cairo_surface_pattern_t *) source);
+ default:
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+}
+
+static cairo_int_status_t
+_vg_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+ cairo_vg_context_t *context;
+ cairo_status_t status;
+ VGfloat state[9];
+ VGfloat strokeTransform[9];
+ vg_path_t vg_path;
+
+ if (! _vg_is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ context = _vg_context_lock (surface->context);
+ status = _vg_context_set_target (context, surface);
+ if (status) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ status = setup_source (context, source);
+ if (status) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status)) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1, 0, 0, 0,
+ VG_PATH_CAPABILITY_ALL);
+
+ vgGetMatrix (state);
+ _vg_matrix_from_cairo (strokeTransform, ctm);
+ vgMultMatrix (strokeTransform);
+
+ vg_path.ctm_inverse = ctm_inverse;
+
+ _vg_path_from_cairo (&vg_path, path);
+
+ /* XXX DASH_PATTERN, DASH_PHASE */
+ vgSetf (VG_STROKE_LINE_WIDTH, style->line_width);
+ vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit);
+ vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join));
+ vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap));
+
+ vgSeti (VG_BLEND_MODE, _vg_operator (op));
+
+ vgSetPaint (context->paint, VG_STROKE_PATH);
+
+ vgDrawPath (vg_path.path, VG_STROKE_PATH);
+
+ vgDestroyPath (vg_path.path);
+
+ vgLoadMatrix (state);
+
+ CHECK_VG_ERRORS();
+ _vg_context_unlock (context);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+ cairo_vg_context_t *context;
+ cairo_status_t status;
+ vg_path_t vg_path;
+
+ if (op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! _vg_is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ context = _vg_context_lock (surface->context);
+ status = _vg_context_set_target (context, surface);
+ if (status) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ status = setup_source (context, source);
+ if (status) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status)) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,
+ 1, 0,
+ 0, 0,
+ VG_PATH_CAPABILITY_ALL);
+ vg_path.ctm_inverse = NULL;
+
+ _vg_path_from_cairo (&vg_path, path);
+
+ /* XXX tolerance */
+
+ vgSeti (VG_BLEND_MODE, _vg_operator (op));
+ vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule));
+ vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias));
+
+ vgSetPaint (context->paint, VG_FILL_PATH);
+
+ vgDrawPath (vg_path.path, VG_FILL_PATH);
+
+ vgDestroyPath (vg_path.path);
+
+ _vg_context_unlock (context);
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+ cairo_vg_context_t *context;
+ cairo_status_t status;
+
+ if (op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! _vg_is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ context = _vg_context_lock (surface->context);
+ status = _vg_context_set_target (context, surface);
+ if (status) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ status = setup_source (context, source);
+ if (status) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status)) {
+ _vg_context_unlock (context);
+ return status;
+ }
+
+ vgSeti (VG_BLEND_MODE, _vg_operator (op));
+ vgSetPaint (context->paint, VG_FILL_PATH);
+
+ { /* creating a rectangular path that should cover the extent */
+ VGubyte segs[] = {
+ VG_MOVE_TO_ABS, VG_LINE_TO_ABS,
+ VG_LINE_TO_ABS, VG_LINE_TO_ABS,
+ VG_CLOSE_PATH
+ };
+ VGfloat data[] = {
+ 0, 0,
+ surface->width, 0,
+ surface->width, surface->height,
+ 0, surface->height
+ };
+ VGPath fullext;
+
+ fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1,0,0,0, VG_PATH_CAPABILITY_ALL);
+ vgAppendPathData (fullext, sizeof(segs), segs, data);
+
+ vgDrawPath (fullext, VG_FILL_PATH);
+
+ vgDestroyPath (fullext);
+ }
+
+ _vg_context_unlock (context);
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (! _vg_is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Handle paint-with-alpha to do fades cheaply */
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask;
+ cairo_vg_context_t *context = _vg_context_lock (surface->context);
+ double alpha = context->alpha;
+
+ context->alpha = solid->color.alpha;
+ status = _vg_surface_paint (abstract_surface, op, source, clip);
+ context->alpha = alpha;
+
+ _vg_context_unlock (context);
+
+ return status;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_vg_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+static cairo_int_status_t
+_vg_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_path_fixed_t path;
+
+ if (num_glyphs <= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_path_fixed_init (&path);
+
+ /* XXX Glyph cache! OpenVG font support in 1.1? */
+
+ status = _cairo_scaled_font_glyph_path (scaled_font,
+ glyphs, num_glyphs,
+ &path);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _vg_surface_fill (abstract_surface,
+ op, source, &path,
+ CAIRO_FILL_RULE_WINDING,
+ CAIRO_GSTATE_TOLERANCE_DEFAULT,
+ CAIRO_ANTIALIAS_DEFAULT,
+ clip);
+BAIL:
+ _cairo_path_fixed_fini (&path);
+ return status;
+}
+
+static inline int
+multiply_alpha (int alpha, int color)
+{
+ int temp = alpha * color + 0x80;
+ return (temp + (temp >> 8)) >> 8;
+}
+
+static void
+premultiply_argb (uint8_t *data,
+ int width,
+ int height,
+ int stride)
+{
+ int i;
+
+ while (height --) {
+ uint32_t *row = (uint32_t *) data;
+
+ for (i = 0; i < width; i++) {
+ uint32_t p = row[i];
+ uint8_t alpha;
+
+ alpha = p >> 24;
+ if (alpha == 0) {
+ row[i] = 0;
+ } else if (alpha != 0xff) {
+ uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff);
+ uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff);
+ uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff);
+ row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
+ }
+ }
+
+ data += stride;
+ }
+}
+
+static cairo_int_status_t
+_vg_get_image (cairo_vg_surface_t *surface,
+ int x, int y,
+ int width, int height,
+ cairo_image_surface_t **image_out)
+{
+ cairo_image_surface_t *image;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+ cairo_bool_t needs_premultiply;
+
+ pixman_format = _vg_format_to_pixman (surface->format,
+ &needs_premultiply);
+ if (pixman_format == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ pixman_image = pixman_image_create_bits (pixman_format,
+ width, height,
+ NULL, 0);
+ if (unlikely (pixman_image == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ vgFinish ();
+ CHECK_VG_ERRORS();
+
+ vgGetImageSubData (surface->image,
+ pixman_image_get_data (pixman_image),
+ pixman_image_get_stride (pixman_image),
+ surface->format,
+ x, y, width, height);
+
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_for_pixman_image (pixman_image,
+ pixman_format);
+ if (unlikely (image->base.status)) {
+ pixman_image_unref (pixman_image);
+ return image->base.status;
+ }
+
+ if (needs_premultiply)
+ premultiply_argb (image->data, width, height, image->stride);
+
+ *image_out = image;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+
+ CHECK_VG_ERRORS();
+ *image_extra = NULL;
+ return _vg_get_image (surface,
+ 0, 0, surface->width, surface->height,
+ image_out);
+}
+
+static void
+_vg_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_vg_surface_finish (void *abstract_surface)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+ cairo_vg_context_t *context = _vg_context_lock (surface->context);
+
+ if (surface->snapshot_cache_entry.hash) {
+ _cairo_cache_remove (&context->snapshot_cache,
+ &surface->snapshot_cache_entry);
+
+ surface->snapshot_cache_entry.hash = 0;
+ }
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ if (surface->own_image)
+ vgDestroyImage (surface->image);
+
+ _vg_context_destroy_target (context, surface);
+
+ _vg_context_unlock (context);
+ _vg_context_destroy (context);
+
+ CHECK_VG_ERRORS();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_vg_surface_backend = {
+ CAIRO_SURFACE_TYPE_VG,
+ _vg_surface_finish,
+
+ _cairo_default_context_create, /* XXX */
+
+ _vg_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ _vg_surface_acquire_source_image,
+ _vg_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _vg_surface_get_extents,
+ _vg_surface_get_font_options, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark dirty */
+
+ _vg_surface_paint,
+ _vg_surface_mask,
+ _vg_surface_stroke,
+ _vg_surface_fill,
+ NULL, /* fill-stroke */
+ _vg_surface_show_glyphs,
+};
+
+static cairo_surface_t *
+_vg_surface_create_internal (cairo_vg_context_t *context,
+ VGImage image,
+ VGImageFormat format,
+ int width, int height)
+{
+ cairo_vg_surface_t *surface;
+
+ surface = 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/src/cairo-vg.h b/src/cairo-vg.h
new file mode 100755
index 000000000..f9a62e51c
--- /dev/null
+++ b/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/src/cairo-wgl-context.c b/src/cairo-wgl-context.c
new file mode 100755
index 000000000..31cbcfe07
--- /dev/null
+++ b/src/cairo-wgl-context.c
@@ -0,0 +1,268 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Zoxc <zoxc32@gmail.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-error-private.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef struct _cairo_wgl_context {
+ cairo_gl_context_t base;
+
+ HDC dummy_dc;
+ HWND dummy_wnd;
+ HGLRC rc;
+
+ HDC prev_dc;
+ HGLRC prev_rc;
+} cairo_wgl_context_t;
+
+typedef struct _cairo_wgl_surface {
+ cairo_gl_surface_t base;
+
+ HDC dc;
+} cairo_wgl_surface_t;
+
+static void
+_wgl_acquire (void *abstract_ctx)
+{
+ cairo_wgl_context_t *ctx = abstract_ctx;
+
+ HDC current_dc;
+
+ ctx->prev_dc = wglGetCurrentDC ();
+ ctx->prev_rc = wglGetCurrentContext ();
+
+ if (ctx->base.current_target == NULL ||
+ _cairo_gl_surface_is_texture (ctx->base.current_target))
+ {
+ current_dc = ctx->dummy_dc;
+ }
+ else
+ {
+ cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) ctx->base.current_target;
+ current_dc = surface->dc;
+ }
+
+ if (ctx->prev_dc != current_dc ||
+ (ctx->prev_rc != ctx->rc &&
+ current_dc != ctx->dummy_dc))
+ {
+ wglMakeCurrent (current_dc, ctx->rc);
+ }
+}
+
+static void
+_wgl_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
+{
+ cairo_wgl_context_t *ctx = abstract_ctx;
+ cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface;
+
+ /* Set the window as the target of our context. */
+ wglMakeCurrent (surface->dc, ctx->rc);
+}
+
+static void
+_wgl_release (void *abstract_ctx)
+{
+ cairo_wgl_context_t *ctx = abstract_ctx;
+
+ if (ctx->prev_dc != wglGetCurrentDC () ||
+ ctx->prev_rc != wglGetCurrentContext ())
+ {
+ wglMakeCurrent (ctx->prev_dc,
+ ctx->prev_rc);
+ }
+}
+
+static void
+_wgl_swap_buffers (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{
+ cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface;
+
+ SwapBuffers (surface->dc);
+}
+
+static void
+_wgl_destroy (void *abstract_ctx)
+{
+ cairo_wgl_context_t *ctx = abstract_ctx;
+
+ if (ctx->dummy_dc != 0) {
+ wglMakeCurrent (ctx->dummy_dc, 0);
+ ReleaseDC (ctx->dummy_wnd, ctx->dummy_dc);
+ DestroyWindow (ctx->dummy_wnd);
+ }
+}
+
+static cairo_status_t
+_wgl_dummy_ctx (cairo_wgl_context_t *ctx)
+{
+ WNDCLASSEXA wincl;
+ PIXELFORMATDESCRIPTOR pfd;
+ int format;
+ HDC dc;
+
+ ZeroMemory (&wincl, sizeof (WNDCLASSEXA));
+ wincl.cbSize = sizeof (WNDCLASSEXA);
+ wincl.hInstance = GetModuleHandle (0);
+ wincl.lpszClassName = "cairo_wgl_context_dummy";
+ wincl.lpfnWndProc = DefWindowProcA;
+ wincl.style = CS_OWNDC;
+
+ RegisterClassExA (&wincl);
+
+ ctx->dummy_wnd = CreateWindowA ("cairo_wgl_context_dummy", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ ctx->dummy_dc = GetDC (ctx->dummy_wnd);
+
+ ZeroMemory (&pfd, sizeof (PIXELFORMATDESCRIPTOR));
+ pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 24;
+ pfd.cDepthBits = 16;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+
+ format = ChoosePixelFormat (ctx->dummy_dc, &pfd);
+ SetPixelFormat (ctx->dummy_dc, format, &pfd);
+
+ wglMakeCurrent(ctx->dummy_dc, ctx->rc);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_gl_generic_func_t
+_cairo_wgl_get_proc_address (void *data, const char *name)
+{
+ return wglGetProcAddress (name);
+}
+
+cairo_device_t *
+cairo_wgl_device_create (HGLRC rc)
+{
+ cairo_wgl_context_t *ctx;
+ cairo_status_t status;
+
+ ctx = calloc (1, sizeof (cairo_wgl_context_t));
+ if (unlikely (ctx == NULL))
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ ctx->rc = rc;
+ ctx->prev_dc = 0;
+ ctx->prev_rc = 0;
+
+ status = _wgl_dummy_ctx (ctx);
+ if (unlikely (status)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ ctx->base.acquire = _wgl_acquire;
+ ctx->base.release = _wgl_release;
+ ctx->base.make_current = _wgl_make_current;
+ ctx->base.swap_buffers = _wgl_swap_buffers;
+ ctx->base.destroy = _wgl_destroy;
+
+ status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
+ (cairo_gl_get_proc_addr_func_t) _cairo_wgl_get_proc_address,
+ NULL);
+ if (unlikely (status)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ status = _cairo_gl_context_init (&ctx->base);
+ if (unlikely (status)) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ ctx->base.release (ctx);
+
+ return &ctx->base.base;
+}
+
+HGLRC
+cairo_wgl_device_get_context (cairo_device_t *device)
+{
+ cairo_wgl_context_t *ctx;
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ ctx = (cairo_wgl_context_t *) device;
+
+ return ctx->rc;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_dc (cairo_device_t *device,
+ HDC dc,
+ int width,
+ int height)
+{
+ cairo_wgl_surface_t *surface;
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+ if (width <= 0 || height <= 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ surface = calloc (1, sizeof (cairo_wgl_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_gl_surface_init (device, &surface->base,
+ CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ surface->dc = dc;
+
+ return &surface->base.base;
+}
diff --git a/src/cairo-wideint-private.h b/src/cairo-wideint-private.h
new file mode 100755
index 000000000..3f5491bb1
--- /dev/null
+++ b/src/cairo-wideint-private.h
@@ -0,0 +1,338 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ * Keith R. Packard <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_double_to_uint64 (double i);
+double I _cairo_uint64_to_double (cairo_uint64_t i);
+cairo_int64_t I _cairo_double_to_int64 (double i);
+double I _cairo_int64_to_double (cairo_uint64_t i);
+
+cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i);
+#define _cairo_uint64_to_uint32(a) ((a).lo)
+cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I _cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I _cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I _cairo_uint32x32_64_mul (uint32_t a, uint32_t b);
+cairo_uint64_t I _cairo_uint64_lsl (cairo_uint64_t a, int shift);
+cairo_uint64_t I _cairo_uint64_rsl (cairo_uint64_t a, int shift);
+cairo_uint64_t I _cairo_uint64_rsa (cairo_uint64_t a, int shift);
+int I _cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b);
+int I _cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b);
+int I _cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I _cairo_uint64_negate (cairo_uint64_t a);
+#define _cairo_uint64_is_zero(a) ((a).hi == 0 && (a).lo == 0)
+#define _cairo_uint64_negative(a) (((int32_t) ((a).hi)) < 0)
+cairo_uint64_t I _cairo_uint64_not (cairo_uint64_t a);
+
+#define _cairo_uint64_to_int64(i) (i)
+#define _cairo_int64_to_uint64(i) (i)
+
+cairo_int64_t I _cairo_int32_to_int64(int32_t i);
+#define _cairo_int64_to_int32(a) ((int32_t) _cairo_uint64_to_uint32(a))
+#define _cairo_int64_add(a,b) _cairo_uint64_add (a,b)
+#define _cairo_int64_sub(a,b) _cairo_uint64_sub (a,b)
+#define _cairo_int64_mul(a,b) _cairo_uint64_mul (a,b)
+cairo_int64_t I _cairo_int32x32_64_mul (int32_t a, int32_t b);
+int I _cairo_int64_lt (cairo_int64_t a, cairo_int64_t b);
+int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b);
+#define _cairo_int64_is_zero(a) _cairo_uint64_is_zero (a)
+#define _cairo_int64_eq(a,b) _cairo_uint64_eq (a,b)
+#define _cairo_int64_lsl(a,b) _cairo_uint64_lsl (a,b)
+#define _cairo_int64_rsl(a,b) _cairo_uint64_rsl (a,b)
+#define _cairo_int64_rsa(a,b) _cairo_uint64_rsa (a,b)
+#define _cairo_int64_negate(a) _cairo_uint64_negate(a)
+#define _cairo_int64_negative(a) (((int32_t) ((a).hi)) < 0)
+#define _cairo_int64_not(a) _cairo_uint64_not(a)
+
+#else
+
+static inline cairo_uquorem64_t
+_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
+{
+ cairo_uquorem64_t qr;
+
+ qr.quo = num / den;
+ qr.rem = num % den;
+ return qr;
+}
+
+/*
+ * These need to be functions or gcc will complain when used on the
+ * result of a function:
+ *
+ * warning: cast from function call of type ‘#cairo_uint64_t’ to
+ * non-matching type ‘double’
+ */
+static cairo_always_inline cairo_const cairo_uint64_t _cairo_double_to_uint64 (double i) { return i; }
+static cairo_always_inline cairo_const double _cairo_uint64_to_double (cairo_uint64_t i) { return i; }
+
+static cairo_always_inline cairo_int64_t I _cairo_double_to_int64 (double i) { return i; }
+static cairo_always_inline double I _cairo_int64_to_double (cairo_int64_t i) { return i; }
+
+#define _cairo_uint32_to_uint64(i) ((uint64_t) (i))
+#define _cairo_uint64_to_uint32(i) ((uint32_t) (i))
+#define _cairo_uint64_add(a,b) ((a) + (b))
+#define _cairo_uint64_sub(a,b) ((a) - (b))
+#define _cairo_uint64_mul(a,b) ((a) * (b))
+#define _cairo_uint32x32_64_mul(a,b) ((uint64_t) (a) * (b))
+#define _cairo_uint64_lsl(a,b) ((a) << (b))
+#define _cairo_uint64_rsl(a,b) ((uint64_t) (a) >> (b))
+#define _cairo_uint64_rsa(a,b) ((uint64_t) ((int64_t) (a) >> (b)))
+#define _cairo_uint64_lt(a,b) ((a) < (b))
+#define _cairo_uint64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define _cairo_uint64_is_zero(a) ((a) == 0)
+#define _cairo_uint64_eq(a,b) ((a) == (b))
+#define _cairo_uint64_negate(a) ((uint64_t) -((int64_t) (a)))
+#define _cairo_uint64_negative(a) ((int64_t) (a) < 0)
+#define _cairo_uint64_not(a) (~(a))
+
+#define _cairo_uint64_to_int64(i) ((int64_t) (i))
+#define _cairo_int64_to_uint64(i) ((uint64_t) (i))
+
+#define _cairo_int32_to_int64(i) ((int64_t) (i))
+#define _cairo_int64_to_int32(i) ((int32_t) (i))
+#define _cairo_int64_add(a,b) ((a) + (b))
+#define _cairo_int64_sub(a,b) ((a) - (b))
+#define _cairo_int64_mul(a,b) ((a) * (b))
+#define _cairo_int32x32_64_mul(a,b) ((int64_t) (a) * (b))
+#define _cairo_int64_lt(a,b) ((a) < (b))
+#define _cairo_int64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define _cairo_int64_is_zero(a) ((a) == 0)
+#define _cairo_int64_eq(a,b) ((a) == (b))
+#define _cairo_int64_lsl(a,b) ((a) << (b))
+#define _cairo_int64_rsl(a,b) ((int64_t) ((uint64_t) (a) >> (b)))
+#define _cairo_int64_rsa(a,b) ((int64_t) (a) >> (b))
+#define _cairo_int64_negate(a) (-(a))
+#define _cairo_int64_negative(a) ((a) < 0)
+#define _cairo_int64_not(a) (~(a))
+
+#endif
+
+/*
+ * 64-bit comparisons derived from lt or eq
+ */
+#define _cairo_uint64_le(a,b) (!_cairo_uint64_gt(a,b))
+#define _cairo_uint64_ne(a,b) (!_cairo_uint64_eq(a,b))
+#define _cairo_uint64_ge(a,b) (!_cairo_uint64_lt(a,b))
+#define _cairo_uint64_gt(a,b) _cairo_uint64_lt(b,a)
+
+#define _cairo_int64_le(a,b) (!_cairo_int64_gt(a,b))
+#define _cairo_int64_ne(a,b) (!_cairo_int64_eq(a,b))
+#define _cairo_int64_ge(a,b) (!_cairo_int64_lt(a,b))
+#define _cairo_int64_gt(a,b) _cairo_int64_lt(b,a)
+
+/*
+ * As the C implementation always computes both, create
+ * a function which returns both for the 'native' type as well
+ */
+
+static inline cairo_quorem64_t
+_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den)
+{
+ int num_neg = _cairo_int64_negative (num);
+ int den_neg = _cairo_int64_negative (den);
+ cairo_uquorem64_t uqr;
+ cairo_quorem64_t qr;
+
+ if (num_neg)
+ num = _cairo_int64_negate (num);
+ if (den_neg)
+ den = _cairo_int64_negate (den);
+ uqr = _cairo_uint64_divrem (num, den);
+ if (num_neg)
+ qr.rem = _cairo_int64_negate (uqr.rem);
+ else
+ qr.rem = uqr.rem;
+ if (num_neg != den_neg)
+ qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo);
+ else
+ qr.quo = (cairo_int64_t) uqr.quo;
+ return qr;
+}
+
+static inline int32_t
+_cairo_int64_32_div (cairo_int64_t num, int32_t den)
+{
+#if !HAVE_UINT64_T
+ return _cairo_int64_to_int32
+ (_cairo_int64_divrem (num, _cairo_int32_to_int64 (den)).quo);
+#else
+ return num / den;
+#endif
+}
+
+/*
+ * 128-bit datatypes. Again, provide two implementations in
+ * case the machine has a native 128-bit datatype. GCC supports int128_t
+ * on ia64
+ */
+
+#if !HAVE_UINT128_T
+
+cairo_uint128_t I _cairo_uint32_to_uint128 (uint32_t i);
+cairo_uint128_t I _cairo_uint64_to_uint128 (cairo_uint64_t i);
+#define _cairo_uint128_to_uint64(a) ((a).lo)
+#define _cairo_uint128_to_uint32(a) _cairo_uint64_to_uint32(_cairo_uint128_to_uint64(a))
+cairo_uint128_t I _cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b);
+cairo_uint128_t I _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b);
+cairo_uint128_t I _cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b);
+cairo_uint128_t I _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint128_t I _cairo_uint128_lsl (cairo_uint128_t a, int shift);
+cairo_uint128_t I _cairo_uint128_rsl (cairo_uint128_t a, int shift);
+cairo_uint128_t I _cairo_uint128_rsa (cairo_uint128_t a, int shift);
+int I _cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b);
+int I _cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b);
+int I _cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b);
+#define _cairo_uint128_is_zero(a) (_cairo_uint64_is_zero ((a).hi) && _cairo_uint64_is_zero ((a).lo))
+cairo_uint128_t I _cairo_uint128_negate (cairo_uint128_t a);
+#define _cairo_uint128_negative(a) (_cairo_uint64_negative(a.hi))
+cairo_uint128_t I _cairo_uint128_not (cairo_uint128_t a);
+
+#define _cairo_uint128_to_int128(i) (i)
+#define _cairo_int128_to_uint128(i) (i)
+
+cairo_int128_t I _cairo_int32_to_int128 (int32_t i);
+cairo_int128_t I _cairo_int64_to_int128 (cairo_int64_t i);
+#define _cairo_int128_to_int64(a) ((cairo_int64_t) (a).lo)
+#define _cairo_int128_to_int32(a) _cairo_int64_to_int32(_cairo_int128_to_int64(a))
+#define _cairo_int128_add(a,b) _cairo_uint128_add(a,b)
+#define _cairo_int128_sub(a,b) _cairo_uint128_sub(a,b)
+#define _cairo_int128_mul(a,b) _cairo_uint128_mul(a,b)
+cairo_int128_t I _cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b);
+#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b))
+#define _cairo_int128_lsl(a,b) _cairo_uint128_lsl(a,b)
+#define _cairo_int128_rsl(a,b) _cairo_uint128_rsl(a,b)
+#define _cairo_int128_rsa(a,b) _cairo_uint128_rsa(a,b)
+int I _cairo_int128_lt (cairo_int128_t a, cairo_int128_t b);
+int I _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b);
+#define _cairo_int128_is_zero(a) _cairo_uint128_is_zero (a)
+#define _cairo_int128_eq(a,b) _cairo_uint128_eq (a,b)
+#define _cairo_int128_negate(a) _cairo_uint128_negate(a)
+#define _cairo_int128_negative(a) (_cairo_uint128_negative(a))
+#define _cairo_int128_not(a) _cairo_uint128_not(a)
+
+#else /* !HAVE_UINT128_T */
+
+#define _cairo_uint32_to_uint128(i) ((uint128_t) (i))
+#define _cairo_uint64_to_uint128(i) ((uint128_t) (i))
+#define _cairo_uint128_to_uint64(i) ((uint64_t) (i))
+#define _cairo_uint128_to_uint32(i) ((uint32_t) (i))
+#define _cairo_uint128_add(a,b) ((a) + (b))
+#define _cairo_uint128_sub(a,b) ((a) - (b))
+#define _cairo_uint128_mul(a,b) ((a) * (b))
+#define _cairo_uint64x64_128_mul(a,b) ((uint128_t) (a) * (b))
+#define _cairo_uint128_lsl(a,b) ((a) << (b))
+#define _cairo_uint128_rsl(a,b) ((uint128_t) (a) >> (b))
+#define _cairo_uint128_rsa(a,b) ((uint128_t) ((int128_t) (a) >> (b)))
+#define _cairo_uint128_lt(a,b) ((a) < (b))
+#define _cairo_uint128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define _cairo_uint128_is_zero(a) ((a) == 0)
+#define _cairo_uint128_eq(a,b) ((a) == (b))
+#define _cairo_uint128_negate(a) ((uint128_t) -((int128_t) (a)))
+#define _cairo_uint128_negative(a) ((int128_t) (a) < 0)
+#define _cairo_uint128_not(a) (~(a))
+
+#define _cairo_uint128_to_int128(i) ((int128_t) (i))
+#define _cairo_int128_to_uint128(i) ((uint128_t) (i))
+
+#define _cairo_int32_to_int128(i) ((int128_t) (i))
+#define _cairo_int64_to_int128(i) ((int128_t) (i))
+#define _cairo_int128_to_int64(i) ((int64_t) (i))
+#define _cairo_int128_to_int32(i) ((int32_t) (i))
+#define _cairo_int128_add(a,b) ((a) + (b))
+#define _cairo_int128_sub(a,b) ((a) - (b))
+#define _cairo_int128_mul(a,b) ((a) * (b))
+#define _cairo_int64x64_128_mul(a,b) ((int128_t) (a) * (b))
+#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b))
+#define _cairo_int128_lt(a,b) ((a) < (b))
+#define _cairo_int128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define _cairo_int128_is_zero(a) ((a) == 0)
+#define _cairo_int128_eq(a,b) ((a) == (b))
+#define _cairo_int128_lsl(a,b) ((a) << (b))
+#define _cairo_int128_rsl(a,b) ((int128_t) ((uint128_t) (a) >> (b)))
+#define _cairo_int128_rsa(a,b) ((int128_t) (a) >> (b))
+#define _cairo_int128_negate(a) (-(a))
+#define _cairo_int128_negative(a) ((a) < 0)
+#define _cairo_int128_not(a) (~(a))
+
+#endif /* HAVE_UINT128_T */
+
+cairo_uquorem128_t I
+_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den);
+
+cairo_quorem128_t I
+_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den);
+
+cairo_uquorem64_t I
+_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num,
+ cairo_uint64_t den);
+
+cairo_quorem64_t I
+_cairo_int_96by64_32x64_divrem (cairo_int128_t num,
+ cairo_int64_t den);
+
+#define _cairo_uint128_le(a,b) (!_cairo_uint128_gt(a,b))
+#define _cairo_uint128_ne(a,b) (!_cairo_uint128_eq(a,b))
+#define _cairo_uint128_ge(a,b) (!_cairo_uint128_lt(a,b))
+#define _cairo_uint128_gt(a,b) _cairo_uint128_lt(b,a)
+
+#define _cairo_int128_le(a,b) (!_cairo_int128_gt(a,b))
+#define _cairo_int128_ne(a,b) (!_cairo_int128_eq(a,b))
+#define _cairo_int128_ge(a,b) (!_cairo_int128_lt(a,b))
+#define _cairo_int128_gt(a,b) _cairo_int128_lt(b,a)
+
+#undef I
+
+#endif /* CAIRO_WIDEINT_H */
diff --git a/src/cairo-wideint-type-private.h b/src/cairo-wideint-type-private.h
new file mode 100755
index 000000000..84a3cbab0
--- /dev/null
+++ b/src/cairo-wideint-type-private.h
@@ -0,0 +1,158 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ * Keith R. Packard <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 HAVE_UINT64_T
+# define HAVE_UINT64_T 1
+# endif
+#else
+#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)
+#endif
+
+#ifndef INT16_MIN
+# define INT16_MIN (-32767-1)
+#endif
+#ifndef INT16_MAX
+# define INT16_MAX (32767)
+#endif
+#ifndef UINT16_MAX
+# define UINT16_MAX (65535)
+#endif
+#ifndef INT32_MIN
+# define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT32_MAX
+# define INT32_MAX (2147483647)
+#endif
+#ifndef UINT32_MAX
+# define UINT32_MAX (4294967295U)
+#endif
+
+#if HAVE_BYTESWAP_H
+# include <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/src/cairo-wideint.c b/src/cairo-wideint.c
new file mode 100755
index 000000000..bba266b24
--- /dev/null
+++ b/src/cairo-wideint.c
@@ -0,0 +1,852 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ * Keith R. Packard <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_double_to_uint64 (double i)
+{
+ cairo_uint64_t q;
+
+ q.hi = i * (1. / 4294967296.);
+ q.lo = i - q.hi * 4294967296.;
+ return q;
+}
+
+double
+_cairo_uint64_to_double (cairo_uint64_t i)
+{
+ return i.hi * 4294967296. + i.lo;
+}
+
+cairo_int64_t
+_cairo_double_to_int64 (double i)
+{
+ cairo_uint64_t q;
+
+ q.hi = i * (1. / INT32_MAX);
+ q.lo = i - q.hi * (double)INT32_MAX;
+ return q;
+}
+
+double
+_cairo_int64_to_double (cairo_int64_t i)
+{
+ return i.hi * INT32_MAX + i.lo;
+}
+
+cairo_uint64_t
+_cairo_uint32_to_uint64 (uint32_t i)
+{
+ cairo_uint64_t q;
+
+ q.lo = i;
+ q.hi = 0;
+ return q;
+}
+
+cairo_int64_t
+_cairo_int32_to_int64 (int32_t i)
+{
+ cairo_uint64_t q;
+
+ q.lo = i;
+ q.hi = i < 0 ? -1 : 0;
+ return q;
+}
+
+static cairo_uint64_t
+_cairo_uint32s_to_uint64 (uint32_t h, uint32_t l)
+{
+ cairo_uint64_t q;
+
+ q.lo = l;
+ q.hi = h;
+ return q;
+}
+
+cairo_uint64_t
+_cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b)
+{
+ cairo_uint64_t s;
+
+ s.hi = a.hi + b.hi;
+ s.lo = a.lo + b.lo;
+ if (s.lo < a.lo)
+ s.hi++;
+ return s;
+}
+
+cairo_uint64_t
+_cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b)
+{
+ cairo_uint64_t s;
+
+ s.hi = a.hi - b.hi;
+ s.lo = a.lo - b.lo;
+ if (s.lo > a.lo)
+ s.hi--;
+ return s;
+}
+
+#define uint32_lo(i) ((i) & 0xffff)
+#define uint32_hi(i) ((i) >> 16)
+#define uint32_carry16 ((1) << 16)
+
+cairo_uint64_t
+_cairo_uint32x32_64_mul (uint32_t a, uint32_t b)
+{
+ cairo_uint64_t s;
+
+ uint16_t ah, al, bh, bl;
+ uint32_t r0, r1, r2, r3;
+
+ al = uint32_lo (a);
+ ah = uint32_hi (a);
+ bl = uint32_lo (b);
+ bh = uint32_hi (b);
+
+ r0 = (uint32_t) al * bl;
+ r1 = (uint32_t) al * bh;
+ r2 = (uint32_t) ah * bl;
+ r3 = (uint32_t) ah * bh;
+
+ r1 += uint32_hi(r0); /* no carry possible */
+ r1 += r2; /* but this can carry */
+ if (r1 < r2) /* check */
+ r3 += uint32_carry16;
+
+ s.hi = r3 + uint32_hi(r1);
+ s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0);
+ return s;
+}
+
+cairo_int64_t
+_cairo_int32x32_64_mul (int32_t a, int32_t b)
+{
+ cairo_int64_t s;
+ s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t) b);
+ if (a < 0)
+ s.hi -= b;
+ if (b < 0)
+ s.hi -= a;
+ return s;
+}
+
+cairo_uint64_t
+_cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b)
+{
+ cairo_uint64_t s;
+
+ s = _cairo_uint32x32_64_mul (a.lo, b.lo);
+ s.hi += a.lo * b.hi + a.hi * b.lo;
+ return s;
+}
+
+cairo_uint64_t
+_cairo_uint64_lsl (cairo_uint64_t a, int shift)
+{
+ if (shift >= 32)
+ {
+ a.hi = a.lo;
+ a.lo = 0;
+ shift -= 32;
+ }
+ if (shift)
+ {
+ a.hi = a.hi << shift | a.lo >> (32 - shift);
+ a.lo = a.lo << shift;
+ }
+ return a;
+}
+
+cairo_uint64_t
+_cairo_uint64_rsl (cairo_uint64_t a, int shift)
+{
+ if (shift >= 32)
+ {
+ a.lo = a.hi;
+ a.hi = 0;
+ shift -= 32;
+ }
+ if (shift)
+ {
+ a.lo = a.lo >> shift | a.hi << (32 - shift);
+ a.hi = a.hi >> shift;
+ }
+ return a;
+}
+
+#define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n)))
+
+cairo_int64_t
+_cairo_uint64_rsa (cairo_int64_t a, int shift)
+{
+ if (shift >= 32)
+ {
+ a.lo = a.hi;
+ a.hi = _cairo_uint32_rsa (a.hi, 31);
+ shift -= 32;
+ }
+ if (shift)
+ {
+ a.lo = a.lo >> shift | a.hi << (32 - shift);
+ a.hi = _cairo_uint32_rsa (a.hi, shift);
+ }
+ return a;
+}
+
+int
+_cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b)
+{
+ return (a.hi < b.hi ||
+ (a.hi == b.hi && a.lo < b.lo));
+}
+
+int
+_cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b)
+{
+ return a.hi == b.hi && a.lo == b.lo;
+}
+
+int
+_cairo_int64_lt (cairo_int64_t a, cairo_int64_t b)
+{
+ if (_cairo_int64_negative (a) && !_cairo_int64_negative (b))
+ return 1;
+ if (!_cairo_int64_negative (a) && _cairo_int64_negative (b))
+ return 0;
+ return _cairo_uint64_lt (a, b);
+}
+
+int
+_cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b)
+{
+ if (a.hi < b.hi)
+ return -1;
+ else if (a.hi > b.hi)
+ return 1;
+ else if (a.lo < b.lo)
+ return -1;
+ else if (a.lo > b.lo)
+ return 1;
+ else
+ return 0;
+}
+
+int
+_cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b)
+{
+ if (_cairo_int64_negative (a) && !_cairo_int64_negative (b))
+ return -1;
+ if (!_cairo_int64_negative (a) && _cairo_int64_negative (b))
+ return 1;
+
+ return _cairo_uint64_cmp (a, b);
+}
+
+cairo_uint64_t
+_cairo_uint64_not (cairo_uint64_t a)
+{
+ a.lo = ~a.lo;
+ a.hi = ~a.hi;
+ return a;
+}
+
+cairo_uint64_t
+_cairo_uint64_negate (cairo_uint64_t a)
+{
+ a.lo = ~a.lo;
+ a.hi = ~a.hi;
+ if (++a.lo == 0)
+ ++a.hi;
+ return a;
+}
+
+/*
+ * Simple bit-at-a-time divide.
+ */
+cairo_uquorem64_t
+_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
+{
+ cairo_uquorem64_t qr;
+ cairo_uint64_t bit;
+ cairo_uint64_t quo;
+
+ bit = _cairo_uint32_to_uint64 (1);
+
+ /* normalize to make den >= num, but not overflow */
+ while (_cairo_uint64_lt (den, num) && (den.hi & 0x80000000) == 0)
+ {
+ bit = _cairo_uint64_lsl (bit, 1);
+ den = _cairo_uint64_lsl (den, 1);
+ }
+ quo = _cairo_uint32_to_uint64 (0);
+
+ /* generate quotient, one bit at a time */
+ while (bit.hi | bit.lo)
+ {
+ if (_cairo_uint64_le (den, num))
+ {
+ num = _cairo_uint64_sub (num, den);
+ quo = _cairo_uint64_add (quo, bit);
+ }
+ bit = _cairo_uint64_rsl (bit, 1);
+ den = _cairo_uint64_rsl (den, 1);
+ }
+ qr.quo = quo;
+ qr.rem = num;
+ return qr;
+}
+
+#endif /* !HAVE_UINT64_T */
+
+#if HAVE_UINT128_T
+cairo_uquorem128_t
+_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den)
+{
+ cairo_uquorem128_t qr;
+
+ qr.quo = num / den;
+ qr.rem = num % den;
+ return qr;
+}
+
+#else
+
+cairo_uint128_t
+_cairo_uint32_to_uint128 (uint32_t i)
+{
+ cairo_uint128_t q;
+
+ q.lo = _cairo_uint32_to_uint64 (i);
+ q.hi = _cairo_uint32_to_uint64 (0);
+ return q;
+}
+
+cairo_int128_t
+_cairo_int32_to_int128 (int32_t i)
+{
+ cairo_int128_t q;
+
+ q.lo = _cairo_int32_to_int64 (i);
+ q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0);
+ return q;
+}
+
+cairo_uint128_t
+_cairo_uint64_to_uint128 (cairo_uint64_t i)
+{
+ cairo_uint128_t q;
+
+ q.lo = i;
+ q.hi = _cairo_uint32_to_uint64 (0);
+ return q;
+}
+
+cairo_int128_t
+_cairo_int64_to_int128 (cairo_int64_t i)
+{
+ cairo_int128_t q;
+
+ q.lo = i;
+ q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0);
+ return q;
+}
+
+cairo_uint128_t
+_cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b)
+{
+ cairo_uint128_t s;
+
+ s.hi = _cairo_uint64_add (a.hi, b.hi);
+ s.lo = _cairo_uint64_add (a.lo, b.lo);
+ if (_cairo_uint64_lt (s.lo, a.lo))
+ s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1));
+ return s;
+}
+
+cairo_uint128_t
+_cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b)
+{
+ cairo_uint128_t s;
+
+ s.hi = _cairo_uint64_sub (a.hi, b.hi);
+ s.lo = _cairo_uint64_sub (a.lo, b.lo);
+ if (_cairo_uint64_gt (s.lo, a.lo))
+ s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1));
+ return s;
+}
+
+cairo_uint128_t
+_cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b)
+{
+ cairo_uint128_t s;
+ uint32_t ah, al, bh, bl;
+ cairo_uint64_t r0, r1, r2, r3;
+
+ al = uint64_lo32 (a);
+ ah = uint64_hi32 (a);
+ bl = uint64_lo32 (b);
+ bh = uint64_hi32 (b);
+
+ r0 = _cairo_uint32x32_64_mul (al, bl);
+ r1 = _cairo_uint32x32_64_mul (al, bh);
+ r2 = _cairo_uint32x32_64_mul (ah, bl);
+ r3 = _cairo_uint32x32_64_mul (ah, bh);
+
+ r1 = _cairo_uint64_add (r1, uint64_hi (r0)); /* no carry possible */
+ r1 = _cairo_uint64_add (r1, r2); /* but this can carry */
+ if (_cairo_uint64_lt (r1, r2)) /* check */
+ r3 = _cairo_uint64_add (r3, uint64_carry32);
+
+ s.hi = _cairo_uint64_add (r3, uint64_hi(r1));
+ s.lo = _cairo_uint64_add (uint64_shift32 (r1),
+ uint64_lo (r0));
+ return s;
+}
+
+cairo_int128_t
+_cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b)
+{
+ cairo_int128_t s;
+ s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a),
+ _cairo_int64_to_uint64(b));
+ if (_cairo_int64_negative (a))
+ s.hi = _cairo_uint64_sub (s.hi,
+ _cairo_int64_to_uint64 (b));
+ if (_cairo_int64_negative (b))
+ s.hi = _cairo_uint64_sub (s.hi,
+ _cairo_int64_to_uint64 (a));
+ return s;
+}
+
+cairo_uint128_t
+_cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b)
+{
+ cairo_uint128_t s;
+
+ s = _cairo_uint64x64_128_mul (a.lo, b.lo);
+ s.hi = _cairo_uint64_add (s.hi,
+ _cairo_uint64_mul (a.lo, b.hi));
+ s.hi = _cairo_uint64_add (s.hi,
+ _cairo_uint64_mul (a.hi, b.lo));
+ return s;
+}
+
+cairo_uint128_t
+_cairo_uint128_lsl (cairo_uint128_t a, int shift)
+{
+ if (shift >= 64)
+ {
+ a.hi = a.lo;
+ a.lo = _cairo_uint32_to_uint64 (0);
+ shift -= 64;
+ }
+ if (shift)
+ {
+ a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift),
+ _cairo_uint64_rsl (a.lo, (64 - shift)));
+ a.lo = _cairo_uint64_lsl (a.lo, shift);
+ }
+ return a;
+}
+
+cairo_uint128_t
+_cairo_uint128_rsl (cairo_uint128_t a, int shift)
+{
+ if (shift >= 64)
+ {
+ a.lo = a.hi;
+ a.hi = _cairo_uint32_to_uint64 (0);
+ shift -= 64;
+ }
+ if (shift)
+ {
+ a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift),
+ _cairo_uint64_lsl (a.hi, (64 - shift)));
+ a.hi = _cairo_uint64_rsl (a.hi, shift);
+ }
+ return a;
+}
+
+cairo_uint128_t
+_cairo_uint128_rsa (cairo_int128_t a, int shift)
+{
+ if (shift >= 64)
+ {
+ a.lo = a.hi;
+ a.hi = _cairo_uint64_rsa (a.hi, 64-1);
+ shift -= 64;
+ }
+ if (shift)
+ {
+ a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift),
+ _cairo_uint64_lsl (a.hi, (64 - shift)));
+ a.hi = _cairo_uint64_rsa (a.hi, shift);
+ }
+ return a;
+}
+
+int
+_cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b)
+{
+ return (_cairo_uint64_lt (a.hi, b.hi) ||
+ (_cairo_uint64_eq (a.hi, b.hi) &&
+ _cairo_uint64_lt (a.lo, b.lo)));
+}
+
+int
+_cairo_int128_lt (cairo_int128_t a, cairo_int128_t b)
+{
+ if (_cairo_int128_negative (a) && !_cairo_int128_negative (b))
+ return 1;
+ if (!_cairo_int128_negative (a) && _cairo_int128_negative (b))
+ return 0;
+ return _cairo_uint128_lt (a, b);
+}
+
+int
+_cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b)
+{
+ int cmp;
+
+ cmp = _cairo_uint64_cmp (a.hi, b.hi);
+ if (cmp)
+ return cmp;
+ return _cairo_uint64_cmp (a.lo, b.lo);
+}
+
+int
+_cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b)
+{
+ if (_cairo_int128_negative (a) && !_cairo_int128_negative (b))
+ return -1;
+ if (!_cairo_int128_negative (a) && _cairo_int128_negative (b))
+ return 1;
+
+ return _cairo_uint128_cmp (a, b);
+}
+
+int
+_cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b)
+{
+ return (_cairo_uint64_eq (a.hi, b.hi) &&
+ _cairo_uint64_eq (a.lo, b.lo));
+}
+
+#if HAVE_UINT64_T
+#define _cairo_msbset64(q) (q & ((uint64_t) 1 << 63))
+#else
+#define _cairo_msbset64(q) (q.hi & ((uint32_t) 1 << 31))
+#endif
+
+cairo_uquorem128_t
+_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den)
+{
+ cairo_uquorem128_t qr;
+ cairo_uint128_t bit;
+ cairo_uint128_t quo;
+
+ bit = _cairo_uint32_to_uint128 (1);
+
+ /* normalize to make den >= num, but not overflow */
+ while (_cairo_uint128_lt (den, num) && !_cairo_msbset64(den.hi))
+ {
+ bit = _cairo_uint128_lsl (bit, 1);
+ den = _cairo_uint128_lsl (den, 1);
+ }
+ quo = _cairo_uint32_to_uint128 (0);
+
+ /* generate quotient, one bit at a time */
+ while (_cairo_uint128_ne (bit, _cairo_uint32_to_uint128(0)))
+ {
+ if (_cairo_uint128_le (den, num))
+ {
+ num = _cairo_uint128_sub (num, den);
+ quo = _cairo_uint128_add (quo, bit);
+ }
+ bit = _cairo_uint128_rsl (bit, 1);
+ den = _cairo_uint128_rsl (den, 1);
+ }
+ qr.quo = quo;
+ qr.rem = num;
+ return qr;
+}
+
+cairo_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/src/cairo-win32.h b/src/cairo-win32.h
new file mode 100755
index 000000000..3d2e1c601
--- /dev/null
+++ b/src/cairo-win32.h
@@ -0,0 +1,112 @@
+/* -*- 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_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 cairo_surface_t *
+cairo_win32_surface_get_image (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 */
+
+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/src/cairo-xcb-connection-core.c b/src/cairo-xcb-connection-core.c
new file mode 100755
index 000000000..ae7c023e1
--- /dev/null
+++ b/src/cairo-xcb-connection-core.c
@@ -0,0 +1,314 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+
+#include <xcb/xcbext.h>
+
+xcb_pixmap_t
+_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection,
+ uint8_t depth,
+ xcb_drawable_t drawable,
+ uint16_t width,
+ uint16_t height)
+{
+ xcb_pixmap_t pixmap = _cairo_xcb_connection_get_xid (connection);
+
+ assert (width > 0);
+ assert (height > 0);
+ xcb_create_pixmap (connection->xcb_connection,
+ depth, pixmap, drawable,
+ width, height);
+ return pixmap;
+}
+
+void
+_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection,
+ xcb_pixmap_t pixmap)
+{
+ xcb_free_pixmap (connection->xcb_connection, pixmap);
+ _cairo_xcb_connection_put_xid (connection, pixmap);
+}
+
+xcb_gcontext_t
+_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection,
+ xcb_drawable_t drawable,
+ uint32_t value_mask,
+ uint32_t *values)
+{
+ xcb_gcontext_t gc = _cairo_xcb_connection_get_xid (connection);
+ xcb_create_gc (connection->xcb_connection, gc, drawable,
+ value_mask, values);
+ return gc;
+}
+
+void
+_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection,
+ xcb_gcontext_t gc)
+{
+ xcb_free_gc (connection->xcb_connection, gc);
+ _cairo_xcb_connection_put_xid (connection, gc);
+}
+
+void
+_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection,
+ xcb_gcontext_t gc,
+ uint32_t value_mask,
+ uint32_t *values)
+{
+ xcb_change_gc (connection->xcb_connection, gc,
+ value_mask, values);
+}
+
+void
+_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection,
+ xcb_drawable_t src,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ int16_t src_x,
+ int16_t src_y,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint16_t width,
+ uint16_t height)
+{
+ xcb_copy_area (connection->xcb_connection, src, dst, gc,
+ src_x, src_y, dst_x, dst_y, width, height);
+}
+
+void
+_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ uint32_t num_rectangles,
+ xcb_rectangle_t *rectangles)
+{
+ xcb_poly_fill_rectangle (connection->xcb_connection, dst, gc,
+ num_rectangles, rectangles);
+}
+
+void
+_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ uint16_t width,
+ uint16_t height,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ uint32_t stride,
+ void *data)
+{
+ const uint32_t req_size = 18;
+ uint32_t length = height * stride;
+ uint32_t len = (req_size + length) >> 2;
+
+ if (len < connection->maximum_request_length) {
+ xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
+ dst, gc, width, height, dst_x, dst_y, 0, depth,
+ length, data);
+ } else {
+ int rows = (connection->maximum_request_length - req_size - 4) / stride;
+ if (rows > 0) {
+ do {
+ if (rows > height)
+ rows = height;
+
+ length = rows * stride;
+
+ xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
+ dst, gc, width, rows, dst_x, dst_y, 0, depth, length, data);
+
+ height -= rows;
+ dst_y += rows;
+ data = (char *) data + length;
+ } while (height);
+ } else {
+ ASSERT_NOT_REACHED;
+ }
+ }
+}
+
+static void
+_cairo_xcb_connection_do_put_subimage (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ uint16_t cpp,
+ int stride,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ void *_data)
+{
+ xcb_protocol_request_t xcb_req = {
+ 0 /* count */,
+ 0 /* ext */,
+ XCB_PUT_IMAGE /* opcode */,
+ 1 /* isvoid (doesn't cause a reply) */
+ };
+ xcb_put_image_request_t req;
+ struct iovec vec_stack[CAIRO_STACK_ARRAY_LENGTH (struct iovec)];
+ struct iovec *vec = vec_stack;
+ uint32_t len = 0;
+ uint8_t *data = _data;
+ int n = 3;
+ /* Two extra entries are needed for xcb, two for us */
+ int entries_needed = height + 2 + 2;
+
+ req.format = XCB_IMAGE_FORMAT_Z_PIXMAP;
+ req.drawable = dst;
+ req.gc = gc;
+ req.width = width;
+ req.height = height;
+ req.dst_x = dst_x;
+ req.dst_y = dst_y;
+ req.left_pad = 0;
+ req.depth = depth;
+ req.pad0[0] = 0;
+ req.pad0[1] = 0;
+
+ if (entries_needed > ARRAY_LENGTH (vec_stack)) {
+ vec = _cairo_malloc_ab (entries_needed, sizeof (struct iovec));
+ if (unlikely (vec == NULL)) {
+ /* XXX loop over ARRAY_LENGTH (vec_stack) */
+ return;
+ }
+ }
+
+ data += src_y * stride + src_x * cpp;
+ /* vec[1] will be used in XCB if it has to use BigRequests or insert a sync,
+ * vec[0] is used if the internal queue needs to be flushed. */
+ vec[2].iov_base = (char *) &req;
+ vec[2].iov_len = sizeof (req);
+
+ /* Now comes the actual data */
+ while (height--) {
+ vec[n].iov_base = data;
+ vec[n].iov_len = cpp * width;
+ len += cpp * width;
+ data += stride;
+ n++;
+ }
+
+ /* And again some padding */
+ vec[n].iov_base = 0;
+ vec[n].iov_len = -len & 3;
+ n++;
+
+ /* For efficiency reasons, this functions writes the request "directly" to
+ * the xcb connection to avoid having to copy the data around. */
+ assert (n == entries_needed);
+ xcb_req.count = n - 2;
+ xcb_send_request (connection->xcb_connection, 0, &vec[2], &xcb_req);
+
+ if (vec != vec_stack)
+ free (vec);
+}
+
+void
+_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ uint16_t cpp,
+ int stride,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ void *_data)
+{
+ const uint32_t req_size = sizeof(xcb_put_image_request_t);
+ uint32_t length = height * cpp * width;
+ uint32_t len = (req_size + length) >> 2;
+
+ if (len < connection->maximum_request_length) {
+ _cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y,
+ width, height, cpp, stride, dst_x, dst_y, depth, _data);
+ } else {
+ int rows = (connection->maximum_request_length - req_size - 4) / (cpp * width);
+ if (rows > 0) {
+ do {
+ if (rows > height)
+ rows = height;
+
+ length = rows * cpp * width;
+
+ _cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y,
+ width, rows, cpp, stride, dst_x, dst_y, depth, _data);
+
+ height -= rows;
+ dst_y += rows;
+ _data = (char *) _data + stride * rows;
+ } while (height);
+ } else {
+ ASSERT_NOT_REACHED;
+ }
+ }
+}
+
+cairo_status_t
+_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t src,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ xcb_get_image_reply_t **reply)
+{
+ xcb_generic_error_t *error;
+
+ *reply = xcb_get_image_reply (connection->xcb_connection,
+ xcb_get_image (connection->xcb_connection,
+ XCB_IMAGE_FORMAT_Z_PIXMAP,
+ src,
+ src_x, src_y,
+ width, height,
+ (uint32_t) -1),
+
+ &error);
+ if (error) {
+ free (error);
+
+ free (*reply);
+ *reply = NULL;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-xcb-connection-render.c b/src/cairo-xcb-connection-render.c
new file mode 100755
index 000000000..83f1d482e
--- /dev/null
+++ b/src/cairo-xcb-connection-render.c
@@ -0,0 +1,301 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+
+#include <xcb/xcbext.h>
+
+void
+_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_drawable_t drawable,
+ xcb_render_pictformat_t format,
+ uint32_t value_mask,
+ uint32_t *value_list)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_create_picture (connection->xcb_connection, picture, drawable,
+ format, value_mask, value_list);
+}
+
+void
+_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ uint32_t value_mask,
+ uint32_t *value_list)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_change_picture (connection->xcb_connection, picture,
+ value_mask, value_list);
+}
+
+void
+_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ int16_t clip_x_origin,
+ int16_t clip_y_origin,
+ uint32_t rectangles_len,
+ xcb_rectangle_t *rectangles)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_set_picture_clip_rectangles (connection->xcb_connection, picture,
+ clip_x_origin, clip_y_origin,
+ rectangles_len, rectangles);
+}
+
+void
+_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_free_picture (connection->xcb_connection, picture);
+ _cairo_xcb_connection_put_xid (connection, picture);
+}
+
+void
+_cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t mask,
+ xcb_render_picture_t dst,
+ int16_t src_x,
+ int16_t src_y,
+ int16_t mask_x,
+ int16_t mask_y,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint16_t width,
+ uint16_t height)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE);
+ xcb_render_composite (connection->xcb_connection, op, src, mask, dst,
+ src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
+}
+
+void
+_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t traps_len,
+ xcb_render_trapezoid_t *traps)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS);
+ xcb_render_trapezoids (connection->xcb_connection, op, src, dst,
+ mask_format, src_x, src_y, traps_len, traps);
+}
+
+void
+_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t id,
+ xcb_render_pictformat_t format)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_create_glyph_set (connection->xcb_connection, id, format);
+}
+
+void
+_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t glyphset)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_free_glyph_set (connection->xcb_connection, glyphset);
+ _cairo_xcb_connection_put_xid (connection, glyphset);
+}
+
+void
+_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t glyphset,
+ uint32_t num_glyphs,
+ uint32_t *glyphs_id,
+ xcb_render_glyphinfo_t *glyphs,
+ uint32_t data_len,
+ uint8_t *data)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_add_glyphs (connection->xcb_connection, glyphset, num_glyphs,
+ glyphs_id, glyphs, data_len, data);
+}
+
+void
+_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t glyphset,
+ uint32_t num_glyphs,
+ xcb_render_glyph_t *glyphs)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_free_glyphs (connection->xcb_connection, glyphset, num_glyphs, glyphs);
+}
+
+void
+_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ xcb_render_glyphset_t glyphset,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t glyphcmds_len,
+ uint8_t *glyphcmds)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_composite_glyphs_8 (connection->xcb_connection, op, src, dst, mask_format,
+ glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
+}
+
+void
+_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ xcb_render_glyphset_t glyphset,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t glyphcmds_len,
+ uint8_t *glyphcmds)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_composite_glyphs_16 (connection->xcb_connection, op, src, dst, mask_format,
+ glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
+}
+
+void
+_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ xcb_render_glyphset_t glyphset,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t glyphcmds_len,
+ uint8_t *glyphcmds)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_RENDER);
+ xcb_render_composite_glyphs_32 (connection->xcb_connection, op, src, dst, mask_format,
+ glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
+}
+
+void
+_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t dst,
+ xcb_render_color_t color,
+ uint32_t num_rects,
+ xcb_rectangle_t *rects)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES);
+ xcb_render_fill_rectangles (connection->xcb_connection, op, dst, color,
+ num_rects, rects);
+}
+
+void
+_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_transform_t *transform)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM);
+ xcb_render_set_picture_transform (connection->xcb_connection, picture, *transform);
+}
+
+void
+_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ uint16_t filter_len,
+ char *filter,
+ uint32_t values_len,
+ xcb_render_fixed_t *values)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILTERS);
+ xcb_render_set_picture_filter (connection->xcb_connection, picture,
+ filter_len, filter, values_len, values);
+}
+
+void
+_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_color_t color)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
+ xcb_render_create_solid_fill (connection->xcb_connection, picture, color);
+}
+
+void
+_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_pointfix_t p1,
+ xcb_render_pointfix_t p2,
+ uint32_t num_stops,
+ xcb_render_fixed_t *stops,
+ xcb_render_color_t *colors)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
+ xcb_render_create_linear_gradient (connection->xcb_connection, picture,
+ p1, p2, num_stops, stops, colors);
+}
+
+void
+_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_pointfix_t inner,
+ xcb_render_pointfix_t outer,
+ xcb_render_fixed_t inner_radius,
+ xcb_render_fixed_t outer_radius,
+ uint32_t num_stops,
+ xcb_render_fixed_t *stops,
+ xcb_render_color_t *colors)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
+ xcb_render_create_radial_gradient (connection->xcb_connection, picture,
+ inner, outer, inner_radius, outer_radius,
+ num_stops, stops, colors);
+}
+
+void
+_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_pointfix_t center,
+ xcb_render_fixed_t angle,
+ uint32_t num_stops,
+ xcb_render_fixed_t *stops,
+ xcb_render_color_t *colors)
+{
+ assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
+ xcb_render_create_conical_gradient (connection->xcb_connection, picture,
+ center, angle, num_stops, stops, colors);
+}
diff --git a/src/cairo-xcb-connection-shm.c b/src/cairo-xcb-connection-shm.c
new file mode 100755
index 000000000..8c1d50698
--- /dev/null
+++ b/src/cairo-xcb-connection-shm.c
@@ -0,0 +1,117 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+
+#include "cairo-xcb-private.h"
+
+#include <xcb/xcbext.h>
+#include <xcb/shm.h>
+
+uint32_t
+_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection,
+ uint32_t id,
+ cairo_bool_t readonly)
+{
+ uint32_t segment = _cairo_xcb_connection_get_xid (connection);
+ assert (connection->flags & CAIRO_XCB_HAS_SHM);
+ xcb_shm_attach (connection->xcb_connection, segment, id, readonly);
+ return segment;
+}
+
+void
+_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ uint16_t total_width,
+ uint16_t total_height,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ uint32_t shm,
+ uint32_t offset)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_SHM);
+ xcb_shm_put_image (connection->xcb_connection, dst, gc, total_width, total_height,
+ src_x, src_y, width, height, dst_x, dst_y, depth,
+ XCB_IMAGE_FORMAT_Z_PIXMAP, 0, shm, offset);
+}
+
+cairo_status_t
+_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t src,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ uint32_t shmseg,
+ uint32_t offset)
+{
+ xcb_shm_get_image_reply_t *reply;
+ xcb_generic_error_t *error;
+
+ assert (connection->flags & CAIRO_XCB_HAS_SHM);
+ reply = xcb_shm_get_image_reply (connection->xcb_connection,
+ xcb_shm_get_image (connection->xcb_connection,
+ src,
+ src_x, src_y,
+ width, height,
+ (uint32_t) -1,
+ XCB_IMAGE_FORMAT_Z_PIXMAP,
+ shmseg, offset),
+ &error);
+ free (reply);
+
+ if (error) {
+ /* an error here should be impossible */
+ free (error);
+ return _cairo_error (CAIRO_STATUS_READ_ERROR);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection,
+ uint32_t segment)
+{
+ assert (connection->flags & CAIRO_XCB_HAS_SHM);
+ xcb_shm_detach (connection->xcb_connection, segment);
+ _cairo_xcb_connection_put_xid (connection, segment);
+}
+
+#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */
diff --git a/src/cairo-xcb-connection.c b/src/cairo-xcb-connection.c
new file mode 100755
index 000000000..78bc9a101
--- /dev/null
+++ b/src/cairo-xcb-connection.c
@@ -0,0 +1,985 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Authors:
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+#include "cairo-hash-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-inline.h"
+
+#include <xcb/xcbext.h>
+#include <xcb/bigreq.h>
+#include <errno.h>
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <xcb/shm.h>
+#endif
+
+typedef struct _cairo_xcb_xrender_format {
+ cairo_hash_entry_t key;
+ xcb_render_pictformat_t xrender_format;
+} cairo_xcb_xrender_format_t;
+
+typedef struct _cairo_xcb_xid {
+ cairo_list_t link;
+ uint32_t xid;
+} cairo_xcb_xid_t;
+
+#define XCB_RENDER_AT_LEAST(V, major, minor) \
+ (((V)->major_version > major) || \
+ (((V)->major_version == major) && ((V)->minor_version >= minor)))
+
+#define XCB_RENDER_HAS_CREATE_PICTURE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
+#define XCB_RENDER_HAS_COMPOSITE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
+#define XCB_RENDER_HAS_COMPOSITE_TEXT(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
+
+#define XCB_RENDER_HAS_FILL_RECTANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 1)
+
+#define XCB_RENDER_HAS_DISJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
+#define XCB_RENDER_HAS_CONJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
+
+#define XCB_RENDER_HAS_TRAPEZOIDS(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
+#define XCB_RENDER_HAS_TRIANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
+#define XCB_RENDER_HAS_TRISTRIP(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
+#define XCB_RENDER_HAS_TRIFAN(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
+
+#define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
+#define XCB_RENDER_HAS_FILTERS(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
+
+#define XCB_RENDER_HAS_EXTENDED_REPEAT(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
+#define XCB_RENDER_HAS_GRADIENTS(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
+
+#define XCB_RENDER_HAS_PDF_OPERATORS(surface) XCB_RENDER_AT_LEAST((surface), 0, 11)
+
+static cairo_list_t connections;
+
+static cairo_status_t
+_cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection,
+ const xcb_render_query_pict_formats_reply_t *formats)
+{
+ xcb_render_pictscreen_iterator_t screens;
+ xcb_render_pictdepth_iterator_t depths;
+ xcb_render_pictvisual_iterator_t visuals;
+
+ for (screens = xcb_render_query_pict_formats_screens_iterator (formats);
+ screens.rem;
+ xcb_render_pictscreen_next (&screens))
+ {
+ for (depths = xcb_render_pictscreen_depths_iterator (screens.data);
+ depths.rem;
+ xcb_render_pictdepth_next (&depths))
+ {
+ for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data);
+ visuals.rem;
+ xcb_render_pictvisual_next (&visuals))
+ {
+ cairo_xcb_xrender_format_t *f;
+ cairo_status_t status;
+
+ f = malloc (sizeof (cairo_xcb_xrender_format_t));
+ if (unlikely (f == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ f->key.hash = visuals.data->visual;
+ f->xrender_format = visuals.data->format;
+ status = _cairo_hash_table_insert (connection->visual_to_xrender_format,
+ &f->key);
+ if (unlikely (status))
+ return status;
+ }
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0
+static xcb_format_t *
+find_format_for_depth (const xcb_setup_t *setup, uint8_t depth)
+{
+ xcb_format_t *fmt = xcb_setup_pixmap_formats (setup);
+ xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup);
+
+ for (; fmt != fmtend; ++fmt)
+ if (fmt->depth == depth)
+ return fmt;
+
+ return 0;
+}
+#endif
+
+static cairo_status_t
+_cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection,
+ const xcb_render_query_pict_formats_reply_t *formats)
+{
+ xcb_render_pictforminfo_iterator_t i;
+ cairo_status_t status;
+
+ for (i = xcb_render_query_pict_formats_formats_iterator (formats);
+ i.rem;
+ xcb_render_pictforminfo_next (&i))
+ {
+ cairo_format_masks_t masks;
+ pixman_format_code_t pixman_format;
+
+ if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT)
+ continue;
+
+ masks.alpha_mask =
+ (unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift;
+ masks.red_mask =
+ (unsigned long) i.data->direct.red_mask << i.data->direct.red_shift;
+ masks.green_mask =
+ (unsigned long) i.data->direct.green_mask << i.data->direct.green_shift;
+ masks.blue_mask =
+ (unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift;
+ masks.bpp = i.data->depth;
+
+ if (_pixman_format_from_masks (&masks, &pixman_format)) {
+ cairo_hash_entry_t key;
+
+ key.hash = pixman_format;
+ if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) {
+ cairo_xcb_xrender_format_t *f;
+
+ f = malloc (sizeof (cairo_xcb_xrender_format_t));
+ if (unlikely (f == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ f->key.hash = pixman_format;
+ f->xrender_format = i.data->id;
+ status = _cairo_hash_table_insert (connection->xrender_formats,
+ &f->key);
+ if (unlikely (status))
+ return status;
+
+#if 0
+ printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n",
+ i.data->id,
+ masks.alpha_mask,
+ masks.red_mask,
+ masks.green_mask,
+ masks.blue_mask,
+ masks.bpp,
+ pixman_format,
+ PIXMAN_FORMAT_DEPTH(pixman_format),
+ PIXMAN_FORMAT_BPP(pixman_format));
+#endif
+ }
+ }
+ }
+
+ status = _cairo_xcb_connection_find_visual_formats (connection, formats);
+ if (unlikely (status))
+ return status;
+
+ connection->standard_formats[CAIRO_FORMAT_A1] =
+ _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1);
+
+ connection->standard_formats[CAIRO_FORMAT_A8] =
+ _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8);
+
+ connection->standard_formats[CAIRO_FORMAT_RGB24] =
+ _cairo_xcb_connection_get_xrender_format (connection,
+ PIXMAN_FORMAT (24,
+ PIXMAN_TYPE_ARGB,
+ 0, 8, 8, 8));
+ if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) {
+ connection->standard_formats[CAIRO_FORMAT_RGB24] =
+ _cairo_xcb_connection_get_xrender_format (connection,
+ PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR,
+ 0, 8, 8, 8));
+ }
+
+ connection->standard_formats[CAIRO_FORMAT_ARGB32] =
+ _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8);
+ if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) {
+ connection->standard_formats[CAIRO_FORMAT_ARGB32] =
+ _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * We require support for depth 1, 8, 24 and 32 pixmaps
+ */
+#define DEPTH_MASK(d) (1 << ((d) - 1))
+#define REQUIRED_DEPTHS (DEPTH_MASK(1) | \
+ DEPTH_MASK(8) | \
+ DEPTH_MASK(24) | \
+ DEPTH_MASK(32))
+static cairo_bool_t
+pixmap_depths_usable (cairo_xcb_connection_t *connection,
+ uint32_t missing,
+ xcb_drawable_t root)
+{
+ xcb_connection_t *c = connection->xcb_connection;
+ xcb_void_cookie_t create_cookie[32];
+ xcb_pixmap_t pixmap;
+ cairo_bool_t success = TRUE;
+ int depth, i, j;
+
+ pixmap = _cairo_xcb_connection_get_xid (connection);
+
+ for (depth = 1, i = 0; depth <= 32; depth++) {
+ if (missing & DEPTH_MASK(depth)) {
+ create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1);
+ xcb_free_pixmap (c, pixmap);
+ if (!create_cookie[i].sequence) {
+ success = FALSE;
+ break;
+ }
+ i++;
+ }
+ }
+
+ for (j = 0; j < i; j++) {
+ xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]);
+ success &= create_error == NULL;
+ free (create_error);
+ }
+
+ _cairo_xcb_connection_put_xid (connection, pixmap);
+
+ return success;
+}
+
+static cairo_bool_t
+has_required_depths (cairo_xcb_connection_t *connection)
+{
+ xcb_screen_iterator_t screens;
+
+ for (screens = xcb_setup_roots_iterator (connection->root);
+ screens.rem;
+ xcb_screen_next (&screens))
+ {
+ xcb_depth_iterator_t depths;
+ uint32_t missing = REQUIRED_DEPTHS;
+
+ for (depths = xcb_screen_allowed_depths_iterator (screens.data);
+ depths.rem;
+ xcb_depth_next (&depths))
+ {
+ missing &= ~DEPTH_MASK (depths.data->depth);
+ }
+ if (missing == 0)
+ continue;
+
+ /*
+ * Ok, this is ugly. It should be sufficient at this
+ * point to just return false, but Xinerama is broken at
+ * this point and only advertises depths which have an
+ * associated visual. Of course, the other depths still
+ * work, but the only way to find out is to try them.
+ */
+ if (! pixmap_depths_usable (connection, missing, screens.data->root))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static xcb_render_query_version_reply_t *
+_render_restrict_env(xcb_render_query_version_reply_t *version)
+{
+ const char *env;
+
+ if (version == NULL)
+ return NULL;
+
+ env = getenv ("CAIRO_DEBUG");
+ if (env != NULL)
+ env = strstr (env, "xrender-version=");
+ if (env != NULL) {
+ int max_render_major, max_render_minor;
+
+ env += sizeof ("xrender-version=") - 1;
+ if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
+ max_render_major = max_render_minor = -1;
+
+ if (max_render_major < 0 || max_render_minor < 0) {
+ free (version);
+ return NULL;
+ }
+
+ if (max_render_major < (int) version->major_version ||
+ (max_render_major == (int) version->major_version &&
+ max_render_minor < (int) version->minor_version))
+ {
+ version->major_version = max_render_major;
+ version->minor_version = max_render_minor;
+ }
+ }
+
+ return version;
+}
+
+static cairo_status_t
+_cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection)
+{
+ xcb_connection_t *c = connection->xcb_connection;
+ xcb_render_query_version_cookie_t version_cookie;
+ xcb_render_query_pict_formats_cookie_t formats_cookie;
+ xcb_render_query_version_reply_t *version;
+ xcb_render_query_pict_formats_reply_t *formats;
+ cairo_status_t status;
+ cairo_bool_t present;
+
+ version_cookie = xcb_render_query_version (c, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION);
+ formats_cookie = xcb_render_query_pict_formats (c);
+
+ present = has_required_depths (connection);
+ version = xcb_render_query_version_reply (c, version_cookie, 0);
+ formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0);
+
+ version = _render_restrict_env (version);
+
+ if (! present || version == NULL || formats == NULL) {
+ free (version);
+ free (formats);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* always true if the extension is present (i.e. >= 0.0) */
+ connection->flags |= CAIRO_XCB_HAS_RENDER;
+ connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE;
+ connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS;
+
+ if (XCB_RENDER_HAS_FILL_RECTANGLES (version))
+ connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
+
+ if (XCB_RENDER_HAS_TRAPEZOIDS (version))
+ connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
+
+ if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version))
+ connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
+
+ if (XCB_RENDER_HAS_FILTERS (version))
+ connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS;
+
+ if (XCB_RENDER_HAS_PDF_OPERATORS (version))
+ connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
+
+ if (XCB_RENDER_HAS_EXTENDED_REPEAT (version))
+ connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
+
+ if (XCB_RENDER_HAS_GRADIENTS (version))
+ connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS;
+
+ free (version);
+
+ status = _cairo_xcb_connection_parse_xrender_formats (connection, formats);
+ free (formats);
+
+ return status;
+}
+
+#if 0
+static void
+_cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection)
+{
+ xcb_connection_t *c = connection->xcb_connection;
+ xcb_cairo_query_version_reply_t *version;
+
+ version = xcb_cairo_query_version_reply (c,
+ xcb_cairo_query_version (c, 0, 0),
+ 0);
+
+ free (version);
+}
+#endif
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+static cairo_bool_t
+can_use_shm (cairo_xcb_connection_t *connection)
+{
+ cairo_bool_t success = TRUE;
+ xcb_connection_t *c = connection->xcb_connection;
+ xcb_void_cookie_t cookie[2];
+ xcb_generic_error_t *error;
+ int shmid;
+ uint32_t shmseg;
+ void *ptr;
+
+ shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
+ if (shmid == -1)
+ return FALSE;
+
+ ptr = shmat (shmid, NULL, 0);
+ if (ptr == (char *) -1) {
+ shmctl (shmid, IPC_RMID, NULL);
+ return FALSE;
+ }
+
+ shmseg = _cairo_xcb_connection_get_xid (connection);
+ cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE);
+ cookie[1] = xcb_shm_detach_checked (c, shmseg);
+ _cairo_xcb_connection_put_xid (connection, shmseg);
+
+ error = xcb_request_check (c, cookie[0]);
+ if (error != NULL)
+ success = FALSE;
+
+ error = xcb_request_check (c, cookie[1]);
+ if (error != NULL)
+ success = FALSE;
+
+ shmctl (shmid, IPC_RMID, NULL);
+ shmdt (ptr);
+
+ return success;
+}
+
+static void
+_cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection)
+{
+ xcb_connection_t *c = connection->xcb_connection;
+ xcb_shm_query_version_reply_t *version;
+
+ version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0);
+ if (version == NULL)
+ return;
+
+ free (version);
+
+ if (can_use_shm (connection))
+ connection->flags |= CAIRO_XCB_HAS_SHM;
+}
+#endif
+
+static cairo_status_t
+_device_flush (void *device)
+{
+ cairo_xcb_connection_t *connection = device;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&connection->device);
+ if (unlikely (status))
+ return status;
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ _cairo_xcb_connection_shm_mem_pools_flush (connection);
+#endif
+
+ xcb_flush (connection->xcb_connection);
+
+ cairo_device_release (&connection->device);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_pluck_xrender_format (void *entry,
+ void *closure)
+{
+ _cairo_hash_table_remove (closure, entry);
+ free (entry);
+}
+
+static void
+_device_finish (void *device)
+{
+ cairo_xcb_connection_t *connection = device;
+ cairo_bool_t was_cached = FALSE;
+
+ if (! cairo_list_is_empty (&connection->link)) {
+ CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
+ cairo_list_del (&connection->link);
+ CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
+ was_cached = TRUE;
+ }
+
+ while (! cairo_list_is_empty (&connection->fonts)) {
+ cairo_xcb_font_t *font;
+
+ font = cairo_list_first_entry (&connection->fonts,
+ cairo_xcb_font_t,
+ link);
+ _cairo_xcb_font_close (font);
+ }
+
+ while (! cairo_list_is_empty (&connection->screens)) {
+ cairo_xcb_screen_t *screen;
+
+ screen = cairo_list_first_entry (&connection->screens,
+ cairo_xcb_screen_t,
+ link);
+ _cairo_xcb_screen_finish (screen);
+ }
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ /* _cairo_xcb_screen_finish finishes surfaces. If any of those surfaces had
+ * a fallback image, we might have done a SHM PutImage. */
+ _cairo_xcb_connection_shm_mem_pools_flush (connection);
+#endif
+
+ if (was_cached)
+ cairo_device_destroy (device);
+}
+
+static void
+_device_destroy (void *device)
+{
+ cairo_xcb_connection_t *connection = device;
+
+ _cairo_hash_table_foreach (connection->xrender_formats,
+ _pluck_xrender_format, connection->xrender_formats);
+ _cairo_hash_table_destroy (connection->xrender_formats);
+
+ _cairo_hash_table_foreach (connection->visual_to_xrender_format,
+ _pluck_xrender_format,
+ connection->visual_to_xrender_format);
+ _cairo_hash_table_destroy (connection->visual_to_xrender_format);
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ _cairo_xcb_connection_shm_mem_pools_fini (connection);
+#endif
+ _cairo_freepool_fini (&connection->shm_info_freelist);
+
+ _cairo_freepool_fini (&connection->xid_pool);
+
+ CAIRO_MUTEX_FINI (connection->shm_mutex);
+ CAIRO_MUTEX_FINI (connection->screens_mutex);
+
+ free (connection);
+}
+
+static const cairo_device_backend_t _cairo_xcb_device_backend = {
+ CAIRO_DEVICE_TYPE_XCB,
+
+ NULL, NULL, /* lock, unlock */
+
+ _device_flush,
+ _device_finish,
+ _device_destroy,
+};
+
+cairo_xcb_connection_t *
+_cairo_xcb_connection_get (xcb_connection_t *xcb_connection)
+{
+ cairo_xcb_connection_t *connection;
+ const xcb_query_extension_reply_t *ext;
+ cairo_status_t status;
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
+ if (connections.next == NULL) {
+ /* XXX _cairo_init () */
+ cairo_list_init (&connections);
+ }
+
+ cairo_list_foreach_entry (connection,
+ cairo_xcb_connection_t,
+ &connections,
+ link)
+ {
+ if (connection->xcb_connection == xcb_connection) {
+ /* Maintain MRU order. */
+ if (connections.next != &connection->link)
+ cairo_list_move (&connection->link, &connections);
+
+ goto unlock;
+ }
+ }
+
+ connection = malloc (sizeof (cairo_xcb_connection_t));
+ if (unlikely (connection == NULL))
+ goto unlock;
+
+ _cairo_device_init (&connection->device, &_cairo_xcb_device_backend);
+
+ connection->xcb_connection = xcb_connection;
+
+ cairo_list_init (&connection->fonts);
+ cairo_list_init (&connection->screens);
+ cairo_list_init (&connection->link);
+ connection->xrender_formats = _cairo_hash_table_create (NULL);
+ if (connection->xrender_formats == NULL) {
+ CAIRO_MUTEX_FINI (connection->device.mutex);
+ free (connection);
+ connection = NULL;
+ goto unlock;
+ }
+
+ connection->visual_to_xrender_format = _cairo_hash_table_create (NULL);
+ if (connection->visual_to_xrender_format == NULL) {
+ _cairo_hash_table_destroy (connection->xrender_formats);
+ CAIRO_MUTEX_FINI (connection->device.mutex);
+ free (connection);
+ connection = NULL;
+ goto unlock;
+ }
+
+ cairo_list_init (&connection->free_xids);
+ _cairo_freepool_init (&connection->xid_pool,
+ sizeof (cairo_xcb_xid_t));
+
+ cairo_list_init (&connection->shm_pools);
+ cairo_list_init (&connection->shm_pending);
+ _cairo_freepool_init (&connection->shm_info_freelist,
+ sizeof (cairo_xcb_shm_info_t));
+
+ connection->maximum_request_length =
+ xcb_get_maximum_request_length (xcb_connection);
+
+ CAIRO_MUTEX_INIT (connection->shm_mutex);
+ CAIRO_MUTEX_INIT (connection->screens_mutex);
+
+ CAIRO_MUTEX_LOCK (connection->device.mutex);
+
+ connection->flags = 0;
+ connection->force_precision = -1;
+
+ xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id);
+ xcb_prefetch_extension_data (xcb_connection, &xcb_render_id);
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id);
+#endif
+#if 0
+ xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id);
+#endif
+
+ xcb_prefetch_maximum_request_length (xcb_connection);
+
+ connection->root = xcb_get_setup (xcb_connection);
+ if (connection->root == NULL) {
+ _cairo_hash_table_destroy (connection->xrender_formats);
+ CAIRO_MUTEX_FINI (connection->device.mutex);
+ _cairo_xcb_connection_destroy (connection);
+ connection = NULL;
+ goto unlock;
+ }
+
+ connection->render = NULL;
+ ext = xcb_get_extension_data (xcb_connection, &xcb_render_id);
+ if (ext != NULL && ext->present) {
+ status = _cairo_xcb_connection_query_render (connection);
+ if (unlikely (status)) {
+ CAIRO_MUTEX_UNLOCK (connection->device.mutex);
+ _cairo_xcb_connection_destroy (connection);
+ connection = NULL;
+ goto unlock;
+ }
+
+ connection->render = ext;
+ }
+
+#if 0
+ ext = xcb_get_extension_data (connection, &xcb_cairo_id);
+ if (ext != NULL && ext->present)
+ _cairo_xcb_connection_query_cairo (connection);
+#endif
+
+ connection->shm = NULL;
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id);
+ if (ext != NULL && ext->present) {
+ _cairo_xcb_connection_query_shm (connection);
+ connection->shm = ext;
+ }
+#endif
+
+ connection->original_flags = connection->flags;
+
+ CAIRO_MUTEX_UNLOCK (connection->device.mutex);
+
+ cairo_list_add (&connection->link, &connections);
+unlock:
+ CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
+
+ return connection;
+}
+
+xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
+ pixman_format_code_t pixman_format)
+{
+ cairo_hash_entry_t key;
+ cairo_xcb_xrender_format_t *format;
+
+ key.hash = pixman_format;
+ format = _cairo_hash_table_lookup (connection->xrender_formats, &key);
+ return format ? format->xrender_format : XCB_NONE;
+}
+
+xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
+ const xcb_visualid_t visual)
+{
+ cairo_hash_entry_t key;
+ cairo_xcb_xrender_format_t *format;
+
+ key.hash = visual;
+ format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key);
+ return format ? format->xrender_format : XCB_NONE;
+}
+
+void
+_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection,
+ uint32_t xid)
+{
+ cairo_xcb_xid_t *cache;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
+ cache = _cairo_freepool_alloc (&connection->xid_pool);
+ if (likely (cache != NULL)) {
+ cache->xid = xid;
+ cairo_list_add (&cache->link, &connection->free_xids);
+ }
+}
+
+uint32_t
+_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection)
+{
+ uint32_t xid;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
+ if (! cairo_list_is_empty (&connection->free_xids)) {
+ cairo_xcb_xid_t *cache;
+
+ cache = cairo_list_first_entry (&connection->free_xids,
+ cairo_xcb_xid_t,
+ link);
+ xid = cache->xid;
+
+ cairo_list_del (&cache->link);
+ _cairo_freepool_free (&connection->xid_pool, cache);
+ } else {
+ xid = xcb_generate_id (connection->xcb_connection);
+ }
+
+ return xid;
+}
+
+/**
+ * cairo_xcb_device_get_connection:
+ * @device: a #cairo_device_t for the XCB backend
+ *
+ * Get the connection for the XCB device.
+ *
+ * Returns: the #xcb_connection_t for the connection
+ *
+ * Since: 1.12
+ **/
+xcb_connection_t *
+cairo_xcb_device_get_connection (cairo_device_t *device)
+{
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XCB)
+ return NULL;
+
+ return ((cairo_xcb_connection_t *)device)->xcb_connection;
+}
+
+/* public (debug) interface */
+
+/**
+ * cairo_xcb_device_debug_cap_xshm_version:
+ * @device: a #cairo_device_t for the XCB backend
+ * @major_version: major version to restrict to
+ * @minor_version: minor version to restrict to
+ *
+ * Restricts all future XCB surfaces for this devices to the specified version
+ * of the SHM extension. This function exists solely for debugging purpose.
+ * It let's you find out how cairo would behave with an older version of
+ * the SHM extension.
+ *
+ * Use the special values -1 and -1 for disabling the SHM extension.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
+ int major_version,
+ int minor_version)
+{
+ cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return;
+ }
+
+ /* First reset all the SHM flags to their original value. This works
+ * because we only ever clear bits after the connection was created.
+ */
+ connection->flags |= (connection->original_flags & CAIRO_XCB_SHM_MASK);
+
+ /* clear any flags that are inappropriate for the desired version */
+ if (major_version < 0 && minor_version < 0) {
+ connection->flags &= ~(CAIRO_XCB_HAS_SHM);
+ }
+}
+
+/**
+ * cairo_xcb_device_debug_cap_xrender_version:
+ * @device: a #cairo_device_t for the XCB backend
+ * @major_version: major version to restrict to
+ * @minor_version: minor version to restrict to
+ *
+ * Restricts all future XCB surfaces for this devices to the specified version
+ * of the RENDER extension. This function exists solely for debugging purpose.
+ * It let's you find out how cairo would behave with an older version of
+ * the RENDER extension.
+ *
+ * Use the special values -1 and -1 for disabling the RENDER extension.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
+ int major_version,
+ int minor_version)
+{
+ cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return;
+ }
+
+ /* First reset all the RENDER flags to their original value. This works
+ * because we only ever clear bits after the connection was created.
+ */
+ connection->flags |= (connection->original_flags & CAIRO_XCB_RENDER_MASK);
+
+ /* clear any flags that are inappropriate for the desired version */
+ if (major_version < 0 && minor_version < 0) {
+ connection->flags &= ~(CAIRO_XCB_HAS_RENDER |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
+ CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+ CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
+ CAIRO_XCB_RENDER_HAS_FILTERS |
+ CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
+ CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
+ CAIRO_XCB_RENDER_HAS_GRADIENTS);
+ } else {
+ xcb_render_query_version_reply_t version;
+
+ version.major_version = major_version;
+ version.minor_version = minor_version;
+
+ if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version))
+ connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
+
+ if (! XCB_RENDER_HAS_TRAPEZOIDS (&version))
+ connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
+
+ if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version))
+ connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
+
+ if (! XCB_RENDER_HAS_FILTERS (&version))
+ connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS;
+
+ if (! XCB_RENDER_HAS_PDF_OPERATORS (&version))
+ connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
+
+ if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version))
+ connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
+
+ if (! XCB_RENDER_HAS_GRADIENTS (&version))
+ connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS;
+ }
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_device_debug_cap_xrender_version);
+#endif
+
+/**
+ * cairo_xcb_device_debug_set_precision:
+ * @device: a #cairo_device_t for the XCB backend
+ * @precision: the precision to use
+ *
+ * Render supports two modes of precision when rendering trapezoids. Set
+ * the precision to the desired mode.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_xcb_device_debug_set_precision (cairo_device_t *device,
+ int precision)
+{
+ if (device == NULL || device->status)
+ return;
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return;
+ }
+
+ ((cairo_xcb_connection_t *) device)->force_precision = precision;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_device_debug_set_precision);
+#endif
+
+/**
+ * cairo_xcb_device_debug_get_precision:
+ * @device: a #cairo_device_t for the XCB backend
+ *
+ * Get the Xrender precision mode.
+ *
+ * Returns: the render precision mode
+ *
+ * Since: 1.12
+ **/
+int
+cairo_xcb_device_debug_get_precision (cairo_device_t *device)
+{
+ if (device == NULL || device->status)
+ return -1;
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return -1;
+ }
+
+ return ((cairo_xcb_connection_t *) device)->force_precision;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_device_debug_get_precision);
+#endif
diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h
new file mode 100755
index 000000000..d162d73d4
--- /dev/null
+++ b/src/cairo-xcb-private.h
@@ -0,0 +1,770 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributors(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_XCB_PRIVATE_H
+#define CAIRO_XCB_PRIVATE_H
+
+#include "cairo-xcb.h"
+
+#include "cairoint.h"
+
+#include "cairo-cache-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-mutex-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-spans-private.h"
+#include "cairo-surface-private.h"
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+#include <xcb/xcbext.h>
+#include <pixman.h>
+
+#define XLIB_COORD_MAX 32767
+
+/* maximum number of cached GC's */
+#define GC_CACHE_SIZE 4
+
+#define CAIRO_XCB_RENDER_AT_LEAST(major, minor) \
+ ((XCB_RENDER_MAJOR_VERSION > major) || \
+ ((XCB_RENDER_MAJOR_VERSION == major) && (XCB_RENDER_MINOR_VERSION >= minor)))
+
+typedef struct _cairo_xcb_connection cairo_xcb_connection_t;
+typedef struct _cairo_xcb_font cairo_xcb_font_t;
+typedef struct _cairo_xcb_screen cairo_xcb_screen_t;
+typedef struct _cairo_xcb_surface cairo_xcb_surface_t;
+typedef struct _cairo_xcb_picture cairo_xcb_picture_t;
+typedef struct _cairo_xcb_shm_mem_pool cairo_xcb_shm_mem_pool_t;
+typedef struct _cairo_xcb_shm_info cairo_xcb_shm_info_t;
+
+struct _cairo_xcb_shm_info {
+ cairo_xcb_connection_t *connection;
+ uint32_t shm;
+ uint32_t offset;
+ size_t size;
+ void *mem;
+ cairo_xcb_shm_mem_pool_t *pool;
+ xcb_get_input_focus_cookie_t sync;
+ cairo_list_t pending;
+};
+
+struct _cairo_xcb_surface {
+ cairo_surface_t base;
+ cairo_image_surface_t *fallback;
+ cairo_boxes_t fallback_damage;
+
+ cairo_xcb_connection_t *connection;
+ cairo_xcb_screen_t *screen;
+
+ xcb_drawable_t drawable;
+ cairo_bool_t owns_pixmap;
+
+ cairo_bool_t deferred_clear;
+ cairo_color_t deferred_clear_color;
+
+ int width;
+ int height;
+ int depth;
+
+ xcb_render_picture_t picture;
+ xcb_render_pictformat_t xrender_format;
+ pixman_format_code_t pixman_format;
+ uint32_t precision;
+
+ cairo_list_t link;
+};
+
+struct _cairo_xcb_picture {
+ cairo_surface_t base;
+
+ cairo_xcb_screen_t *screen;
+ xcb_render_picture_t picture;
+ xcb_render_pictformat_t xrender_format;
+ pixman_format_code_t pixman_format;
+
+ int width, height;
+
+ cairo_extend_t extend;
+ cairo_filter_t filter;
+ cairo_bool_t has_component_alpha;
+ xcb_render_transform_t transform;
+
+ int x0, y0;
+ int x, y;
+
+ cairo_list_t link;
+};
+
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+typedef struct _cairo_xlib_xcb_surface {
+ cairo_surface_t base;
+
+ cairo_xcb_surface_t *xcb;
+
+ /* original settings for query */
+ void *display;
+ void *screen;
+ void *visual;
+ void *format;
+} cairo_xlib_xcb_surface_t;
+#endif
+
+
+enum {
+ GLYPHSET_INDEX_ARGB32,
+ GLYPHSET_INDEX_A8,
+ GLYPHSET_INDEX_A1,
+ NUM_GLYPHSETS
+};
+
+typedef struct _cairo_xcb_font_glyphset_free_glyphs {
+ xcb_render_glyphset_t glyphset;
+ int glyph_count;
+ xcb_render_glyph_t glyph_indices[128];
+} cairo_xcb_font_glyphset_free_glyphs_t;
+
+typedef struct _cairo_xcb_font_glyphset_info {
+ xcb_render_glyphset_t glyphset;
+ cairo_format_t format;
+ xcb_render_pictformat_t xrender_format;
+ cairo_xcb_font_glyphset_free_glyphs_t *pending_free_glyphs;
+} cairo_xcb_font_glyphset_info_t;
+
+struct _cairo_xcb_font {
+ cairo_scaled_font_private_t base;
+ cairo_scaled_font_t *scaled_font;
+ cairo_xcb_connection_t *connection;
+ cairo_xcb_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS];
+ cairo_list_t link;
+};
+
+struct _cairo_xcb_screen {
+ cairo_xcb_connection_t *connection;
+
+ xcb_screen_t *xcb_screen;
+
+ xcb_gcontext_t gc[GC_CACHE_SIZE];
+ uint8_t gc_depths[GC_CACHE_SIZE];
+
+ cairo_surface_t *stock_colors[CAIRO_STOCK_NUM_COLORS];
+ struct {
+ cairo_surface_t *picture;
+ cairo_color_t color;
+ } solid_cache[16];
+ int solid_cache_size;
+
+ cairo_cache_t linear_pattern_cache;
+ cairo_cache_t radial_pattern_cache;
+ cairo_freelist_t pattern_cache_entry_freelist;
+
+ cairo_list_t link;
+ cairo_list_t surfaces;
+ cairo_list_t pictures;
+};
+
+struct _cairo_xcb_connection {
+ cairo_device_t device;
+
+ xcb_connection_t *xcb_connection;
+
+ xcb_render_pictformat_t standard_formats[5];
+ cairo_hash_table_t *xrender_formats;
+ cairo_hash_table_t *visual_to_xrender_format;
+
+ unsigned int maximum_request_length;
+ unsigned int flags;
+ unsigned int original_flags;
+
+ int force_precision;
+
+ const xcb_setup_t *root;
+ const xcb_query_extension_reply_t *render;
+ const xcb_query_extension_reply_t *shm;
+
+ cairo_list_t free_xids;
+ cairo_freepool_t xid_pool;
+
+ cairo_mutex_t shm_mutex;
+ cairo_list_t shm_pools;
+ cairo_list_t shm_pending;
+ cairo_freepool_t shm_info_freelist;
+
+ cairo_mutex_t screens_mutex;
+ cairo_list_t screens;
+
+ cairo_list_t fonts;
+
+ cairo_list_t link;
+};
+
+enum {
+ CAIRO_XCB_HAS_RENDER = 0x0001,
+ CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES = 0x0002,
+ CAIRO_XCB_RENDER_HAS_COMPOSITE = 0x0004,
+ CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS = 0x0008,
+ CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS = 0x0010,
+ CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM = 0x0020,
+ CAIRO_XCB_RENDER_HAS_FILTERS = 0x0040,
+ CAIRO_XCB_RENDER_HAS_PDF_OPERATORS = 0x0080,
+ CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT = 0x0100,
+ CAIRO_XCB_RENDER_HAS_GRADIENTS = 0x0200,
+
+ CAIRO_XCB_HAS_SHM = 0x80000000,
+
+ CAIRO_XCB_RENDER_MASK = CAIRO_XCB_HAS_RENDER |
+ CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
+ CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
+ CAIRO_XCB_RENDER_HAS_FILTERS |
+ CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
+ CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
+ CAIRO_XCB_RENDER_HAS_GRADIENTS,
+ CAIRO_XCB_SHM_MASK = CAIRO_XCB_HAS_SHM
+};
+
+#define CAIRO_XCB_SHM_SMALL_IMAGE 8192
+
+cairo_private extern const cairo_surface_backend_t _cairo_xcb_surface_backend;
+
+cairo_private cairo_xcb_connection_t *
+_cairo_xcb_connection_get (xcb_connection_t *connection);
+
+static inline cairo_xcb_connection_t *
+_cairo_xcb_connection_reference (cairo_xcb_connection_t *connection)
+{
+ return (cairo_xcb_connection_t *) cairo_device_reference (&connection->device);
+}
+
+cairo_private xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
+ pixman_format_code_t pixman_format);
+
+cairo_private xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
+ const xcb_visualid_t visual);
+
+static inline cairo_status_t cairo_warn
+_cairo_xcb_connection_acquire (cairo_xcb_connection_t *connection)
+{
+ return cairo_device_acquire (&connection->device);
+}
+
+cairo_private uint32_t
+_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection);
+
+cairo_private void
+_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection,
+ uint32_t xid);
+
+static inline void
+_cairo_xcb_connection_release (cairo_xcb_connection_t *connection)
+{
+ cairo_device_release (&connection->device);
+}
+
+static inline void
+_cairo_xcb_connection_destroy (cairo_xcb_connection_t *connection)
+{
+ cairo_device_destroy (&connection->device);
+}
+
+cairo_private cairo_int_status_t
+_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *display,
+ size_t size,
+ cairo_bool_t might_reuse,
+ cairo_xcb_shm_info_t **shm_info_out);
+
+cairo_private void
+_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info);
+
+cairo_private void
+_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection);
+
+cairo_private void
+_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection);
+
+cairo_private void
+_cairo_xcb_font_close (cairo_xcb_font_t *font);
+
+cairo_private cairo_xcb_screen_t *
+_cairo_xcb_screen_get (xcb_connection_t *connection,
+ xcb_screen_t *screen);
+
+cairo_private void
+_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen);
+
+cairo_private xcb_gcontext_t
+_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ int depth);
+
+cairo_private void
+_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc);
+
+cairo_private cairo_status_t
+_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
+ const cairo_linear_pattern_t *linear,
+ cairo_surface_t *picture);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen,
+ const cairo_linear_pattern_t *linear);
+
+cairo_private cairo_status_t
+_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen,
+ const cairo_radial_pattern_t *radial,
+ cairo_surface_t *picture);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
+ const cairo_radial_pattern_t *radial);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_surface_create_similar_image (void *abstrct_other,
+ cairo_format_t format,
+ int width,
+ int height);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ cairo_bool_t owns_pixmap,
+ pixman_format_code_t pixman_format,
+ xcb_render_pictformat_t xrender_format,
+ int width,
+ int height);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_xcb_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_render_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_render_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_render_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap);
+cairo_private void
+_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst);
+
+cairo_private cairo_status_t
+_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst,
+ const cairo_pattern_t *src_pattern,
+ const cairo_rectangle_int_t *extents,
+ const cairo_boxes_t *boxes);
+
+cairo_private cairo_status_t
+_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes);
+
+cairo_private xcb_pixmap_t
+_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection,
+ uint8_t depth,
+ xcb_drawable_t drawable,
+ uint16_t width,
+ uint16_t height);
+
+cairo_private void
+_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection,
+ xcb_pixmap_t pixmap);
+
+cairo_private xcb_gcontext_t
+_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection,
+ xcb_drawable_t drawable,
+ uint32_t value_mask,
+ uint32_t *values);
+
+cairo_private void
+_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection,
+ xcb_gcontext_t gc);
+
+cairo_private void
+_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection,
+ xcb_gcontext_t gc,
+ uint32_t value_mask,
+ uint32_t *values);
+
+cairo_private void
+_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection,
+ xcb_drawable_t src,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ int16_t src_x,
+ int16_t src_y,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint16_t width,
+ uint16_t height);
+
+cairo_private void
+_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ uint16_t width,
+ uint16_t height,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ uint32_t length,
+ void *data);
+
+cairo_private void
+_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ uint16_t cpp,
+ int stride,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ void *data);
+
+cairo_private cairo_status_t
+_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t src,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ xcb_get_image_reply_t **reply);
+
+cairo_private void
+_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ uint32_t num_rectangles,
+ xcb_rectangle_t *rectangles);
+
+cairo_private cairo_status_t
+_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection,
+ pixman_format_code_t pixman_format,
+ int width, int height,
+ cairo_image_surface_t **image_out,
+ cairo_xcb_shm_info_t **shm_info_out);
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+cairo_private uint32_t
+_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection,
+ uint32_t id,
+ cairo_bool_t readonly);
+
+cairo_private void
+_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ uint16_t total_width,
+ uint16_t total_height,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ uint32_t shm,
+ uint32_t offset);
+
+cairo_private cairo_status_t
+_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t src,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ uint32_t shmseg,
+ uint32_t offset);
+
+cairo_private void
+_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection,
+ uint32_t segment);
+#else
+static inline void
+_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
+ xcb_drawable_t dst,
+ xcb_gcontext_t gc,
+ uint16_t total_width,
+ uint16_t total_height,
+ int16_t src_x,
+ int16_t src_y,
+ uint16_t width,
+ uint16_t height,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint8_t depth,
+ uint32_t shm,
+ uint32_t offset)
+{
+ ASSERT_NOT_REACHED;
+}
+#endif
+
+cairo_private void
+_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_drawable_t drawable,
+ xcb_render_pictformat_t format,
+ uint32_t value_mask,
+ uint32_t *value_list);
+
+cairo_private void
+_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ uint32_t value_mask,
+ uint32_t *value_list);
+
+cairo_private void
+_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ int16_t clip_x_origin,
+ int16_t clip_y_origin,
+ uint32_t rectangles_len,
+ xcb_rectangle_t *rectangles);
+
+cairo_private void
+_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture);
+
+cairo_private void
+_cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t mask,
+ xcb_render_picture_t dst,
+ int16_t src_x,
+ int16_t src_y,
+ int16_t mask_x,
+ int16_t mask_y,
+ int16_t dst_x,
+ int16_t dst_y,
+ uint16_t width,
+ uint16_t height);
+
+cairo_private void
+_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t traps_len,
+ xcb_render_trapezoid_t *traps);
+
+cairo_private void
+_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t id,
+ xcb_render_pictformat_t format);
+
+cairo_private void
+_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t glyphset);
+
+cairo_private void
+_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t glyphset,
+ uint32_t num_glyphs,
+ uint32_t *glyphs_id,
+ xcb_render_glyphinfo_t *glyphs,
+ uint32_t data_len,
+ uint8_t *data);
+
+cairo_private void
+_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection,
+ xcb_render_glyphset_t glyphset,
+ uint32_t num_glyphs,
+ xcb_render_glyph_t *glyphs);
+
+cairo_private void
+_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ xcb_render_glyphset_t glyphset,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t glyphcmds_len,
+ uint8_t *glyphcmds);
+
+cairo_private void
+_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ xcb_render_glyphset_t glyphset,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t glyphcmds_len,
+ uint8_t *glyphcmds);
+
+cairo_private void
+_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ xcb_render_glyphset_t glyphset,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t glyphcmds_len,
+ uint8_t *glyphcmds);
+
+cairo_private void
+_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t dst,
+ xcb_render_color_t color,
+ uint32_t num_rects,
+ xcb_rectangle_t *rects);
+
+cairo_private void
+_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_transform_t *transform);
+
+cairo_private void
+_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ uint16_t filter_len,
+ char *filter,
+ uint32_t values_len,
+ xcb_render_fixed_t *values);
+
+cairo_private void
+_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_color_t color);
+
+cairo_private void
+_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_pointfix_t p1,
+ xcb_render_pointfix_t p2,
+ uint32_t num_stops,
+ xcb_render_fixed_t *stops,
+ xcb_render_color_t *colors);
+
+cairo_private void
+_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection,
+ xcb_render_picture_t picture,
+ xcb_render_pointfix_t inner,
+ xcb_render_pointfix_t outer,
+ xcb_render_fixed_t inner_radius,
+ xcb_render_fixed_t outer_radius,
+ uint32_t num_stops,
+ xcb_render_fixed_t *stops,
+ xcb_render_color_t *colors);
+
+cairo_private void
+_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *c,
+ xcb_render_picture_t picture,
+ xcb_render_pointfix_t center,
+ xcb_render_fixed_t angle,
+ uint32_t num_stops,
+ xcb_render_fixed_t *stops,
+ xcb_render_color_t *colors);
+
+cairo_private cairo_xcb_picture_t *
+_cairo_xcb_gaussian_filter (cairo_xcb_surface_t *target,
+ cairo_xcb_picture_t *orig_pict,
+ const cairo_pattern_t *pattern);
+
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_proto (cairo_xcb_surface_create);
+slim_hidden_proto (cairo_xcb_surface_create_for_bitmap);
+slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format);
+slim_hidden_proto (cairo_xcb_surface_set_size);
+slim_hidden_proto (cairo_xcb_surface_set_drawable);
+slim_hidden_proto (cairo_xcb_device_debug_get_precision);
+slim_hidden_proto_no_warn (cairo_xcb_device_debug_set_precision);
+slim_hidden_proto_no_warn (cairo_xcb_device_debug_cap_xrender_version);
+#endif
+
+#endif /* CAIRO_XCB_PRIVATE_H */
diff --git a/src/cairo-xcb-screen.c b/src/cairo-xcb-screen.c
new file mode 100755
index 000000000..2858d23fb
--- /dev/null
+++ b/src/cairo-xcb-screen.c
@@ -0,0 +1,364 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Authors:
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+#include "cairo-list-inline.h"
+
+struct pattern_cache_entry {
+ cairo_cache_entry_t key;
+ cairo_xcb_screen_t *screen;
+ cairo_pattern_union_t pattern;
+ cairo_surface_t *picture;
+};
+
+void
+_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen)
+{
+ int i;
+
+ CAIRO_MUTEX_LOCK (screen->connection->screens_mutex);
+ cairo_list_del (&screen->link);
+ CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex);
+
+ while (! cairo_list_is_empty (&screen->surfaces)) {
+ cairo_surface_t *surface;
+
+ surface = &cairo_list_first_entry (&screen->surfaces,
+ cairo_xcb_surface_t,
+ link)->base;
+
+ cairo_surface_finish (surface);
+ }
+
+ while (! cairo_list_is_empty (&screen->pictures)) {
+ cairo_surface_t *surface;
+
+ surface = &cairo_list_first_entry (&screen->pictures,
+ cairo_xcb_picture_t,
+ link)->base;
+
+ cairo_surface_finish (surface);
+ }
+
+ for (i = 0; i < screen->solid_cache_size; i++)
+ cairo_surface_destroy (screen->solid_cache[i].picture);
+
+ for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
+ cairo_surface_destroy (screen->stock_colors[i]);
+
+ for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
+ if (screen->gc_depths[i] != 0)
+ _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
+ }
+
+ _cairo_cache_fini (&screen->linear_pattern_cache);
+ _cairo_cache_fini (&screen->radial_pattern_cache);
+ _cairo_freelist_fini (&screen->pattern_cache_entry_freelist);
+
+ free (screen);
+}
+
+static cairo_bool_t
+_linear_pattern_cache_entry_equal (const void *A, const void *B)
+{
+ const struct pattern_cache_entry *a = A, *b = B;
+
+ return _cairo_linear_pattern_equal (&a->pattern.gradient.linear,
+ &b->pattern.gradient.linear);
+}
+
+static cairo_bool_t
+_radial_pattern_cache_entry_equal (const void *A, const void *B)
+{
+ const struct pattern_cache_entry *a = A, *b = B;
+
+ return _cairo_radial_pattern_equal (&a->pattern.gradient.radial,
+ &b->pattern.gradient.radial);
+}
+
+static void
+_pattern_cache_entry_destroy (void *closure)
+{
+ struct pattern_cache_entry *entry = closure;
+
+ _cairo_pattern_fini (&entry->pattern.base);
+ cairo_surface_destroy (entry->picture);
+ _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry);
+}
+
+cairo_xcb_screen_t *
+_cairo_xcb_screen_get (xcb_connection_t *xcb_connection,
+ xcb_screen_t *xcb_screen)
+{
+ cairo_xcb_connection_t *connection;
+ cairo_xcb_screen_t *screen;
+ cairo_status_t status;
+ int i;
+
+ connection = _cairo_xcb_connection_get (xcb_connection);
+ if (unlikely (connection == NULL))
+ return NULL;
+
+ CAIRO_MUTEX_LOCK (connection->screens_mutex);
+
+ cairo_list_foreach_entry (screen,
+ cairo_xcb_screen_t,
+ &connection->screens,
+ link)
+ {
+ if (screen->xcb_screen == xcb_screen) {
+ /* Maintain list in MRU order */
+ if (&screen->link != connection->screens.next)
+ cairo_list_move (&screen->link, &connection->screens);
+
+ goto unlock;
+ }
+ }
+
+ screen = malloc (sizeof (cairo_xcb_screen_t));
+ if (unlikely (screen == NULL))
+ goto unlock;
+
+ screen->connection = connection;
+ screen->xcb_screen = xcb_screen;
+
+ _cairo_freelist_init (&screen->pattern_cache_entry_freelist,
+ sizeof (struct pattern_cache_entry));
+ cairo_list_init (&screen->link);
+ cairo_list_init (&screen->surfaces);
+ cairo_list_init (&screen->pictures);
+
+ memset (screen->gc_depths, 0, sizeof (screen->gc_depths));
+ memset (screen->gc, 0, sizeof (screen->gc));
+
+ screen->solid_cache_size = 0;
+ for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
+ screen->stock_colors[i] = NULL;
+
+ status = _cairo_cache_init (&screen->linear_pattern_cache,
+ _linear_pattern_cache_entry_equal,
+ NULL,
+ _pattern_cache_entry_destroy,
+ 16);
+ if (unlikely (status))
+ goto error_screen;
+
+ status = _cairo_cache_init (&screen->radial_pattern_cache,
+ _radial_pattern_cache_entry_equal,
+ NULL,
+ _pattern_cache_entry_destroy,
+ 4);
+ if (unlikely (status))
+ goto error_linear;
+
+ cairo_list_add (&screen->link, &connection->screens);
+
+unlock:
+ CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
+
+ return screen;
+
+error_linear:
+ _cairo_cache_fini (&screen->linear_pattern_cache);
+error_screen:
+ CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
+ free (screen);
+
+ return NULL;
+}
+
+static xcb_gcontext_t
+_create_gc (cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable)
+{
+ uint32_t values[] = { 0 };
+
+ return _cairo_xcb_connection_create_gc (screen->connection, drawable,
+ XCB_GC_GRAPHICS_EXPOSURES,
+ values);
+}
+
+xcb_gcontext_t
+_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ int depth)
+{
+ int i;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+ for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
+ if (screen->gc_depths[i] == depth) {
+ screen->gc_depths[i] = 0;
+ return screen->gc[i];
+ }
+ }
+
+ return _create_gc (screen, drawable);
+}
+
+void
+_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc)
+{
+ int i;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+ for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
+ if (screen->gc_depths[i] == 0)
+ break;
+ }
+
+ if (i == ARRAY_LENGTH (screen->gc)) {
+ /* perform random substitution to ensure fair caching over depths */
+ i = rand () % ARRAY_LENGTH (screen->gc);
+ _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
+ }
+
+ screen->gc[i] = gc;
+ screen->gc_depths[i] = depth;
+}
+
+cairo_status_t
+_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
+ const cairo_linear_pattern_t *linear,
+ cairo_surface_t *picture)
+{
+ struct pattern_cache_entry *entry;
+ cairo_status_t status;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+ entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
+ if (unlikely (entry == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
+ entry->key.size = 1;
+
+ status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base);
+ if (unlikely (status)) {
+ _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+ return status;
+ }
+
+ entry->picture = cairo_surface_reference (picture);
+ entry->screen = screen;
+
+ status = _cairo_cache_insert (&screen->linear_pattern_cache,
+ &entry->key);
+ if (unlikely (status)) {
+ cairo_surface_destroy (picture);
+ _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen,
+ const cairo_linear_pattern_t *linear)
+{
+ cairo_surface_t *picture = NULL;
+ struct pattern_cache_entry tmpl;
+ struct pattern_cache_entry *entry;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+ tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
+ _cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base);
+
+ entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key);
+ if (entry != NULL)
+ picture = cairo_surface_reference (entry->picture);
+
+ return picture;
+}
+
+cairo_status_t
+_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen,
+ const cairo_radial_pattern_t *radial,
+ cairo_surface_t *picture)
+{
+ struct pattern_cache_entry *entry;
+ cairo_status_t status;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+ entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
+ if (unlikely (entry == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
+ entry->key.size = 1;
+
+ status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base);
+ if (unlikely (status)) {
+ _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+ return status;
+ }
+
+ entry->picture = cairo_surface_reference (picture);
+ entry->screen = screen;
+
+ status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key);
+ if (unlikely (status)) {
+ cairo_surface_destroy (picture);
+ _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
+ const cairo_radial_pattern_t *radial)
+{
+ cairo_surface_t *picture = NULL;
+ struct pattern_cache_entry tmpl;
+ struct pattern_cache_entry *entry;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+ tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
+ _cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base);
+
+ entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key);
+ if (entry != NULL)
+ picture = cairo_surface_reference (entry->picture);
+
+ return picture;
+}
diff --git a/src/cairo-xcb-shm.c b/src/cairo-xcb-shm.c
new file mode 100755
index 000000000..2be2dac5b
--- /dev/null
+++ b/src/cairo-xcb-shm.c
@@ -0,0 +1,337 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributors(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+
+#include "cairo-xcb-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-mempool-private.h"
+
+#include <xcb/shm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <errno.h>
+
+#define CAIRO_MAX_SHM_MEMORY (16*1024*1024)
+
+/* a simple buddy allocator for memory pools
+ * XXX fragmentation? use Doug Lea's malloc?
+ */
+
+typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t;
+
+typedef enum {
+ PENDING_WAIT,
+ PENDING_POLL
+} shm_wait_type_t;
+
+struct _cairo_xcb_shm_mem_pool {
+ int shmid;
+ uint32_t shmseg;
+ void *shm;
+
+ cairo_mempool_t mem;
+
+ cairo_list_t link;
+};
+
+static void
+_cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool)
+{
+ cairo_list_del (&pool->link);
+
+ shmdt (pool->shm);
+ _cairo_mempool_fini (&pool->mem);
+
+ free (pool);
+}
+
+static void
+_cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info)
+{
+ cairo_xcb_connection_t *connection = shm_info->connection;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
+
+ _cairo_mempool_free (&shm_info->pool->mem, shm_info->mem);
+ _cairo_freepool_free (&connection->shm_info_freelist, shm_info);
+
+ /* scan for old, unused pools - hold at least one in reserve */
+ if (! cairo_list_is_singular (&connection->shm_pools))
+ {
+ cairo_xcb_shm_mem_pool_t *pool, *next;
+ cairo_list_t head;
+
+ cairo_list_init (&head);
+ cairo_list_move (connection->shm_pools.next, &head);
+
+ cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
+ &connection->shm_pools, link)
+ {
+ if (pool->mem.free_bytes == pool->mem.max_bytes) {
+ _cairo_xcb_connection_shm_detach (connection, pool->shmseg);
+ _cairo_xcb_shm_mem_pool_destroy (pool);
+ }
+ }
+
+ cairo_list_move (head.next, &connection->shm_pools);
+ }
+}
+
+static void
+_cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait)
+{
+ cairo_xcb_shm_info_t *info, *next;
+ xcb_get_input_focus_reply_t *reply;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
+ cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t,
+ &connection->shm_pending, pending)
+ {
+ switch (wait) {
+ case PENDING_WAIT:
+ reply = xcb_wait_for_reply (connection->xcb_connection,
+ info->sync.sequence, NULL);
+ break;
+ case PENDING_POLL:
+ if (! xcb_poll_for_reply (connection->xcb_connection,
+ info->sync.sequence,
+ (void **) &reply, NULL))
+ /* We cannot be sure the server finished with this image yet, so
+ * try again later. All other shm info are guaranteed to have a
+ * larger sequence number and thus don't have to be checked. */
+ return;
+ break;
+ default:
+ /* silence Clang static analyzer warning */
+ ASSERT_NOT_REACHED;
+ reply = NULL;
+ }
+
+ free (reply);
+ cairo_list_del (&info->pending);
+ _cairo_xcb_shm_info_finalize (info);
+ }
+}
+
+cairo_int_status_t
+_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection,
+ size_t size,
+ cairo_bool_t might_reuse,
+ cairo_xcb_shm_info_t **shm_info_out)
+{
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_xcb_shm_mem_pool_t *pool, *next;
+ size_t bytes, maxbits = 16, minbits = 8;
+ size_t shm_allocated = 0;
+ void *mem = NULL;
+ cairo_status_t status;
+
+ assert (connection->flags & CAIRO_XCB_HAS_SHM);
+
+ CAIRO_MUTEX_LOCK (connection->shm_mutex);
+ _cairo_xcb_shm_process_pending (connection, PENDING_POLL);
+
+ if (might_reuse) {
+ cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t,
+ &connection->shm_pending, pending) {
+ if (shm_info->size >= size) {
+ cairo_list_del (&shm_info->pending);
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+
+ xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence);
+ shm_info->sync.sequence = XCB_NONE;
+
+ *shm_info_out = shm_info;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+ }
+
+ cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
+ &connection->shm_pools, link)
+ {
+ if (pool->mem.free_bytes > size) {
+ mem = _cairo_mempool_alloc (&pool->mem, size);
+ if (mem != NULL) {
+ /* keep the active pools towards the front */
+ cairo_list_move (&pool->link, &connection->shm_pools);
+ goto allocate_shm_info;
+ }
+ }
+ /* scan for old, unused pools */
+ if (pool->mem.free_bytes == pool->mem.max_bytes) {
+ _cairo_xcb_connection_shm_detach (connection,
+ pool->shmseg);
+ _cairo_xcb_shm_mem_pool_destroy (pool);
+ } else {
+ shm_allocated += pool->mem.max_bytes;
+ }
+ }
+
+ if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) {
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pool = malloc (sizeof (cairo_xcb_shm_mem_pool_t));
+ if (unlikely (pool == NULL)) {
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ bytes = 1 << maxbits;
+ while (bytes <= size)
+ bytes <<= 1, maxbits++;
+ bytes <<= 3;
+
+ do {
+ pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
+ if (pool->shmid != -1)
+ break;
+
+ /* If the allocation failed because we asked for too much memory, we try
+ * again with a smaller request, as long as our allocation still fits. */
+ bytes >>= 1;
+ if (errno != EINVAL || bytes < size)
+ break;
+ } while (TRUE);
+ if (pool->shmid == -1) {
+ int err = errno;
+ if (! (err == EINVAL || err == ENOMEM))
+ connection->flags &= ~CAIRO_XCB_HAS_SHM;
+ free (pool);
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ pool->shm = shmat (pool->shmid, NULL, 0);
+ if (unlikely (pool->shm == (char *) -1)) {
+ shmctl (pool->shmid, IPC_RMID, NULL);
+ free (pool);
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ status = _cairo_mempool_init (&pool->mem, pool->shm, bytes,
+ minbits, maxbits - minbits + 1);
+ if (unlikely (status)) {
+ shmdt (pool->shm);
+ free (pool);
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+ return status;
+ }
+
+ pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE);
+ shmctl (pool->shmid, IPC_RMID, NULL);
+
+ cairo_list_add (&pool->link, &connection->shm_pools);
+ mem = _cairo_mempool_alloc (&pool->mem, size);
+
+ allocate_shm_info:
+ shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist);
+ if (unlikely (shm_info == NULL)) {
+ _cairo_mempool_free (&pool->mem, mem);
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ shm_info->connection = connection;
+ shm_info->pool = pool;
+ shm_info->shm = pool->shmseg;
+ shm_info->size = size;
+ shm_info->offset = (char *) mem - (char *) pool->shm;
+ shm_info->mem = mem;
+ shm_info->sync.sequence = XCB_NONE;
+
+ /* scan for old, unused pools */
+ cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
+ &connection->shm_pools, link)
+ {
+ if (pool->mem.free_bytes == pool->mem.max_bytes) {
+ _cairo_xcb_connection_shm_detach (connection,
+ pool->shmseg);
+ _cairo_xcb_shm_mem_pool_destroy (pool);
+ }
+ }
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+
+ *shm_info_out = shm_info;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info)
+{
+ cairo_xcb_connection_t *connection = shm_info->connection;
+
+ /* We can only return shm_info->mem to the allocator when we can be sure
+ * that the X server no longer reads from it. Since the X server processes
+ * requests in order, we send a GetInputFocus here.
+ * _cairo_xcb_shm_process_pending () will later check if the reply for that
+ * request was received and then actually mark this memory area as free. */
+
+ CAIRO_MUTEX_LOCK (connection->shm_mutex);
+ assert (shm_info->sync.sequence == XCB_NONE);
+ shm_info->sync = xcb_get_input_focus (connection->xcb_connection);
+
+ cairo_list_init (&shm_info->pending);
+ cairo_list_add_tail (&shm_info->pending, &connection->shm_pending);
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+}
+
+void
+_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection)
+{
+ CAIRO_MUTEX_LOCK (connection->shm_mutex);
+ _cairo_xcb_shm_process_pending (connection, PENDING_WAIT);
+ CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+}
+
+void
+_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection)
+{
+ assert (cairo_list_is_empty (&connection->shm_pending));
+ while (! cairo_list_is_empty (&connection->shm_pools)) {
+ _cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools,
+ cairo_xcb_shm_mem_pool_t,
+ link));
+ }
+}
+
+#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */
diff --git a/src/cairo-xcb-surface-core.c b/src/cairo-xcb-surface-core.c
new file mode 100755
index 000000000..db775cd17
--- /dev/null
+++ b/src/cairo-xcb-surface-core.c
@@ -0,0 +1,642 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-xcb-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+/* XXX dithering */
+
+typedef struct _cairo_xcb_pixmap {
+ cairo_surface_t base;
+
+ cairo_xcb_connection_t *connection;
+ cairo_xcb_screen_t *screen;
+
+ cairo_surface_t *owner;
+ xcb_pixmap_t pixmap;
+ int width;
+ int height;
+ int depth;
+ int x0, y0;
+ cairo_bool_t repeat;
+} cairo_xcb_pixmap_t;
+
+static cairo_status_t
+_cairo_xcb_pixmap_finish (void *abstract_surface)
+{
+ cairo_xcb_pixmap_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->owner != NULL) {
+ cairo_surface_destroy (surface->owner);
+ } else {
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xcb_connection_free_pixmap (surface->connection,
+ surface->pixmap);
+ _cairo_xcb_connection_release (surface->connection);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t _cairo_xcb_pixmap_backend = {
+ CAIRO_SURFACE_TYPE_XCB,
+ _cairo_xcb_pixmap_finish,
+};
+
+static cairo_xcb_pixmap_t *
+_cairo_xcb_pixmap_create (cairo_xcb_surface_t *target,
+ int width, int height)
+{
+ cairo_xcb_pixmap_t *surface;
+
+ surface = malloc (sizeof (cairo_xcb_pixmap_t));
+ if (unlikely (surface == NULL))
+ return (cairo_xcb_pixmap_t *)
+ _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_xcb_pixmap_backend,
+ NULL,
+ target->base.content);
+
+ surface->connection = target->connection;
+ surface->screen = target->screen;
+ surface->owner = NULL;
+ surface->width = width;
+ surface->height = height;
+ surface->depth = target->depth;
+ surface->x0 = surface->y0 = 0;
+ surface->repeat = FALSE;
+
+ surface->pixmap =
+ _cairo_xcb_connection_create_pixmap (surface->connection,
+ surface->depth,
+ target->drawable,
+ width, height);
+
+ return surface;
+}
+
+static cairo_xcb_pixmap_t *
+_cairo_xcb_pixmap_copy (cairo_xcb_surface_t *target)
+{
+ cairo_xcb_pixmap_t *surface;
+
+ surface = malloc (sizeof (cairo_xcb_pixmap_t));
+ if (unlikely (surface == NULL))
+ return (cairo_xcb_pixmap_t *)
+ _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_xcb_pixmap_backend,
+ NULL,
+ target->base.content);
+
+ surface->connection = target->connection;
+ surface->screen = target->screen;
+ surface->pixmap = target->drawable;
+ surface->owner = cairo_surface_reference (&target->base);
+ surface->width = target->width;
+ surface->height = target->height;
+ surface->depth = target->depth;
+ surface->x0 = surface->y0 = 0;
+ surface->repeat = FALSE;
+
+ return surface;
+}
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+static cairo_status_t
+_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection,
+ pixman_format_code_t pixman_format,
+ int width, int height,
+ cairo_image_surface_t **image_out,
+ cairo_xcb_shm_info_t **shm_info_out)
+{
+ cairo_surface_t *image = NULL;
+ cairo_xcb_shm_info_t *shm_info = NULL;
+ cairo_status_t status;
+ size_t size, stride;
+
+ if (! (connection->flags & CAIRO_XCB_HAS_SHM))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format));
+ size = stride * height;
+ if (size <= CAIRO_XCB_SHM_SMALL_IMAGE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_xcb_connection_allocate_shm_info (connection, size,
+ FALSE, &shm_info);
+ if (unlikely (status))
+ return status;
+
+ image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
+ pixman_format,
+ width, height,
+ stride);
+ status = image->status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return status;
+ }
+
+ status = _cairo_user_data_array_set_data (&image->user_data,
+ (const cairo_user_data_key_t *) connection,
+ shm_info,
+ (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return status;
+ }
+
+ *image_out = (cairo_image_surface_t *) image;
+ *shm_info_out = shm_info;
+ return CAIRO_STATUS_SUCCESS;
+}
+#else
+static cairo_status_t
+_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection,
+ pixman_format_code_t pixman_format,
+ int width, int height,
+ cairo_image_surface_t **image_out,
+ cairo_xcb_shm_info_t **shm_info_out)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+#endif
+
+cairo_status_t
+_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection,
+ pixman_format_code_t pixman_format,
+ int width, int height,
+ cairo_image_surface_t **image_out,
+ cairo_xcb_shm_info_t **shm_info_out)
+{
+ cairo_surface_t *image = NULL;
+ cairo_xcb_shm_info_t *shm_info = NULL;
+ cairo_status_t status;
+
+ status = _cairo_xcb_shm_image_create_shm (connection,
+ pixman_format,
+ width,
+ height,
+ image_out,
+ shm_info_out);
+
+ if (status != CAIRO_STATUS_SUCCESS) {
+ image = _cairo_image_surface_create_with_pixman_format (NULL,
+ pixman_format,
+ width, height,
+ 0);
+ status = image->status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ *image_out = (cairo_image_surface_t *) image;
+ *shm_info_out = shm_info;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_xcb_pixmap_t *
+_pixmap_from_image (cairo_xcb_surface_t *target,
+ xcb_render_pictformat_t format,
+ cairo_image_surface_t *image,
+ cairo_xcb_shm_info_t *shm_info)
+{
+ xcb_gcontext_t gc;
+ cairo_xcb_pixmap_t *pixmap;
+
+ pixmap = _cairo_xcb_pixmap_create (target,
+ image->width,
+ image->height);
+ if (unlikely (pixmap->base.status))
+ return pixmap;
+
+ gc = _cairo_xcb_screen_get_gc (target->screen, pixmap->pixmap, image->depth);
+
+ if (shm_info != NULL) {
+ _cairo_xcb_connection_shm_put_image (target->connection,
+ pixmap->pixmap, gc,
+ image->width, image->height,
+ 0, 0,
+ image->width, image->height,
+ 0, 0,
+ image->depth,
+ shm_info->shm,
+ shm_info->offset);
+ } else {
+ int len;
+
+ /* Do we need to trim the image? */
+ len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width,
+ PIXMAN_FORMAT_BPP (image->pixman_format));
+ if (len == image->stride) {
+ _cairo_xcb_connection_put_image (target->connection,
+ pixmap->pixmap, gc,
+ image->width, image->height,
+ 0, 0,
+ image->depth,
+ image->stride,
+ image->data);
+ } else {
+ _cairo_xcb_connection_put_subimage (target->connection,
+ pixmap->pixmap, gc,
+ 0, 0,
+ image->width, image->height,
+ PIXMAN_FORMAT_BPP (image->pixman_format) / 8,
+ image->stride,
+ 0, 0,
+ image->depth,
+ image->data);
+
+ }
+ }
+
+ _cairo_xcb_screen_put_gc (target->screen, image->depth, gc);
+
+ return pixmap;
+}
+
+static cairo_xcb_pixmap_t *
+_render_to_pixmap (cairo_xcb_surface_t *target,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_image_surface_t *image;
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_pattern_union_t copy;
+ cairo_status_t status;
+ cairo_xcb_pixmap_t *pixmap;
+
+ status = _cairo_xcb_shm_image_create (target->screen->connection,
+ target->pixman_format,
+ extents->width, extents->height,
+ &image, &shm_info);
+ if (unlikely (status))
+ return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status);
+
+ _cairo_pattern_init_static_copy (&copy.base, pattern);
+ cairo_matrix_translate (&copy.base.matrix, -extents->x, -extents->y);
+ status = _cairo_surface_paint (&image->base,
+ CAIRO_OPERATOR_SOURCE,
+ &copy.base,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image->base);
+ return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status);
+ }
+
+ pixmap = _pixmap_from_image (target, target->xrender_format, image, shm_info);
+ cairo_surface_destroy (&image->base);
+
+ if (unlikely (pixmap->base.status))
+ return pixmap;
+
+ pixmap->x0 = -extents->x;
+ pixmap->y0 = -extents->y;
+ return pixmap;
+}
+
+static cairo_xcb_pixmap_t *
+_copy_to_pixmap (cairo_xcb_surface_t *source)
+{
+ cairo_xcb_pixmap_t *pixmap;
+
+ /* If the source may be a window, we need to copy it and its children
+ * via a temporary pixmap so that we can IncludeInferiors on the source
+ * and use ClipByChildren on the destination.
+ */
+ if (source->owns_pixmap) {
+ pixmap = _cairo_xcb_pixmap_copy (source);
+ if (unlikely (pixmap->base.status))
+ return pixmap;
+ } else {
+ uint32_t values[1];
+ xcb_gcontext_t gc;
+
+ pixmap = _cairo_xcb_pixmap_create (source,
+ source->width,
+ source->height);
+ if (unlikely (pixmap->base.status))
+ return pixmap;
+
+ gc = _cairo_xcb_screen_get_gc (source->screen,
+ pixmap->pixmap,
+ pixmap->depth);
+
+ values[0] = TRUE;
+ _cairo_xcb_connection_change_gc (pixmap->connection, gc,
+ XCB_GC_SUBWINDOW_MODE, values);
+
+ _cairo_xcb_connection_copy_area (pixmap->connection,
+ source->drawable,
+ pixmap->pixmap, gc,
+ 0, 0,
+ 0, 0,
+ source->width,
+ source->height);
+
+ values[0] = FALSE;
+ _cairo_xcb_connection_change_gc (pixmap->connection, gc,
+ XCB_GC_SUBWINDOW_MODE, values);
+
+ _cairo_xcb_screen_put_gc (source->screen,
+ pixmap->depth,
+ gc);
+ }
+
+ return pixmap;
+}
+static cairo_xcb_pixmap_t *
+_cairo_xcb_surface_pixmap (cairo_xcb_surface_t *target,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int tx, int ty)
+{
+ cairo_surface_t *source;
+ cairo_xcb_pixmap_t *pixmap;
+
+ source = pattern->surface;
+ pixmap = (cairo_xcb_pixmap_t *)
+ _cairo_surface_has_snapshot (source, &_cairo_xcb_pixmap_backend);
+ if (pixmap != NULL && pixmap->screen == target->screen)
+ return (cairo_xcb_pixmap_t *) cairo_surface_reference (&pixmap->base);
+
+ if (source->type == CAIRO_SURFACE_TYPE_XCB &&
+ ((cairo_xcb_surface_t *) source)->screen == target->screen)
+ {
+ cairo_xcb_surface_t *xcb_source = (cairo_xcb_surface_t *) source;
+
+ if (xcb_source->depth == target->depth)
+ pixmap = _copy_to_pixmap (xcb_source);
+ }
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+ else if (source->type == CAIRO_SURFACE_TYPE_XLIB &&
+ ((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen)
+ {
+ cairo_xcb_surface_t *xcb_source = ((cairo_xlib_xcb_surface_t *) source)->xcb;
+
+ if (xcb_source->depth == target->depth)
+ pixmap = _copy_to_pixmap (xcb_source);
+ }
+#endif
+
+ if (pixmap == NULL) {
+ cairo_rectangle_int_t rect;
+
+ if (! _cairo_surface_get_extents (source, &rect)) {
+ rect.x = rect.y = 0;
+ rect.width = target->width;
+ rect.height = target->height;
+ }
+
+ pixmap = _render_to_pixmap (target, &pattern->base, &rect);
+ }
+
+ if (unlikely (pixmap->base.status))
+ return pixmap;
+
+ _cairo_surface_attach_snapshot (source, &pixmap->base, NULL);
+
+ if (pattern->base.extend != CAIRO_EXTEND_NONE) {
+ if (extents->x < 0 || extents->y < 0 ||
+ extents->x + extents->width > pixmap->width ||
+ extents->y + extents->height > pixmap->height)
+ {
+ pixmap->repeat = TRUE;
+ }
+ }
+
+ pixmap->x0 += tx;
+ pixmap->y0 += ty;
+
+ return pixmap;
+}
+
+static cairo_xcb_pixmap_t *
+_cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ int tx, ty;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ /* Core can only perform a native, unscaled blit, but can handle tiles */
+ if (_cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) {
+ switch (pattern->extend) {
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_REPEAT:
+ return _cairo_xcb_surface_pixmap (target,
+ (cairo_surface_pattern_t *) pattern,
+ extents, tx, ty);
+
+ default:
+ case CAIRO_EXTEND_PAD:
+ case CAIRO_EXTEND_REFLECT:
+ break;
+ }
+ }
+ /* fallthrough */
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_MESH:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _render_to_pixmap (target, pattern, extents);
+
+ default:
+ case CAIRO_PATTERN_TYPE_SOLID:
+ ASSERT_NOT_REACHED;
+ return NULL;
+ }
+}
+
+cairo_status_t
+_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst,
+ const cairo_pattern_t *src_pattern,
+ const cairo_rectangle_int_t *extents,
+ const cairo_boxes_t *boxes)
+{
+ cairo_xcb_pixmap_t *src;
+ const struct _cairo_boxes_chunk *chunk;
+ xcb_gcontext_t gc;
+ cairo_status_t status;
+
+ status = _cairo_xcb_connection_acquire (dst->connection);
+ if (unlikely (status))
+ return status;
+
+ src = _cairo_xcb_pixmap_for_pattern (dst, src_pattern, extents);
+ status = src->base.status;
+ if (unlikely (status))
+ goto CLEANUP_CONNECTION;
+
+ assert (src->depth == dst->depth);
+
+ gc = _cairo_xcb_screen_get_gc (dst->screen, src->pixmap, src->depth);
+
+ if (src->repeat) {
+ uint32_t mask =
+ XCB_GC_FILL_STYLE |
+ XCB_GC_TILE |
+ XCB_GC_TILE_STIPPLE_ORIGIN_X |
+ XCB_GC_TILE_STIPPLE_ORIGIN_Y;
+ uint32_t values[] = {
+ XCB_FILL_STYLE_TILED,
+ src->pixmap,
+ - src->x0, - src->y0,
+ };
+ xcb_rectangle_t *xcb_rects;
+
+ _cairo_xcb_connection_change_gc (dst->connection, gc, mask, values);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ int i;
+
+ xcb_rects = (xcb_rectangle_t *) chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
+ int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
+ int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
+ int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
+
+ xcb_rects[i].x = x1;
+ xcb_rects[i].y = y1;
+ xcb_rects[i].width = x2 - x1;
+ xcb_rects[i].height = y2 - y1;
+ }
+ _cairo_xcb_connection_poly_fill_rectangle (dst->connection,
+ dst->drawable,
+ gc, chunk->count, xcb_rects);
+ }
+
+ values[0] = 0;
+ _cairo_xcb_connection_change_gc (dst->connection, gc, XCB_GC_FILL_STYLE, values);
+ } else {
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ int i;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
+ int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
+ int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
+ int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
+
+ _cairo_xcb_connection_copy_area (dst->connection,
+ src->pixmap,
+ dst->drawable, gc,
+ src->x0 + x1,
+ src->y0 + y1,
+ x1, y1,
+ x2 - x2, y2 - x2);
+ }
+ }
+ }
+
+ _cairo_xcb_screen_put_gc (dst->screen, src->depth, gc);
+ cairo_surface_destroy (&src->base);
+
+ CLEANUP_CONNECTION:
+ _cairo_xcb_connection_release (dst->connection);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ struct _cairo_boxes_chunk *chunk;
+ xcb_gcontext_t gc;
+ cairo_status_t status;
+
+ status = _cairo_xcb_connection_acquire (dst->connection);
+ if (unlikely (status))
+ return status;
+
+ gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
+
+#if 0
+ xcb_pixmap_t source;
+
+ source = _dither_source (dst, color);
+ XSetTSOrigin (surface->dpy, gc, 0, 0);
+ XSetTile (surface->dpy, gc, source);
+#endif
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ xcb_rectangle_t *xcb_rects;
+ int i;
+
+ xcb_rects = (xcb_rectangle_t *) chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
+ int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
+ int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
+ int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
+
+ xcb_rects[i].x = x1;
+ xcb_rects[i].y = y1;
+ xcb_rects[i].width = x2 - x1;
+ xcb_rects[i].height = y2 - y1;
+ }
+
+ _cairo_xcb_connection_poly_fill_rectangle (dst->connection,
+ dst->drawable, gc,
+ chunk->count, xcb_rects);
+ }
+
+ _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
+ _cairo_xcb_connection_release (dst->connection);
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
new file mode 100755
index 000000000..c6393a809
--- /dev/null
+++ b/src/cairo-xcb-surface-render.c
@@ -0,0 +1,5253 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ * Copyright © 2013 Samsung Research America, Silicon Valley
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Henry Song <henry.song@samsung.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-filters-private.h"
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ cairo_composite_rectangles_t *extents);
+
+static inline cairo_xcb_connection_t *
+_picture_to_connection (cairo_xcb_picture_t *picture)
+{
+ return (cairo_xcb_connection_t *) picture->base.device;
+}
+
+static void
+_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface);
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static cairo_status_t
+_cairo_xcb_picture_finish (void *abstract_surface)
+{
+ cairo_xcb_picture_t *surface = abstract_surface;
+ cairo_xcb_connection_t *connection = _picture_to_connection (surface);
+ cairo_status_t status;
+
+ status = _cairo_xcb_connection_acquire (connection);
+ cairo_list_del (&surface->link);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xcb_connection_render_free_picture (connection, surface->picture);
+
+ _cairo_xcb_connection_release (connection);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t _cairo_xcb_picture_backend = {
+ CAIRO_SURFACE_TYPE_XCB,
+ _cairo_xcb_picture_finish,
+};
+
+static const struct xcb_render_transform_t identity_transform = {
+ 1 << 16, 0, 0,
+ 0, 1 << 16, 0,
+ 0, 0, 1 << 16,
+};
+
+static cairo_xcb_picture_t *
+_cairo_xcb_picture_create (cairo_xcb_screen_t *screen,
+ pixman_format_code_t pixman_format,
+ xcb_render_pictformat_t xrender_format,
+ int width, int height)
+{
+ cairo_xcb_picture_t *surface;
+
+ surface = malloc (sizeof (cairo_xcb_picture_t));
+ if (unlikely (surface == NULL))
+ return (cairo_xcb_picture_t *)
+ _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_xcb_picture_backend,
+ &screen->connection->device,
+ _cairo_content_from_pixman_format (pixman_format));
+
+ cairo_list_add (&surface->link, &screen->pictures);
+
+ surface->screen = screen;
+ surface->picture = _cairo_xcb_connection_get_xid (screen->connection);
+ surface->pixman_format = pixman_format;
+ surface->xrender_format = xrender_format;
+
+ surface->x0 = surface->y0 = 0;
+ surface->x = surface->y = 0;
+ surface->width = width;
+ surface->height = height;
+
+ surface->transform = identity_transform;
+ surface->extend = CAIRO_EXTEND_NONE;
+ surface->filter = CAIRO_FILTER_NEAREST;
+ surface->has_component_alpha = FALSE;
+
+ return surface;
+}
+
+static inline cairo_bool_t
+_operator_is_supported (uint32_t flags, cairo_operator_t op)
+{
+ if (op <= CAIRO_OPERATOR_SATURATE)
+ return TRUE;
+
+ /* Can we use PDF operators? */
+#if CAIRO_XCB_RENDER_AT_LEAST(0, 11)
+ if (op <= CAIRO_OPERATOR_HSL_LUMINOSITY)
+ return flags & CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
+#endif
+
+ return FALSE;
+}
+
+static int
+_render_operator (cairo_operator_t op)
+{
+#define C(x,y) case CAIRO_OPERATOR_##x: return XCB_RENDER_PICT_OP_##y
+ switch (op) {
+ C(CLEAR, CLEAR);
+ C(SOURCE, SRC);
+
+ C(OVER, OVER);
+ C(IN, IN);
+ C(OUT, OUT);
+ C(ATOP, ATOP);
+
+ C(DEST, DST);
+ C(DEST_OVER, OVER_REVERSE);
+ C(DEST_IN, IN_REVERSE);
+ C(DEST_OUT, OUT_REVERSE);
+ C(DEST_ATOP, ATOP_REVERSE);
+
+ C(XOR, XOR);
+ C(ADD, ADD);
+ C(SATURATE, SATURATE);
+
+ /* PDF operators were added in RENDER 0.11, check if the xcb headers have
+ * the defines, else fall through to the default case. */
+#if CAIRO_XCB_RENDER_AT_LEAST(0, 11)
+#define BLEND(x,y) C(x,y)
+#else
+#define BLEND(x,y) case CAIRO_OPERATOR_##x:
+#endif
+ BLEND(MULTIPLY, MULTIPLY);
+ BLEND(SCREEN, SCREEN);
+ BLEND(OVERLAY, OVERLAY);
+ BLEND(DARKEN, DARKEN);
+ BLEND(LIGHTEN, LIGHTEN);
+ BLEND(COLOR_DODGE, COLOR_DODGE);
+ BLEND(COLOR_BURN, COLOR_BURN);
+ BLEND(HARD_LIGHT, HARD_LIGHT);
+ BLEND(SOFT_LIGHT, SOFT_LIGHT);
+ BLEND(DIFFERENCE, DIFFERENCE);
+ BLEND(EXCLUSION, EXCLUSION);
+ BLEND(HSL_HUE, HSL_HUE);
+ BLEND(HSL_SATURATION, HSL_SATURATION);
+ BLEND(HSL_COLOR, HSL_COLOR);
+ BLEND(HSL_LUMINOSITY, HSL_LUMINOSITY);
+
+ default:
+ ASSERT_NOT_REACHED;
+ return XCB_RENDER_PICT_OP_OVER;
+ }
+}
+
+static cairo_status_t
+_cairo_xcb_surface_set_clip_region (cairo_xcb_surface_t *surface,
+ cairo_region_t *region)
+{
+ xcb_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
+ xcb_rectangle_t *rects = stack_rects;
+ int i, num_rects;
+
+ num_rects = cairo_region_num_rectangles (region);
+
+ if (num_rects > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (num_rects, sizeof (xcb_rectangle_t));
+ if (unlikely (rects == NULL)) {
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
+ for (i = 0; i < num_rects; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (region, i, &rect);
+
+ rects[i].x = rect.x;
+ rects[i].y = rect.y;
+ rects[i].width = rect.width;
+ rects[i].height = rect.height;
+ }
+
+ _cairo_xcb_connection_render_set_picture_clip_rectangles (surface->connection,
+ surface->picture,
+ 0, 0,
+ num_rects, rects);
+
+ if (rects != stack_rects)
+ free (rects);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xcb_surface_clear_clip_region (cairo_xcb_surface_t *surface)
+{
+ uint32_t values[] = { XCB_NONE };
+ _cairo_xcb_connection_render_change_picture (surface->connection,
+ surface->picture,
+ XCB_RENDER_CP_CLIP_MASK,
+ values);
+}
+
+static void
+_cairo_xcb_surface_set_precision (cairo_xcb_surface_t *surface,
+ cairo_antialias_t antialias)
+{
+ cairo_xcb_connection_t *connection = surface->connection;
+ uint32_t precision;
+
+ if (connection->force_precision != -1)
+ precision = connection->force_precision;
+ else switch (antialias) {
+ default:
+ case CAIRO_ANTIALIAS_DEFAULT:
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_NONE:
+ case CAIRO_ANTIALIAS_FAST:
+ case CAIRO_ANTIALIAS_GOOD:
+ precision = XCB_RENDER_POLY_MODE_IMPRECISE;
+ break;
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ case CAIRO_ANTIALIAS_BEST:
+ precision = XCB_RENDER_POLY_MODE_PRECISE;
+ break;
+ }
+
+ if (surface->precision != precision) {
+ _cairo_xcb_connection_render_change_picture (connection,
+ surface->picture,
+ XCB_RENDER_CP_POLY_MODE,
+ &precision);
+ surface->precision = precision;
+ }
+}
+
+
+static void
+_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface)
+{
+ assert (surface->fallback == NULL);
+ if (surface->picture == XCB_NONE) {
+ uint32_t values[1];
+ uint32_t flags = 0;
+
+ if (surface->precision != XCB_RENDER_POLY_MODE_PRECISE) {
+ flags |= XCB_RENDER_CP_POLY_MODE;
+ values[0] = surface->precision;
+ }
+
+ surface->picture = _cairo_xcb_connection_get_xid (surface->connection);
+ _cairo_xcb_connection_render_create_picture (surface->connection,
+ surface->picture,
+ surface->drawable,
+ surface->xrender_format,
+ flags, values);
+ }
+}
+
+static cairo_xcb_picture_t *
+_picture_from_image (cairo_xcb_surface_t *target,
+ xcb_render_pictformat_t format,
+ cairo_image_surface_t *image,
+ cairo_xcb_shm_info_t *shm_info)
+{
+ xcb_pixmap_t pixmap;
+ xcb_gcontext_t gc;
+ cairo_xcb_picture_t *picture;
+
+ pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
+ image->depth,
+ target->drawable,
+ image->width, image->height);
+
+ gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, image->depth);
+
+ if (shm_info != NULL) {
+ _cairo_xcb_connection_shm_put_image (target->connection,
+ pixmap, gc,
+ image->width, image->height,
+ 0, 0,
+ image->width, image->height,
+ 0, 0,
+ image->depth,
+ shm_info->shm,
+ shm_info->offset);
+ } else {
+ int len;
+
+ /* Do we need to trim the image? */
+ len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format));
+ if (len == image->stride) {
+ _cairo_xcb_connection_put_image (target->connection,
+ pixmap, gc,
+ image->width, image->height,
+ 0, 0,
+ image->depth,
+ image->stride,
+ image->data);
+ } else {
+ _cairo_xcb_connection_put_subimage (target->connection,
+ pixmap, gc,
+ 0, 0,
+ image->width, image->height,
+ PIXMAN_FORMAT_BPP (image->pixman_format) / 8,
+ image->stride,
+ 0, 0,
+ image->depth,
+ image->data);
+
+ }
+ }
+
+ _cairo_xcb_screen_put_gc (target->screen, image->depth, gc);
+
+ picture = _cairo_xcb_picture_create (target->screen,
+ image->pixman_format, format,
+ image->width, image->height);
+ if (likely (picture->base.status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_xcb_connection_render_create_picture (target->connection,
+ picture->picture, pixmap, format,
+ 0, 0);
+ }
+
+ _cairo_xcb_connection_free_pixmap (target->connection, pixmap);
+
+ return picture;
+}
+
+static cairo_bool_t
+_pattern_is_supported (uint32_t flags,
+ const cairo_pattern_t *pattern)
+
+{
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+ return TRUE;
+
+ if (! _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) {
+ if ((flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) == 0)
+ return FALSE;
+ }
+
+ switch (pattern->extend) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_REPEAT:
+ break;
+ case CAIRO_EXTEND_PAD:
+ case CAIRO_EXTEND_REFLECT:
+ if ((flags & CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT) == 0)
+ return FALSE;
+ }
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_filter_t filter;
+
+ filter = pattern->filter;
+ if (_cairo_matrix_has_unity_scale (&pattern->matrix) &&
+ _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
+ {
+ filter = CAIRO_FILTER_NEAREST;
+ }
+
+ if (! (filter == CAIRO_FILTER_NEAREST || filter == CAIRO_FILTER_FAST)) {
+ if ((flags & CAIRO_XCB_RENDER_HAS_FILTERS) == 0)
+ return FALSE;
+ }
+ } else { /* gradient */
+ if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0)
+ return FALSE;
+
+ /* The RENDER specification says that the inner circle has to be
+ * completely contained inside the outer one. */
+ if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL &&
+ ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) pattern))
+ {
+ return FALSE;
+ }
+ }
+
+ return pattern->type != CAIRO_PATTERN_TYPE_MESH;
+}
+
+static void
+_cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture,
+ const cairo_matrix_t *matrix,
+ cairo_filter_t filter,
+ double xc, double yc)
+{
+ xcb_render_transform_t transform;
+ pixman_transform_t *pixman_transform;
+ cairo_int_status_t ignored;
+
+ /* Casting between pixman_transform_t and xcb_render_transform_t is safe
+ * because they happen to be the exact same type.
+ */
+ pixman_transform = (pixman_transform_t *) &transform;
+
+ picture->x = picture->x0;
+ picture->y = picture->y0;
+ ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc,
+ pixman_transform,
+ &picture->x, &picture->y);
+ (void) ignored;
+
+ if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) {
+ _cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture),
+ picture->picture,
+ &transform);
+
+ picture->transform = transform;
+ }
+}
+
+static void
+_cairo_xcb_picture_set_filter (cairo_xcb_picture_t *picture,
+ cairo_filter_t filter,
+ int values_len,
+ xcb_render_fixed_t *values)
+{
+ const char *render_filter;
+ int len;
+
+ if (picture->filter == filter)
+ return;
+
+ switch (filter) {
+ case CAIRO_FILTER_FAST:
+ render_filter = "fast";
+ len = strlen ("fast");
+ break;
+
+ case CAIRO_FILTER_GOOD:
+ render_filter = "good";
+ len = strlen ("good");
+ break;
+
+ case CAIRO_FILTER_BEST:
+ render_filter = "best";
+ len = strlen ("best");
+ break;
+
+ case CAIRO_FILTER_NEAREST:
+ render_filter = "nearest";
+ len = strlen ("nearest");
+ break;
+
+ case CAIRO_FILTER_BILINEAR:
+ render_filter = "bilinear";
+ len = strlen ("bilinear");
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FILTER_GAUSSIAN:
+ if (values_len == 0 || values == NULL) {
+ render_filter = "best";
+ len = strlen ("best");
+ }
+ else {
+ render_filter = "convolution";
+ len = strlen ("convolution");
+ }
+ break;
+ }
+
+ _cairo_xcb_connection_render_set_picture_filter (_picture_to_connection (picture),
+ picture->picture,
+ len, (char *) render_filter,
+ values_len, values);
+ picture->filter = filter;
+}
+
+static void
+_cairo_xcb_picture_set_extend (cairo_xcb_picture_t *picture,
+ cairo_extend_t extend)
+{
+ uint32_t pa[1];
+
+ if (picture->extend == extend)
+ return;
+
+ switch (extend) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_EXTEND_NONE:
+ pa[0] = XCB_RENDER_REPEAT_NONE;
+ break;
+
+ case CAIRO_EXTEND_REPEAT:
+ pa[0] = XCB_RENDER_REPEAT_NORMAL;
+ break;
+
+ case CAIRO_EXTEND_REFLECT:
+ pa[0] = XCB_RENDER_REPEAT_REFLECT;
+ break;
+
+ case CAIRO_EXTEND_PAD:
+ pa[0] = XCB_RENDER_REPEAT_PAD;
+ break;
+ }
+
+ _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
+ picture->picture,
+ XCB_RENDER_CP_REPEAT, pa);
+ picture->extend = extend;
+}
+
+static void
+_cairo_xcb_picture_set_component_alpha (cairo_xcb_picture_t *picture,
+ cairo_bool_t ca)
+{
+ uint32_t pa[1];
+
+ if (picture->has_component_alpha == ca)
+ return;
+
+ pa[0] = ca;
+
+ _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
+ picture->picture,
+ XCB_RENDER_CP_COMPONENT_ALPHA,
+ pa);
+ picture->has_component_alpha = ca;
+}
+
+static cairo_xcb_picture_t *
+_solid_picture (cairo_xcb_surface_t *target,
+ const cairo_color_t *color)
+{
+ xcb_render_color_t xcb_color;
+ xcb_render_pictformat_t xrender_format;
+ cairo_xcb_picture_t *picture;
+
+ xcb_color.red = color->red_short;
+ xcb_color.green = color->green_short;
+ xcb_color.blue = color->blue_short;
+ xcb_color.alpha = color->alpha_short;
+
+ xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
+ picture = _cairo_xcb_picture_create (target->screen,
+ PIXMAN_a8r8g8b8,
+ xrender_format,
+ -1, -1);
+ if (unlikely (picture->base.status))
+ return picture;
+
+ if (target->connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) {
+ _cairo_xcb_connection_render_create_solid_fill (target->connection,
+ picture->picture,
+ xcb_color);
+ } else {
+ xcb_pixmap_t pixmap;
+ uint32_t values[] = { XCB_RENDER_REPEAT_NORMAL };
+
+ pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
+ 32, target->drawable, 1, 1);
+ _cairo_xcb_connection_render_create_picture (target->connection,
+ picture->picture,
+ pixmap,
+ xrender_format,
+ XCB_RENDER_CP_REPEAT,
+ values);
+ if (target->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
+ xcb_rectangle_t rect;
+
+ rect.x = rect.y = 0;
+ rect.width = rect.height = 1;
+
+ _cairo_xcb_connection_render_fill_rectangles (_picture_to_connection (picture),
+ XCB_RENDER_PICT_OP_SRC,
+ picture->picture,
+ xcb_color, 1, &rect);
+ } else {
+ xcb_gcontext_t gc;
+ uint32_t pixel;
+
+ gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, 32);
+
+ /* XXX byte ordering? */
+ pixel = ((color->alpha_short >> 8) << 24) |
+ ((color->red_short >> 8) << 16) |
+ ((color->green_short >> 8) << 8) |
+ ((color->blue_short >> 8) << 0);
+
+ _cairo_xcb_connection_put_image (target->connection,
+ pixmap, gc,
+ 1, 1, 0, 0,
+ 32, 4, &pixel);
+
+ _cairo_xcb_screen_put_gc (target->screen, 32, gc);
+ }
+
+ _cairo_xcb_connection_free_pixmap (target->connection, pixmap);
+ }
+
+ return picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_transparent_picture (cairo_xcb_surface_t *target)
+{
+ cairo_xcb_picture_t *picture;
+
+ picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT];
+ if (picture == NULL) {
+ picture = _solid_picture (target, CAIRO_COLOR_TRANSPARENT);
+ target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT] = &picture->base;
+ }
+
+ return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_black_picture (cairo_xcb_surface_t *target)
+{
+ cairo_xcb_picture_t *picture;
+
+ picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_BLACK];
+ if (picture == NULL) {
+ picture = _solid_picture (target, CAIRO_COLOR_BLACK);
+ target->screen->stock_colors[CAIRO_STOCK_BLACK] = &picture->base;
+ }
+
+ return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_white_picture (cairo_xcb_surface_t *target)
+{
+ cairo_xcb_picture_t *picture;
+
+ picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_WHITE];
+ if (picture == NULL) {
+ picture = _solid_picture (target, CAIRO_COLOR_WHITE);
+ target->screen->stock_colors[CAIRO_STOCK_WHITE] = &picture->base;
+ }
+
+ return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_solid_picture (cairo_xcb_surface_t *target,
+ const cairo_solid_pattern_t *pattern)
+{
+ cairo_xcb_picture_t *picture;
+ cairo_xcb_screen_t *screen;
+ int i, n_cached;
+
+ if (pattern->color.alpha_short <= 0x00ff)
+ return _cairo_xcb_transparent_picture (target);
+
+ if (pattern->color.alpha_short >= 0xff00) {
+ if (pattern->color.red_short <= 0x00ff &&
+ pattern->color.green_short <= 0x00ff &&
+ pattern->color.blue_short <= 0x00ff)
+ {
+ return _cairo_xcb_black_picture (target);
+ }
+
+ if (pattern->color.red_short >= 0xff00 &&
+ pattern->color.green_short >= 0xff00 &&
+ pattern->color.blue_short >= 0xff00)
+ {
+ return _cairo_xcb_white_picture (target);
+ }
+ }
+
+ screen = target->screen;
+ n_cached = screen->solid_cache_size;
+ for (i = 0; i < n_cached; i++) {
+ if (_cairo_color_equal (&screen->solid_cache[i].color, &pattern->color)) {
+ return (cairo_xcb_picture_t *) cairo_surface_reference (screen->solid_cache[i].picture);
+ }
+ }
+
+ picture = _solid_picture (target, &pattern->color);
+ if (unlikely (picture->base.status))
+ return picture;
+
+ if (screen->solid_cache_size < ARRAY_LENGTH (screen->solid_cache)) {
+ i = screen->solid_cache_size++;
+ } else {
+ i = hars_petruska_f54_1_random () % ARRAY_LENGTH (screen->solid_cache);
+ cairo_surface_destroy (screen->solid_cache[i].picture);
+ }
+ screen->solid_cache[i].picture = cairo_surface_reference (&picture->base);
+ screen->solid_cache[i].color = pattern->color;
+
+ return picture;
+}
+
+static cairo_xcb_picture_t *
+_render_to_picture (cairo_xcb_surface_t *target,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_image_surface_t *image;
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_pattern_union_t copy;
+ cairo_status_t status;
+ cairo_xcb_picture_t *picture;
+ pixman_format_code_t pixman_format;
+ xcb_render_pictformat_t xrender_format;
+
+ /* XXX handle extend modes via tiling? */
+ /* XXX alpha-only masks? */
+
+ pixman_format = PIXMAN_a8r8g8b8;
+ xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
+
+ status = _cairo_xcb_shm_image_create (target->screen->connection,
+ pixman_format,
+ extents->width, extents->height,
+ &image, &shm_info);
+ if (unlikely (status))
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+
+ _cairo_pattern_init_static_copy (&copy.base, pattern);
+ cairo_matrix_translate (&copy.base.matrix, extents->x, extents->y);
+ status = _cairo_surface_paint (&image->base,
+ CAIRO_OPERATOR_SOURCE,
+ &copy.base,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image->base);
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+ }
+
+ picture = _picture_from_image (target, xrender_format, image, shm_info);
+ cairo_surface_destroy (&image->base);
+
+ if (unlikely (picture->base.status))
+ return picture;
+
+ _cairo_xcb_picture_set_component_alpha (picture, pattern->has_component_alpha);
+ picture->x = -extents->x;
+ picture->y = -extents->y;
+
+ return picture;
+}
+
+static xcb_render_fixed_t *
+_gradient_to_xcb (const cairo_gradient_pattern_t *gradient,
+ unsigned int *n_stops,
+ char *buf, unsigned int buflen)
+{
+ xcb_render_fixed_t *stops;
+ xcb_render_color_t *colors;
+ unsigned int i;
+
+ assert (gradient->n_stops > 0);
+ *n_stops = MAX (gradient->n_stops, 2);
+
+ if (*n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen)
+ {
+ stops = (xcb_render_fixed_t *) buf;
+ }
+ else
+ {
+ stops =
+ _cairo_malloc_ab (*n_stops,
+ sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t));
+ if (unlikely (stops == NULL))
+ return NULL;
+ }
+
+ colors = (xcb_render_color_t *) (stops + *n_stops);
+ for (i = 0; i < gradient->n_stops; i++) {
+ stops[i] =
+ _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
+
+ colors[i].red = gradient->stops[i].color.red_short;
+ colors[i].green = gradient->stops[i].color.green_short;
+ colors[i].blue = gradient->stops[i].color.blue_short;
+ colors[i].alpha = gradient->stops[i].color.alpha_short;
+ }
+
+ /* RENDER does not support gradients with less than 2 stops. If a
+ * gradient has only a single stop, duplicate it to make RENDER
+ * happy. */
+ if (gradient->n_stops == 1) {
+ stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset);
+
+ colors[1].red = gradient->stops[0].color.red_short;
+ colors[1].green = gradient->stops[0].color.green_short;
+ colors[1].blue = gradient->stops[0].color.blue_short;
+ colors[1].alpha = gradient->stops[0].color.alpha_short;
+ }
+
+ return stops;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
+ const cairo_linear_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ char buf[CAIRO_STACK_BUFFER_SIZE];
+ xcb_render_fixed_t *stops;
+ xcb_render_color_t *colors;
+ xcb_render_pointfix_t p1, p2;
+ cairo_matrix_t matrix;
+ cairo_circle_double_t extremes[2];
+ cairo_xcb_picture_t *picture;
+ cairo_status_t status;
+ unsigned int n_stops;
+
+ _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+ picture = (cairo_xcb_picture_t *)
+ _cairo_xcb_screen_lookup_linear_picture (target->screen, pattern);
+ if (picture != NULL)
+ goto setup_picture;
+
+ stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
+ if (unlikely (stops == NULL))
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ picture = _cairo_xcb_picture_create (target->screen,
+ target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
+ PIXMAN_a8r8g8b8,
+ -1, -1);
+ if (unlikely (picture->base.status)) {
+ if (stops != (xcb_render_fixed_t *) buf)
+ free (stops);
+ return picture;
+ }
+ picture->filter = CAIRO_FILTER_DEFAULT;
+
+ colors = (xcb_render_color_t *) (stops + n_stops);
+
+ p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+ _cairo_xcb_connection_render_create_linear_gradient (target->connection,
+ picture->picture,
+ p1, p2,
+ n_stops,
+ stops, colors);
+
+ if (stops != (xcb_render_fixed_t *) buf)
+ free (stops);
+
+ status = _cairo_xcb_screen_store_linear_picture (target->screen,
+ pattern,
+ &picture->base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&picture->base);
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+ }
+
+setup_picture:
+ _cairo_xcb_picture_set_matrix (picture, &matrix,
+ pattern->base.base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter, 0, NULL);
+ _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
+ _cairo_xcb_picture_set_component_alpha (picture,
+ pattern->base.base.has_component_alpha);
+
+ return picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
+ const cairo_radial_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ char buf[CAIRO_STACK_BUFFER_SIZE];
+ xcb_render_fixed_t *stops;
+ xcb_render_color_t *colors;
+ xcb_render_pointfix_t p1, p2;
+ xcb_render_fixed_t r1, r2;
+ cairo_matrix_t matrix;
+ cairo_circle_double_t extremes[2];
+ cairo_xcb_picture_t *picture;
+ cairo_status_t status;
+ unsigned int n_stops;
+
+ _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+ picture = (cairo_xcb_picture_t *)
+ _cairo_xcb_screen_lookup_radial_picture (target->screen, pattern);
+ if (picture != NULL)
+ goto setup_picture;
+
+ stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
+ if (unlikely (stops == NULL))
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ picture = _cairo_xcb_picture_create (target->screen,
+ target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
+ PIXMAN_a8r8g8b8,
+ -1, -1);
+ if (unlikely (picture->base.status)) {
+ if (stops != (xcb_render_fixed_t *) buf)
+ free (stops);
+ return picture;
+ }
+ picture->filter = CAIRO_FILTER_DEFAULT;
+
+ colors = (xcb_render_color_t *) (stops + n_stops);
+
+ p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+ r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
+ r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+ _cairo_xcb_connection_render_create_radial_gradient (target->connection,
+ picture->picture,
+ p1, p2, r1, r2,
+ n_stops,
+ stops, colors);
+
+ if (stops != (xcb_render_fixed_t *) buf)
+ free (stops);
+
+ status = _cairo_xcb_screen_store_radial_picture (target->screen,
+ pattern,
+ &picture->base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&picture->base);
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+ }
+
+setup_picture:
+ _cairo_xcb_picture_set_matrix (picture, &matrix,
+ pattern->base.base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter, 0, NULL);
+ _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
+ _cairo_xcb_picture_set_component_alpha (picture,
+ pattern->base.base.has_component_alpha);
+
+ return picture;
+}
+
+static cairo_xcb_picture_t *
+_copy_to_picture (cairo_xcb_surface_t *source)
+{
+ cairo_xcb_picture_t *picture;
+ uint32_t values[] = { 0, 1 };
+
+ if (source->deferred_clear) {
+ cairo_status_t status = _cairo_xcb_surface_clear (source);
+ if (unlikely (status))
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+ }
+
+ picture = _cairo_xcb_picture_create (source->screen,
+ source->xrender_format,
+ source->pixman_format,
+ source->width,
+ source->height);
+ if (unlikely (picture->base.status))
+ return picture;
+
+ _cairo_xcb_connection_render_create_picture (source->connection,
+ picture->picture,
+ source->drawable,
+ source->xrender_format,
+ XCB_RENDER_CP_GRAPHICS_EXPOSURE |
+ XCB_RENDER_CP_SUBWINDOW_MODE,
+ values);
+
+ return picture;
+}
+
+static void
+_cairo_xcb_surface_setup_surface_picture(cairo_xcb_picture_t *picture,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_filter_t filter;
+
+ filter = pattern->base.filter;
+ if (filter != CAIRO_FILTER_NEAREST &&
+ _cairo_matrix_has_unity_scale (&pattern->base.matrix) &&
+ _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.x0)) &&
+ _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.y0)))
+ {
+ filter = CAIRO_FILTER_NEAREST;
+ }
+ _cairo_xcb_picture_set_filter (picture, filter, 0, NULL);
+
+ _cairo_xcb_picture_set_matrix (picture,
+ &pattern->base.matrix, filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+
+
+ _cairo_xcb_picture_set_extend (picture, pattern->base.extend);
+ _cairo_xcb_picture_set_component_alpha (picture, pattern->base.has_component_alpha);
+}
+
+static cairo_xcb_picture_t *
+record_to_picture (cairo_surface_t *target,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_pattern_t tmp_pattern;
+ cairo_xcb_picture_t *picture;
+ cairo_status_t status;
+ cairo_matrix_t matrix;
+ cairo_surface_t *tmp;
+ cairo_surface_t *source;
+ cairo_rectangle_int_t limit;
+ cairo_extend_t extend;
+
+ /* XXX: The following was once more or less copied from cairo-xlibs-ource.c,
+ * record_source() and recording_pattern_get_surface(), can we share a
+ * single version?
+ */
+
+ /* First get the 'real' recording surface and figure out the size for tmp */
+ source = _cairo_pattern_get_source (pattern, &limit);
+ assert (_cairo_surface_is_recording (source));
+
+ if (! _cairo_matrix_is_identity (&pattern->base.matrix)) {
+ double x1, y1, x2, y2;
+
+ matrix = pattern->base.matrix;
+ status = cairo_matrix_invert (&matrix);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x1 = limit.x;
+ y1 = limit.y;
+ x2 = limit.x + limit.width;
+ y2 = limit.y + limit.height;
+
+ _cairo_matrix_transform_bounding_box (&matrix,
+ &x1, &y1, &x2, &y2, NULL);
+
+ limit.x = floor (x1);
+ limit.y = floor (y1);
+ limit.width = ceil (x2) - limit.x;
+ limit.height = ceil (y2) - limit.y;
+ }
+ extend = pattern->base.extend;
+ if (_cairo_rectangle_contains_rectangle (&limit, extents))
+ extend = CAIRO_EXTEND_NONE;
+ if (extend == CAIRO_EXTEND_NONE && ! _cairo_rectangle_intersect (&limit, extents))
+ return _cairo_xcb_transparent_picture ((cairo_xcb_surface_t *) target);
+
+ /* Now draw the recording surface to an xcb surface */
+ tmp = _cairo_surface_create_similar_scratch (target,
+ source->content,
+ limit.width,
+ limit.height);
+ if (tmp->status != CAIRO_STATUS_SUCCESS) {
+ return (cairo_xcb_picture_t *) tmp;
+ }
+
+ cairo_matrix_init_translate (&matrix, limit.x, limit.y);
+ cairo_matrix_multiply (&matrix, &matrix, &pattern->base.matrix);
+
+ status = _cairo_recording_surface_replay_with_clip (source,
+ &matrix, tmp,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (tmp);
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+ }
+
+ /* Now that we have drawn this to an xcb surface, try again with that */
+ _cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base);
+ tmp_pattern.surface = tmp;
+ cairo_matrix_init_translate (&tmp_pattern.base.matrix, -limit.x, -limit.y);
+
+ picture = _copy_to_picture ((cairo_xcb_surface_t *) tmp);
+ if (picture->base.status == CAIRO_STATUS_SUCCESS)
+ _cairo_xcb_surface_setup_surface_picture (picture, &tmp_pattern, extents);
+ cairo_surface_destroy (tmp);
+ return picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_surface_picture (cairo_xcb_surface_t *target,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *source = pattern->surface;
+ cairo_xcb_picture_t *picture = NULL;
+ cairo_xcb_picture_t *filtered_picture;
+ cairo_bool_t is_gaussian_filter = FALSE;
+ cairo_surface_pattern_t *orig_pattern = (cairo_surface_pattern_t *)pattern;
+
+ picture = (cairo_xcb_picture_t *)
+ _cairo_surface_has_snapshot (source, &_cairo_xcb_picture_backend);
+ if (picture != NULL) {
+ if (picture->screen == target->screen) {
+ picture = (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+ _cairo_xcb_surface_setup_surface_picture (picture, pattern, extents);
+ if (pattern->base.filter == CAIRO_FILTER_GAUSSIAN) {
+ orig_pattern->base.filter = CAIRO_FILTER_NEAREST;
+ is_gaussian_filter = TRUE;
+ }
+
+ /* apply gaussian filter if pattern filter is gaussian */
+ if (is_gaussian_filter)
+ orig_pattern->base.filter = CAIRO_FILTER_GAUSSIAN;
+
+ filtered_picture = _cairo_xcb_gaussian_filter (target,
+ picture,
+ &pattern->base);
+
+ /* restore transform */
+ if (filtered_picture != picture) {
+ _cairo_xcb_picture_set_matrix (filtered_picture,
+ &pattern->base.matrix,
+ pattern->base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+
+ _cairo_xcb_picture_set_matrix (picture,
+ &pattern->base.matrix,
+ pattern->base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ }
+
+ _cairo_xcb_surface_setup_surface_picture (filtered_picture, pattern, extents);
+
+ cairo_surface_destroy (&picture->base);
+ return filtered_picture;
+ }
+ picture = NULL;
+ }
+
+ if (source->type == CAIRO_SURFACE_TYPE_XCB)
+ {
+ if (source->backend->type == CAIRO_SURFACE_TYPE_XCB) {
+ cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) source;
+ if (xcb->screen == target->screen && xcb->fallback == NULL) {
+ picture = _copy_to_picture ((cairo_xcb_surface_t *) source);
+ if (unlikely (picture->base.status))
+ return picture;
+ }
+ } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+ cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) sub->target;
+
+ /* XXX repeat interval with source clipping? */
+ if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) {
+ xcb_rectangle_t rect;
+
+ picture = _copy_to_picture (xcb);
+ if (unlikely (picture->base.status))
+ return picture;
+
+ rect.x = sub->extents.x;
+ rect.y = sub->extents.y;
+ rect.width = sub->extents.width;
+ rect.height = sub->extents.height;
+
+ _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
+ picture->picture,
+ 0, 0,
+ 1, &rect);
+ picture->x0 = rect.x;
+ picture->y0 = rect.y;
+ picture->width = rect.width;
+ picture->height = rect.height;
+ }
+ } else if (_cairo_surface_is_snapshot (source)) {
+ cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
+ cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target;
+
+ if (xcb->screen == target->screen && xcb->fallback == NULL) {
+ picture = _copy_to_picture (xcb);
+ if (unlikely (picture->base.status))
+ return picture;
+ }
+ }
+ }
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+ else if (source->type == CAIRO_SURFACE_TYPE_XLIB)
+ {
+ if (source->backend->type == CAIRO_SURFACE_TYPE_XLIB) {
+ cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) source)->xcb;
+ if (xcb->screen == target->screen && xcb->fallback == NULL) {
+ picture = _copy_to_picture (xcb);
+ if (unlikely (picture->base.status))
+ return picture;
+ }
+ } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+ cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) sub->target)->xcb;
+
+ if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) {
+ xcb_rectangle_t rect;
+
+ picture = _copy_to_picture (xcb);
+ if (unlikely (picture->base.status))
+ return picture;
+
+ rect.x = sub->extents.x;
+ rect.y = sub->extents.y;
+ rect.width = sub->extents.width;
+ rect.height = sub->extents.height;
+
+ _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
+ picture->picture,
+ 0, 0,
+ 1, &rect);
+ picture->x0 = rect.x;
+ picture->y0 = rect.y;
+ picture->width = rect.width;
+ picture->height = rect.height;
+ }
+ } else if (_cairo_surface_is_snapshot (source)) {
+ cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
+ cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) snap->target)->xcb;
+
+ if (xcb->screen == target->screen && xcb->fallback == NULL) {
+ picture = _copy_to_picture (xcb);
+ if (unlikely (picture->base.status))
+ return picture;
+ }
+ }
+ }
+#endif
+#if CAIRO_HAS_GL_FUNCTIONS
+ else if (source->type == CAIRO_SURFACE_TYPE_GL)
+ {
+ /* pixmap from texture */
+ }
+#endif
+ else if (source->type == CAIRO_SURFACE_TYPE_RECORDING)
+ {
+ /* We have to skip the call to attach_snapshot() because we possibly
+ * only drew part of the recording surface.
+ * TODO: When can we safely attach a snapshot?
+ */
+
+ /* XXX: do we rely on record implementation of blur? */
+ return record_to_picture(&target->base, pattern, extents);
+ }
+
+ if (picture == NULL) {
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
+ if (unlikely (status))
+ return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+
+ if (image->format != CAIRO_FORMAT_INVALID) {
+ xcb_render_pictformat_t format;
+
+ format = target->screen->connection->standard_formats[image->format];
+
+ picture = _picture_from_image (target, format, image, NULL);
+ _cairo_surface_release_source_image (source, image, image_extra);
+ } else {
+ cairo_image_surface_t *conv;
+ xcb_render_pictformat_t render_format;
+
+ /* XXX XRenderPutImage! */
+
+ conv = _cairo_image_surface_coerce (image);
+ _cairo_surface_release_source_image (source, image, image_extra);
+ if (unlikely (conv->base.status))
+ return (cairo_xcb_picture_t *) conv;
+
+ render_format = target->screen->connection->standard_formats[conv->format];
+ picture = _picture_from_image (target, render_format, conv, NULL);
+ cairo_surface_destroy (&conv->base);
+ }
+
+ if (unlikely (picture->base.status))
+ return picture;
+ }
+
+ _cairo_surface_attach_snapshot (source,
+ &picture->base,
+ NULL);
+
+ if (pattern->base.filter == CAIRO_FILTER_GAUSSIAN) {
+ orig_pattern->base.filter = CAIRO_FILTER_NEAREST;
+ is_gaussian_filter = TRUE;
+ }
+
+ /* apply gaussian filter if pattern filter is gaussian */
+ if (is_gaussian_filter)
+ orig_pattern->base.filter = CAIRO_FILTER_GAUSSIAN;
+
+ filtered_picture = _cairo_xcb_gaussian_filter (target, picture, &pattern->base);
+
+ /* restore transform */
+ if (filtered_picture != picture) {
+ _cairo_xcb_picture_set_matrix (filtered_picture,
+ &pattern->base.matrix,
+ pattern->base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+
+ _cairo_xcb_picture_set_matrix (picture,
+ &pattern->base.matrix,
+ pattern->base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ }
+
+ _cairo_xcb_surface_setup_surface_picture (filtered_picture, pattern, extents);
+
+ cairo_surface_destroy (&picture->base);
+ return filtered_picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ if (pattern == NULL)
+ return _cairo_xcb_white_picture (target);
+
+ if (! _pattern_is_supported (target->connection->flags, pattern))
+ return _render_to_picture (target, pattern, extents);
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _cairo_xcb_solid_picture (target, (cairo_solid_pattern_t *) pattern);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _cairo_xcb_linear_picture (target,
+ (cairo_linear_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _cairo_xcb_radial_picture (target,
+ (cairo_radial_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _cairo_xcb_surface_picture (target, (cairo_surface_pattern_t *) pattern,
+ extents);
+
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_MESH:
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _render_to_picture (target, pattern, extents);
+ }
+}
+
+COMPILE_TIME_ASSERT (sizeof (xcb_rectangle_t) <= sizeof (cairo_box_t));
+
+static cairo_status_t
+_render_fill_boxes (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ cairo_xcb_surface_t *dst = abstract_dst;
+ xcb_rectangle_t stack_xrects[CAIRO_STACK_ARRAY_LENGTH (sizeof (xcb_rectangle_t))];
+ xcb_rectangle_t *xrects = stack_xrects;
+ xcb_render_color_t render_color;
+ int render_op = _render_operator (op);
+ struct _cairo_boxes_chunk *chunk;
+ int max_count;
+
+ render_color.red = color->red_short;
+ render_color.green = color->green_short;
+ render_color.blue = color->blue_short;
+ render_color.alpha = color->alpha_short;
+
+ max_count = 0;
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ if (chunk->count > max_count)
+ max_count = chunk->count;
+ }
+ if (max_count > ARRAY_LENGTH (stack_xrects)) {
+ xrects = _cairo_malloc_ab (max_count, sizeof (xcb_rectangle_t));
+ if (unlikely (xrects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ int i, j;
+
+ for (i = j = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.y);
+
+ if (x2 > x1 && y2 > y1) {
+ xrects[j].x = x1;
+ xrects[j].y = y1;
+ xrects[j].width = x2 - x1;
+ xrects[j].height = y2 - y1;
+ j++;
+ }
+ }
+
+ if (j) {
+ _cairo_xcb_connection_render_fill_rectangles
+ (dst->connection,
+ render_op, dst->picture,
+ render_color, j, xrects);
+ }
+ }
+
+ if (xrects != stack_xrects)
+ free (xrects);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* pixel aligned, non-overlapping boxes */
+static cairo_int_status_t
+_render_composite_boxes (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_pattern_t *mask_pattern,
+ const cairo_rectangle_int_t *extents,
+ const cairo_boxes_t *boxes)
+{
+ cairo_xcb_picture_t *src, *mask;
+ const struct _cairo_boxes_chunk *chunk;
+ xcb_rectangle_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
+ xcb_rectangle_t *clip_boxes;
+ cairo_rectangle_int_t stack_extents;
+ cairo_status_t status;
+ int num_boxes;
+ int render_op;
+
+ render_op = _render_operator (op);
+
+ if (src_pattern == NULL) {
+ src_pattern = mask_pattern;
+ mask_pattern = NULL;
+ }
+
+ /* amalgamate into a single Composite call by setting a clip region */
+ clip_boxes = stack_boxes;
+ if (boxes->num_boxes > ARRAY_LENGTH (stack_boxes)) {
+ clip_boxes = _cairo_malloc_ab (boxes->num_boxes, sizeof (xcb_rectangle_t));
+ if (unlikely (clip_boxes == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
+ status = src->base.status;
+ if (unlikely (status))
+ goto cleanup_boxes;
+
+ num_boxes = 0;
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ int i;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x = _cairo_fixed_integer_round_down (box[i].p1.x);
+ int y = _cairo_fixed_integer_round_down (box[i].p1.y);
+ int width = _cairo_fixed_integer_round_down (box[i].p2.x) - x;
+ int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y;
+
+ if (width && height) {
+ clip_boxes[num_boxes].x = x;
+ clip_boxes[num_boxes].y = y;
+ clip_boxes[num_boxes].width = width;
+ clip_boxes[num_boxes].height = height;
+ num_boxes++;
+ }
+ }
+ }
+
+ if (num_boxes) {
+ if (num_boxes > 1) {
+ _cairo_xcb_connection_render_set_picture_clip_rectangles (dst->connection,
+ dst->picture,
+ 0, 0,
+ num_boxes,
+ clip_boxes);
+ } else {
+ stack_extents.x = clip_boxes[0].x;
+ stack_extents.y = clip_boxes[0].y;
+ stack_extents.width = clip_boxes[0].width;
+ stack_extents.height = clip_boxes[0].height;
+ extents = &stack_extents;
+ }
+
+ if (mask_pattern != NULL) {
+ mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
+ status = mask->base.status;
+ if (unlikely (status))
+ goto cleanup_clip;
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ render_op,
+ src->picture,
+ mask->picture,
+ dst->picture,
+ src->x + extents->x, src->y + extents->y,
+ mask->x + extents->x, mask->y + extents->y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ cairo_surface_destroy (&mask->base);
+ } else {
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ render_op,
+ src->picture,
+ XCB_NONE,
+ dst->picture,
+ src->x + extents->x, src->y + extents->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ }
+
+cleanup_clip:
+
+ if (num_boxes > 1)
+ _cairo_xcb_surface_clear_clip_region (dst);
+ }
+
+ cairo_surface_destroy (&src->base);
+
+cleanup_boxes:
+
+ if (clip_boxes != stack_boxes)
+ free (clip_boxes);
+
+ return status;
+}
+
+
+#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
+#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
+
+static cairo_bool_t
+_line_exceeds_16_16 (const cairo_line_t *line)
+{
+ return
+ line->p1.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p1.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.y >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.y >= CAIRO_FIXED_16_16_MAX;
+}
+
+static void
+_project_line_x_onto_16_16 (const cairo_line_t *line,
+ cairo_fixed_t top,
+ cairo_fixed_t bottom,
+ xcb_render_linefix_t *out)
+{
+ cairo_point_double_t p1, p2;
+ double m;
+
+ p1.x = _cairo_fixed_to_double (line->p1.x);
+ p1.y = _cairo_fixed_to_double (line->p1.y);
+
+ p2.x = _cairo_fixed_to_double (line->p2.x);
+ p2.y = _cairo_fixed_to_double (line->p2.y);
+
+ m = (p2.x - p1.x) / (p2.y - p1.y);
+ out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+ out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+
+typedef struct {
+ cairo_traps_t traps;
+ cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+COMPILE_TIME_ASSERT (sizeof (xcb_render_trapezoid_t) <= sizeof (cairo_trapezoid_t));
+
+static cairo_int_status_t
+_composite_traps (void *closure,
+ cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x, int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ composite_traps_info_t *info = closure;
+ const cairo_traps_t *traps = &info->traps;
+ cairo_xcb_picture_t *src;
+ cairo_format_t format;
+ xcb_render_pictformat_t xrender_format;
+ xcb_render_trapezoid_t *xtraps;
+ int render_reference_x, render_reference_y;
+ cairo_status_t status;
+ int i;
+
+ if (dst->deferred_clear) {
+ status = _cairo_xcb_surface_clear (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+ if (unlikely (src->base.status))
+ return src->base.status;
+
+ if (info->antialias == CAIRO_ANTIALIAS_NONE)
+ format = CAIRO_FORMAT_A1;
+ else
+ format = CAIRO_FORMAT_A8;
+ xrender_format = dst->screen->connection->standard_formats[format];
+
+ xtraps = (xcb_render_trapezoid_t *) traps->traps;
+ for (i = 0; i < traps->num_traps; i++) {
+ cairo_trapezoid_t t = traps->traps[i];
+
+ /* top/bottom will be clamped to surface bounds */
+ xtraps[i].top = _cairo_fixed_to_16_16 (t.top);
+ xtraps[i].top -= dst_y << 16;
+ xtraps[i].bottom = _cairo_fixed_to_16_16 (t.bottom);
+ xtraps[i].bottom -= dst_y << 16;
+
+ /* However, all the other coordinates will have been left untouched so
+ * as not to introduce numerical error. Recompute them if they
+ * exceed the 16.16 limits.
+ */
+ if (unlikely (_line_exceeds_16_16 (&t.left))) {
+ _project_line_x_onto_16_16 (&t.left,
+ t.top,
+ t.bottom,
+ &xtraps[i].left);
+ xtraps[i].left.p1.y = xtraps[i].top;
+ xtraps[i].left.p2.y = xtraps[i].bottom;
+ } else {
+ xtraps[i].left.p1.x = _cairo_fixed_to_16_16 (t.left.p1.x);
+ xtraps[i].left.p1.y = _cairo_fixed_to_16_16 (t.left.p1.y);
+ xtraps[i].left.p2.x = _cairo_fixed_to_16_16 (t.left.p2.x);
+ xtraps[i].left.p2.y = _cairo_fixed_to_16_16 (t.left.p2.y);
+ }
+ xtraps[i].left.p1.x -= dst_x << 16;
+ xtraps[i].left.p1.y -= dst_y << 16;
+ xtraps[i].left.p2.x -= dst_x << 16;
+ xtraps[i].left.p2.y -= dst_y << 16;
+
+ if (unlikely (_line_exceeds_16_16 (&t.right))) {
+ _project_line_x_onto_16_16 (&t.right,
+ t.top,
+ t.bottom,
+ &xtraps[i].right);
+ xtraps[i].right.p1.y = xtraps[i].top;
+ xtraps[i].right.p2.y = xtraps[i].bottom;
+ } else {
+ xtraps[i].right.p1.x = _cairo_fixed_to_16_16 (t.right.p1.x);
+ xtraps[i].right.p1.y = _cairo_fixed_to_16_16 (t.right.p1.y);
+ xtraps[i].right.p2.x = _cairo_fixed_to_16_16 (t.right.p2.x);
+ xtraps[i].right.p2.y = _cairo_fixed_to_16_16 (t.right.p2.y);
+ }
+ xtraps[i].right.p1.x -= dst_x << 16;
+ xtraps[i].right.p1.y -= dst_y << 16;
+ xtraps[i].right.p2.x -= dst_x << 16;
+ xtraps[i].right.p2.y -= dst_y << 16;
+ }
+
+ if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
+ render_reference_x = xtraps[0].left.p1.x >> 16;
+ render_reference_y = xtraps[0].left.p1.y >> 16;
+ } else {
+ render_reference_x = xtraps[0].left.p2.x >> 16;
+ render_reference_y = xtraps[0].left.p2.y >> 16;
+ }
+ render_reference_x += src->x + dst_x;
+ render_reference_y += src->y + dst_y;
+
+ _cairo_xcb_surface_set_precision (dst, info->antialias);
+ _cairo_xcb_connection_render_trapezoids (dst->connection,
+ _render_operator (op),
+ src->picture,
+ dst->picture,
+ xrender_format,
+ render_reference_x,
+ render_reference_y,
+ traps->num_traps, xtraps);
+
+ cairo_surface_destroy (&src->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* low-level composite driver */
+
+static cairo_xcb_surface_t *
+get_clip_surface (const cairo_clip_t *clip,
+ cairo_xcb_surface_t *target,
+ int *tx, int *ty)
+{
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ surface = _cairo_surface_create_similar_solid (&target->base,
+ CAIRO_CONTENT_ALPHA,
+ clip->extents.width,
+ clip->extents.height,
+ CAIRO_COLOR_WHITE);
+ if (unlikely (surface->status))
+ return (cairo_xcb_surface_t *) surface;
+
+ assert (surface->backend == &_cairo_xcb_surface_backend);
+ status = _cairo_clip_combine_with_surface (clip, surface,
+ clip->extents.x, clip->extents.y);
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ surface = _cairo_surface_create_in_error (status);
+ }
+
+ *tx = clip->extents.x;
+ *ty = clip->extents.y;
+
+ return (cairo_xcb_surface_t *) surface;
+}
+
+typedef cairo_int_status_t
+(*xcb_draw_func_t) (void *closure,
+ cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip);
+
+static void do_unaligned_row(void (*blt)(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage),
+ void *closure,
+ const cairo_box_t *b,
+ int tx, int y, int h,
+ uint16_t coverage)
+{
+ int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
+ int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
+ if (x2 > x1) {
+ if (! _cairo_fixed_is_integer (b->p1.x)) {
+ blt(closure, x1, y, 1, h,
+ coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
+ x1++;
+ }
+
+ if (x2 > x1)
+ blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
+
+ if (! _cairo_fixed_is_integer (b->p2.x))
+ blt(closure, x2, y, 1, h,
+ coverage * _cairo_fixed_fractional_part (b->p2.x));
+ } else
+ blt(closure, x1, y, 1, h,
+ coverage * (b->p2.x - b->p1.x));
+}
+
+static void do_unaligned_box(void (*blt)(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage),
+ void *closure,
+ const cairo_box_t *b, int tx, int ty)
+{
+ int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
+ int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
+ if (y2 > y1) {
+ if (! _cairo_fixed_is_integer (b->p1.y)) {
+ do_unaligned_row(blt, closure, b, tx, y1, 1,
+ 256 - _cairo_fixed_fractional_part (b->p1.y));
+ y1++;
+ }
+
+ if (y2 > y1)
+ do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
+
+ if (! _cairo_fixed_is_integer (b->p2.y))
+ do_unaligned_row(blt, closure, b, tx, y2, 1,
+ _cairo_fixed_fractional_part (b->p2.y));
+ } else
+ do_unaligned_row(blt, closure, b, tx, y1, 1,
+ b->p2.y - b->p1.y);
+}
+
+
+static void blt_in(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ cairo_xcb_surface_t *mask = closure;
+ xcb_render_color_t color;
+ xcb_rectangle_t rect;
+
+ if (coverage == 0xffff)
+ return;
+
+ color.red = color.green = color.blue = 0;
+ color.alpha = coverage;
+
+ rect.x = x;
+ rect.y = y;
+ rect.width = w;
+ rect.height = h;
+
+ _cairo_xcb_connection_render_fill_rectangles (mask->connection,
+ XCB_RENDER_PICT_OP_IN,
+ mask->picture,
+ color, 1, &rect);
+}
+
+static cairo_xcb_surface_t *
+_create_composite_mask (cairo_clip_t *clip,
+ xcb_draw_func_t draw_func,
+ xcb_draw_func_t mask_func,
+ void *draw_closure,
+ cairo_xcb_surface_t *dst,
+ const cairo_rectangle_int_t*extents)
+{
+ cairo_xcb_surface_t *surface;
+ cairo_bool_t need_clip_combine;
+ cairo_int_status_t status;
+
+ surface = (cairo_xcb_surface_t *)
+ _cairo_xcb_surface_create_similar (dst, CAIRO_CONTENT_ALPHA,
+ extents->width, extents->height);
+ if (unlikely (surface->base.status))
+ return surface;
+
+ _cairo_xcb_surface_ensure_picture (surface);
+
+ surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT;
+ surface->deferred_clear = TRUE;
+ surface->base.is_clear = TRUE;
+
+ if (mask_func) {
+ status = mask_func (draw_closure, surface,
+ CAIRO_OPERATOR_ADD, NULL,
+ extents->x, extents->y,
+ extents, clip);
+ if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
+ return surface;
+ }
+
+ /* Is it worth setting the clip region here? */
+ status = draw_func (draw_closure, surface,
+ CAIRO_OPERATOR_ADD, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
+ }
+
+ if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
+ int i;
+
+ for (i = 0; i < clip->num_boxes; i++) {
+ cairo_box_t *b = &clip->boxes[i];
+
+ if (! _cairo_fixed_is_integer (b->p1.x) ||
+ ! _cairo_fixed_is_integer (b->p1.y) ||
+ ! _cairo_fixed_is_integer (b->p2.x) ||
+ ! _cairo_fixed_is_integer (b->p2.y))
+ {
+ do_unaligned_box(blt_in, surface, b, extents->x, extents->y);
+ }
+ }
+
+ need_clip_combine = clip->path != NULL;
+ } else
+ need_clip_combine = ! _cairo_clip_is_region (clip);
+
+ if (need_clip_combine) {
+ status = _cairo_clip_combine_with_surface (clip, &surface->base,
+ extents->x, extents->y);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
+ }
+ }
+
+ return surface;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ xcb_draw_func_t draw_func,
+ xcb_draw_func_t mask_func,
+ void *draw_closure,
+ cairo_xcb_surface_t *dst,
+ const cairo_rectangle_int_t*extents)
+{
+ cairo_xcb_surface_t *mask;
+ cairo_xcb_picture_t *src;
+ cairo_status_t status;
+
+ mask = _create_composite_mask (clip,
+ draw_func, mask_func, draw_closure,
+ dst, extents);
+ if (unlikely (mask->base.status)) {
+ status = mask->base.status;
+ cairo_surface_destroy (&mask->base);
+ return status;
+ }
+
+ if (pattern != NULL || dst->base.content != CAIRO_CONTENT_ALPHA) {
+ src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+ if (unlikely (src->base.status)) {
+ cairo_surface_destroy (&mask->base);
+ return src->base.status;
+ }
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ _render_operator (op),
+ src->picture,
+ mask->picture,
+ dst->picture,
+ extents->x + src->x, extents->y + src->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ cairo_surface_destroy (&src->base);
+ } else {
+ _cairo_xcb_connection_render_composite (dst->connection,
+ _render_operator (op),
+ mask->picture,
+ XCB_NONE,
+ dst->picture,
+ 0, 0,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ }
+ cairo_surface_destroy (&mask->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ xcb_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_xcb_surface_t *dst,
+ const cairo_rectangle_int_t*extents)
+{
+ cairo_xcb_surface_t *tmp;
+ cairo_xcb_surface_t *clip_surface;
+ int clip_x, clip_y;
+ xcb_render_picture_t clip_picture;
+ cairo_status_t status;
+
+ tmp = (cairo_xcb_surface_t *)
+ _cairo_xcb_surface_create_similar (dst, dst->base.content,
+ extents->width, extents->height);
+ if (unlikely (tmp->base.status)) {
+ status = tmp->base.status;
+ cairo_surface_destroy (&tmp->base);
+ return status;
+ }
+
+ /* create_similar() could have done a fallback to an image surface */
+ assert (tmp->base.backend == &_cairo_xcb_surface_backend);
+
+ _cairo_xcb_surface_ensure_picture (tmp);
+
+ if (pattern == NULL) {
+ status = (*draw_func) (draw_closure, tmp,
+ CAIRO_OPERATOR_ADD, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ } else {
+ /* Initialize the temporary surface from the destination surface */
+ if (! dst->base.is_clear ||
+ (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) == 0)
+ {
+ /* XCopyArea may actually be quicker here.
+ * A good driver should translate if appropriate.
+ */
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_SRC,
+ dst->picture,
+ XCB_NONE,
+ tmp->picture,
+ extents->x, extents->y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height);
+ }
+ else
+ {
+ xcb_render_color_t clear;
+ xcb_rectangle_t xrect;
+
+ clear.red = clear.green = clear.blue = clear.alpha = 0;
+
+ xrect.x = xrect.y = 0;
+ xrect.width = extents->width;
+ xrect.height = extents->height;
+
+ _cairo_xcb_connection_render_fill_rectangles (dst->connection,
+ XCB_RENDER_PICT_OP_CLEAR,
+ dst->picture,
+ clear, 1, &xrect);
+ }
+
+ status = (*draw_func) (draw_closure, tmp, op, pattern,
+ extents->x, extents->y,
+ extents, NULL);
+ }
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ clip_surface = get_clip_surface (clip, dst, &clip_x, &clip_y);
+ status = clip_surface->base.status;
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ assert (clip_surface->base.backend == &_cairo_xcb_surface_backend);
+ clip_picture = clip_surface->picture;
+ assert (clip_picture != XCB_NONE);
+
+ if (dst->base.is_clear) {
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_SRC,
+ tmp->picture, clip_picture, dst->picture,
+ 0, 0,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ } else {
+ /* Punch the clip out of the destination */
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_OUT_REVERSE,
+ clip_picture, XCB_NONE, dst->picture,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ /* Now add the two results together */
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_ADD,
+ tmp->picture, clip_picture, dst->picture,
+ 0, 0,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ }
+ cairo_surface_destroy (&clip_surface->base);
+
+ CLEANUP_SURFACE:
+ cairo_surface_destroy (&tmp->base);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_clip_t *clip,
+ const cairo_pattern_t *pattern,
+ xcb_draw_func_t draw_func,
+ xcb_draw_func_t mask_func,
+ void *draw_closure,
+ cairo_xcb_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_xcb_surface_t *mask;
+ cairo_xcb_picture_t *src;
+ cairo_status_t status;
+
+ /* Create a surface that is mask IN clip */
+ mask = _create_composite_mask (clip,
+ draw_func, mask_func, draw_closure,
+ dst, extents);
+ if (unlikely (mask->base.status)) {
+ status = mask->base.status;
+ cairo_surface_destroy (&mask->base);
+ return status;
+ }
+
+ src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+ if (unlikely (src->base.status)) {
+ cairo_surface_destroy (&mask->base);
+ return src->base.status;
+ }
+
+ if (dst->base.is_clear) {
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_SRC,
+ src->picture,
+ mask->picture,
+ dst->picture,
+ extents->x + src->x, extents->y + src->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ } else {
+ /* Compute dest' = dest OUT (mask IN clip) */
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_OUT_REVERSE,
+ mask->picture,
+ XCB_NONE,
+ dst->picture,
+ 0, 0, 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ /* Now compute (src IN (mask IN clip)) ADD dest' */
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_ADD,
+ src->picture,
+ mask->picture,
+ dst->picture,
+ extents->x + src->x, extents->y + src->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ }
+
+ cairo_surface_destroy (&src->base);
+ cairo_surface_destroy (&mask->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+ int iop = op;
+ switch (iop) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ return dst->is_clear &&
+ dst->content == CAIRO_CONTENT_ALPHA &&
+ _cairo_pattern_is_opaque_solid (pattern) &&
+ can_reduce_alpha_op (op);
+}
+
+static cairo_status_t
+_cairo_xcb_surface_fixup_unbounded (cairo_xcb_surface_t *dst,
+ const cairo_composite_rectangles_t *rects)
+{
+ xcb_rectangle_t xrects[4];
+ int n;
+
+ if (rects->bounded.width == rects->unbounded.width &&
+ rects->bounded.height == rects->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ n = 0;
+ if (rects->bounded.width == 0 || rects->bounded.height == 0) {
+ xrects[n].x = rects->unbounded.x;
+ xrects[n].width = rects->unbounded.width;
+ xrects[n].y = rects->unbounded.y;
+ xrects[n].height = rects->unbounded.height;
+ n++;
+ } else {
+ /* top */
+ if (rects->bounded.y != rects->unbounded.y) {
+ xrects[n].x = rects->unbounded.x;
+ xrects[n].width = rects->unbounded.width;
+ xrects[n].y = rects->unbounded.y;
+ xrects[n].height = rects->bounded.y - rects->unbounded.y;
+ n++;
+ }
+ /* left */
+ if (rects->bounded.x != rects->unbounded.x) {
+ xrects[n].x = rects->unbounded.x;
+ xrects[n].width = rects->bounded.x - rects->unbounded.x;
+ xrects[n].y = rects->bounded.y;
+ xrects[n].height = rects->bounded.height;
+ n++;
+ }
+ /* right */
+ if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+ xrects[n].x = rects->bounded.x + rects->bounded.width;
+ xrects[n].width = rects->unbounded.x + rects->unbounded.width - xrects[n].x;
+ xrects[n].y = rects->bounded.y;
+ xrects[n].height = rects->bounded.height;
+ n++;
+ }
+ /* bottom */
+ if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+ xrects[n].x = rects->unbounded.x;
+ xrects[n].width = rects->unbounded.width;
+ xrects[n].y = rects->bounded.y + rects->bounded.height;
+ xrects[n].height = rects->unbounded.y + rects->unbounded.height - xrects[n].y;
+ n++;
+ }
+ }
+
+ if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
+ xcb_render_color_t color;
+
+ color.red = 0;
+ color.green = 0;
+ color.blue = 0;
+ color.alpha = 0;
+
+ _cairo_xcb_connection_render_fill_rectangles (dst->connection,
+ XCB_RENDER_PICT_OP_CLEAR,
+ dst->picture,
+ color, n, xrects);
+ } else {
+ int i;
+ cairo_xcb_picture_t *src;
+
+ src = _cairo_xcb_transparent_picture (dst);
+ if (unlikely (src->base.status))
+ return src->base.status;
+
+ for (i = 0; i < n; i++) {
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_CLEAR,
+ src->picture, XCB_NONE, dst->picture,
+ 0, 0,
+ 0, 0,
+ xrects[i].x, xrects[i].y,
+ xrects[i].width, xrects[i].height);
+ }
+ cairo_surface_destroy (&src->base);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_fixup_unbounded_with_mask (cairo_xcb_surface_t *dst,
+ const cairo_composite_rectangles_t *rects,
+ cairo_clip_t *clip)
+{
+ cairo_xcb_surface_t *mask;
+ int mask_x, mask_y;
+
+ mask = get_clip_surface (clip, dst, &mask_x, &mask_y);
+ if (unlikely (mask->base.status))
+ return mask->base.status;
+
+ /* top */
+ if (rects->bounded.y != rects->unbounded.y) {
+ int x = rects->unbounded.x;
+ int y = rects->unbounded.y;
+ int width = rects->unbounded.width;
+ int height = rects->bounded.y - y;
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_OUT_REVERSE,
+ mask->picture, XCB_NONE, dst->picture,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* left */
+ if (rects->bounded.x != rects->unbounded.x) {
+ int x = rects->unbounded.x;
+ int y = rects->bounded.y;
+ int width = rects->bounded.x - x;
+ int height = rects->bounded.height;
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_OUT_REVERSE,
+ mask->picture, XCB_NONE, dst->picture,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* right */
+ if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+ int x = rects->bounded.x + rects->bounded.width;
+ int y = rects->bounded.y;
+ int width = rects->unbounded.x + rects->unbounded.width - x;
+ int height = rects->bounded.height;
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_OUT_REVERSE,
+ mask->picture, XCB_NONE, dst->picture,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* bottom */
+ if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+ int x = rects->unbounded.x;
+ int y = rects->bounded.y + rects->bounded.height;
+ int width = rects->unbounded.width;
+ int height = rects->unbounded.y + rects->unbounded.height - y;
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ XCB_RENDER_PICT_OP_OUT_REVERSE,
+ mask->picture, XCB_NONE, dst->picture,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ cairo_surface_destroy (&mask->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_fixup_unbounded_boxes (cairo_xcb_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip,
+ cairo_boxes_t *boxes)
+{
+ cairo_boxes_t clear;
+ cairo_box_t box;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ if (boxes->num_boxes <= 1 && clip == NULL)
+ return _cairo_xcb_surface_fixup_unbounded (dst, extents);
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (clip == NULL) {
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+
+ tmp.chunks.next = NULL;
+ } else {
+ _cairo_boxes_init_with_clip (&clear, clip);
+
+ status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (&clear,
+ CAIRO_ANTIALIAS_DEFAULT,
+ &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&clear);
+ return status;
+ }
+ }
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ }
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES)
+ status = _render_fill_boxes (dst,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+ else
+ status = _cairo_xcb_surface_core_fill_boxes (dst,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear);
+ }
+
+ _cairo_boxes_fini (&clear);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst)
+{
+ xcb_gcontext_t gc;
+ xcb_rectangle_t rect;
+ cairo_status_t status;
+
+ status = _cairo_xcb_connection_acquire (dst->connection);
+ if (unlikely (status))
+ return status;
+
+ rect.x = rect.y = 0;
+ rect.width = dst->width;
+ rect.height = dst->height;
+
+ if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
+ xcb_render_color_t color;
+ uint8_t op;
+
+ color.red = dst->deferred_clear_color.red_short;
+ color.green = dst->deferred_clear_color.green_short;
+ color.blue = dst->deferred_clear_color.blue_short;
+ color.alpha = dst->deferred_clear_color.alpha_short;
+
+ if (color.alpha == 0)
+ op = XCB_RENDER_PICT_OP_CLEAR;
+ else
+ op = XCB_RENDER_PICT_OP_SRC;
+
+ _cairo_xcb_surface_ensure_picture (dst);
+ _cairo_xcb_connection_render_fill_rectangles (dst->connection,
+ op, dst->picture, color,
+ 1, &rect);
+ } else {
+ gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
+
+ /* XXX color */
+ _cairo_xcb_connection_poly_fill_rectangle (dst->connection,
+ dst->drawable, gc,
+ 1, &rect);
+
+ _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
+ }
+
+ _cairo_xcb_connection_release (dst->connection);
+
+ dst->deferred_clear = FALSE;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+enum {
+ NEED_CLIP_REGION = 0x1,
+ NEED_CLIP_SURFACE = 0x2,
+ FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = NEED_CLIP_REGION;
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+ return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = 0;
+ if (! extents->is_bounded) {
+ flags |= NEED_CLIP_REGION;
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+ }
+ if (extents->clip->path != NULL)
+ flags |= NEED_CLIP_SURFACE;
+ return flags;
+}
+
+static cairo_status_t
+_clip_and_composite (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ xcb_draw_func_t draw_func,
+ xcb_draw_func_t mask_func,
+ void *draw_closure,
+ cairo_composite_rectangles_t*extents,
+ unsigned int need_clip)
+{
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ status = _cairo_xcb_connection_acquire (dst->connection);
+ if (unlikely (status))
+ return status;
+
+ if (dst->deferred_clear) {
+ status = _cairo_xcb_surface_clear (dst);
+ if (unlikely (status)) {
+ _cairo_xcb_connection_release (dst->connection);
+ return status;
+ }
+ }
+
+ _cairo_xcb_surface_ensure_picture (dst);
+
+ if (need_clip & NEED_CLIP_REGION) {
+ clip_region = _cairo_clip_get_region (extents->clip);
+ if ((need_clip & FORCE_CLIP_REGION) == 0 && clip_region != NULL &&
+ cairo_region_contains_rectangle (clip_region,
+ &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+ clip_region = NULL;
+ if (clip_region != NULL) {
+ status = _cairo_xcb_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status)) {
+ _cairo_xcb_connection_release (dst->connection);
+ return status;
+ }
+ }
+ }
+
+ if (reduce_alpha_op (&dst->base, op, src)) {
+ op = CAIRO_OPERATOR_ADD;
+ src = NULL;
+ }
+
+ if (extents->bounded.width != 0 && extents->bounded.height != 0) {
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = _clip_and_composite_source (extents->clip, src,
+ draw_func, mask_func, draw_closure,
+ dst, &extents->bounded);
+ } else {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ op = CAIRO_OPERATOR_DEST_OUT;
+ src = NULL;
+ }
+
+ if (need_clip & NEED_CLIP_SURFACE) {
+ if (extents->is_bounded) {
+ status = _clip_and_composite_with_mask (extents->clip, op, src,
+ draw_func,
+ mask_func,
+ draw_closure,
+ dst, &extents->bounded);
+ } else {
+ status = _clip_and_composite_combine (extents->clip, op, src,
+ draw_func, draw_closure,
+ dst, &extents->bounded);
+ }
+ } else {
+ status = draw_func (draw_closure,
+ dst, op, src,
+ 0, 0,
+ &extents->bounded,
+ extents->clip);
+ }
+ }
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+ if (need_clip & NEED_CLIP_SURFACE)
+ status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, extents->clip);
+ else
+ status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
+ }
+
+ if (clip_region)
+ _cairo_xcb_surface_clear_clip_region (dst);
+
+ _cairo_xcb_connection_release (dst->connection);
+
+ return status;
+}
+
+static cairo_status_t
+_core_boxes (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ if (! boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_clip_is_region (extents->clip))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ return _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, boxes);
+
+ if (op == CAIRO_OPERATOR_OVER) {
+ if (dst->base.is_clear || _cairo_pattern_is_opaque (src, &extents->bounded))
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+ if (op != CAIRO_OPERATOR_SOURCE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
+ return _cairo_xcb_surface_core_fill_boxes (dst,
+ &((cairo_solid_pattern_t *) src)->color,
+ boxes);
+ }
+
+ return _cairo_xcb_surface_core_copy_boxes (dst, src, &extents->bounded, boxes);
+}
+
+static cairo_status_t
+_composite_boxes (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_clip_t *clip = extents->clip;
+ cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (clip);
+ cairo_status_t status;
+
+ /* If the boxes are not pixel-aligned, we will need to compute a real mask */
+ if (! boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (need_clip_mask &&
+ (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = _cairo_xcb_connection_acquire (dst->connection);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xcb_surface_ensure_picture (dst);
+ if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES && ! need_clip_mask &&
+ (op == CAIRO_OPERATOR_CLEAR || src->type == CAIRO_PATTERN_TYPE_SOLID))
+ {
+ const cairo_color_t *color;
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ color = CAIRO_COLOR_TRANSPARENT;
+ else
+ color = &((cairo_solid_pattern_t *) src)->color;
+
+ status = _render_fill_boxes (dst, op, color, boxes);
+ }
+ else
+ {
+ cairo_surface_pattern_t mask;
+
+ if (need_clip_mask) {
+ cairo_xcb_surface_t *clip_surface;
+ int clip_x, clip_y;
+
+ clip_surface = get_clip_surface (extents->clip, dst,
+ &clip_x, &clip_y);
+ if (unlikely (clip_surface->base.status))
+ return clip_surface->base.status;
+
+ _cairo_pattern_init_for_surface (&mask, &clip_surface->base);
+ mask.base.filter = CAIRO_FILTER_NEAREST;
+ cairo_matrix_init_translate (&mask.base.matrix,
+ -clip_x,
+ -clip_y);
+ cairo_surface_destroy (&clip_surface->base);
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = NULL;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+ }
+
+ status = _render_composite_boxes (dst, op, src,
+ need_clip_mask ? &mask.base : NULL,
+ &extents->bounded, boxes);
+
+ if (need_clip_mask)
+ _cairo_pattern_fini (&mask.base);
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+ status =
+ _cairo_xcb_surface_fixup_unbounded_boxes (dst, extents,
+ clip, boxes);
+ }
+
+ _cairo_xcb_connection_release (dst->connection);
+
+ return status;
+}
+
+static cairo_bool_t
+cairo_boxes_for_each_box (cairo_boxes_t *boxes,
+ cairo_bool_t (*func) (cairo_box_t *box,
+ void *data),
+ void *data)
+{
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++)
+ if (! func (&chunk->base[i], data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+struct _image_contains_box {
+ int width, height;
+ int tx, ty;
+};
+
+static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure)
+{
+ struct _image_contains_box *data = closure;
+
+ /* The box is pixel-aligned so the truncation is safe. */
+ return
+ _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 &&
+ _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 &&
+ _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width &&
+ _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height;
+}
+
+struct _image_upload_box {
+ cairo_xcb_surface_t *surface;
+ cairo_image_surface_t *image;
+ xcb_gcontext_t gc;
+ int tx, ty;
+};
+
+static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure)
+{
+ const struct _image_upload_box *iub = closure;
+ /* The box is pixel-aligned so the truncation is safe. */
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+ int bpp = PIXMAN_FORMAT_BPP (iub->image->pixman_format);
+ int len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp);
+ if (len == iub->image->stride) {
+ _cairo_xcb_connection_put_image (iub->surface->connection,
+ iub->surface->drawable,
+ iub->gc,
+ width, height,
+ x, y,
+ iub->image->depth,
+ iub->image->stride,
+ iub->image->data +
+ (y + iub->ty) * iub->image->stride +
+ (x + iub->tx) * bpp/8);
+ } else {
+ _cairo_xcb_connection_put_subimage (iub->surface->connection,
+ iub->surface->drawable,
+ iub->gc,
+ x + iub->tx,
+ y + iub->ty,
+ width, height,
+ bpp / 8,
+ iub->image->stride,
+ x, y,
+ iub->image->depth,
+ iub->image->data);
+ }
+
+ return TRUE;
+}
+
+static cairo_status_t
+_upload_image_inplace (cairo_xcb_surface_t *surface,
+ const cairo_pattern_t *source,
+ cairo_boxes_t *boxes)
+{
+ const cairo_surface_pattern_t *pattern;
+ struct _image_contains_box icb;
+ struct _image_upload_box iub;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ int tx, ty;
+
+ if (! boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Have we already upload this image to a pixmap? */
+ {
+ cairo_xcb_picture_t *snapshot;
+
+ snapshot = (cairo_xcb_picture_t *)
+ _cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_picture_backend);
+ if (snapshot != NULL) {
+ if (snapshot->screen == surface->screen)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ image = (cairo_image_surface_t *) pattern->surface;
+ if (image->format == CAIRO_FORMAT_INVALID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (image->depth != surface->depth)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Check that the data is entirely within the image */
+ icb.width = image->width;
+ icb.height = image->height;
+ icb.tx = tx;
+ icb.ty = ty;
+ if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->deferred_clear) {
+ status = _cairo_xcb_surface_clear (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (unlikely (status))
+ return status;
+
+ iub.surface = surface;
+ iub.image = image;
+ iub.gc = _cairo_xcb_screen_get_gc (surface->screen,
+ surface->drawable,
+ image->depth);
+ iub.tx = tx;
+ iub.ty = ty;
+ cairo_boxes_for_each_box (boxes, image_upload_box, &iub);
+
+ _cairo_xcb_screen_put_gc (surface->screen, image->depth, iub.gc);
+ _cairo_xcb_connection_release (surface->connection);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+trim_extents_to_traps (cairo_composite_rectangles_t *extents,
+ cairo_traps_t *traps)
+{
+ cairo_box_t box;
+
+ /* X trims the affected area to the extents of the trapezoids, so
+ * we need to compensate when fixing up the unbounded area.
+ */
+ _cairo_traps_extents (traps, &box);
+ return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_bool_t
+_mono_edge_is_vertical (const cairo_line_t *line)
+{
+ return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
+}
+
+static cairo_bool_t
+_traps_are_pixel_aligned (cairo_traps_t *traps,
+ cairo_antialias_t antialias)
+{
+ int i;
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
+ ! _mono_edge_is_vertical (&traps->traps[i].right))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ } else {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
+ traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
+ ! _cairo_fixed_is_integer (traps->traps[i].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_boxes_for_traps (cairo_boxes_t *boxes,
+ cairo_traps_t *traps,
+ cairo_antialias_t antialias)
+{
+ int i;
+
+ _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
+_composite_polygon (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_polygon_t *polygon,
+ cairo_antialias_t antialias,
+ cairo_fill_rule_t fill_rule,
+ cairo_composite_rectangles_t *extents)
+{
+ composite_traps_info_t traps;
+ cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip);
+ cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+ cairo_status_t status;
+
+ if (polygon->num_edges == 0) {
+ status = CAIRO_STATUS_SUCCESS;
+
+ if (! extents->is_bounded) {
+ if (cairo_region_contains_rectangle (clip_region, &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+ clip_region = NULL;
+
+ if (clip_surface == FALSE) {
+ if (clip_region != NULL) {
+ status = _cairo_xcb_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
+
+ if (clip_region != NULL)
+ _cairo_xcb_surface_clear_clip_region (dst);
+ } else {
+ status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst,
+ extents,
+ extents->clip);
+ }
+ }
+
+ return status;
+ }
+
+ if (extents->clip->path != NULL && extents->is_bounded) {
+ cairo_polygon_t clipper;
+ cairo_fill_rule_t clipper_fill_rule;
+ cairo_antialias_t clipper_antialias;
+
+ status = _cairo_clip_get_polygon (extents->clip,
+ &clipper,
+ &clipper_fill_rule,
+ &clipper_antialias);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ if (clipper_antialias == antialias) {
+ status = _cairo_polygon_intersect (polygon, fill_rule,
+ &clipper, clipper_fill_rule);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip);
+ _cairo_clip_destroy (extents->clip);
+ extents->clip = clip;
+
+ fill_rule = CAIRO_FILL_RULE_WINDING;
+ }
+ _cairo_polygon_fini (&clipper);
+ }
+ }
+ }
+
+ _cairo_traps_init (&traps.traps);
+
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
+ if (unlikely (status))
+ goto CLEANUP_TRAPS;
+
+ if (traps.traps.has_intersections) {
+ if (traps.traps.is_rectangular)
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
+ else if (traps.traps.is_rectilinear)
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
+ else
+ status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ goto CLEANUP_TRAPS;
+ }
+
+ /* Use a fast path if the trapezoids consist of a simple region,
+ * but we can only do this if we do not have a clip surface, or can
+ * substitute the mask with the clip.
+ */
+ if (traps.traps.maybe_region &&
+ _traps_are_pixel_aligned (&traps.traps, antialias) &&
+ (! clip_surface ||
+ (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE)))
+ {
+ cairo_boxes_t boxes;
+
+ _boxes_for_traps (&boxes, &traps.traps, antialias);
+ status = _clip_and_composite_boxes (dst, op, source, &boxes, extents);
+ }
+ else
+ {
+ /* Otherwise render the trapezoids to a mask and composite in the usual
+ * fashion.
+ */
+ traps.antialias = antialias;
+ status = trim_extents_to_traps (extents, &traps.traps);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ unsigned int flags = 0;
+
+ /* For unbounded operations, the X11 server will estimate the
+ * affected rectangle and apply the operation to that. However,
+ * there are cases where this is an overestimate (e.g. the
+ * clip-fill-{eo,nz}-unbounded test).
+ *
+ * The clip will trim that overestimate to our expectations.
+ */
+ if (! extents->is_bounded)
+ flags |= FORCE_CLIP_REGION;
+
+ status = _clip_and_composite (dst, op, source, _composite_traps,
+ NULL, &traps, extents,
+ need_unbounded_clip (extents) | flags);
+ }
+ }
+
+CLEANUP_TRAPS:
+ _cairo_traps_fini (&traps.traps);
+
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ cairo_composite_rectangles_t *extents)
+{
+ composite_traps_info_t info;
+ cairo_int_status_t status;
+
+ if (boxes->num_boxes == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ (dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))))
+ {
+ if (boxes->num_boxes == 1 &&
+ extents->bounded.width == dst->width &&
+ extents->bounded.height == dst->height)
+ {
+ op = CAIRO_OPERATOR_SOURCE;
+ dst->deferred_clear = FALSE;
+ }
+
+ status = _upload_image_inplace (dst, src, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+ if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS &&
+ extents->clip->path != NULL && extents->is_bounded) {
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+ cairo_clip_t *clip;
+
+ clip = _cairo_clip_copy (extents->clip);
+ clip = _cairo_clip_intersect_boxes (clip, boxes);
+ status = _cairo_clip_get_polygon (clip, &polygon,
+ &fill_rule, &antialias);
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *saved_clip = extents->clip;
+ extents->clip = clip;
+ status = _composite_polygon (dst, op, src,
+ &polygon,
+ antialias,
+ fill_rule,
+ extents);
+ if (extents->clip != clip)
+ clip = NULL;
+ extents->clip = saved_clip;
+ _cairo_polygon_fini (&polygon);
+ }
+ if (clip)
+ _cairo_clip_destroy (clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ if (dst->deferred_clear) {
+ status = _cairo_xcb_surface_clear (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (boxes->is_pixel_aligned &&
+ _cairo_clip_is_region (extents->clip) &&
+ op == CAIRO_OPERATOR_SOURCE) {
+ status = _upload_image_inplace (dst, src, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
+ return _core_boxes (dst, op, src, boxes, extents);
+
+ /* Use a fast path if the boxes are pixel aligned */
+ status = _composite_boxes (dst, op, src, boxes, extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Otherwise render via a mask and composite in the usual fashion. */
+ status = _cairo_traps_init_boxes (&info.traps, boxes);
+ if (unlikely (status))
+ return status;
+
+ info.antialias = CAIRO_ANTIALIAS_DEFAULT;
+ status = trim_extents_to_traps (extents, &info.traps);
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+ status = _clip_and_composite (dst, op, src,
+ _composite_traps, NULL, &info,
+ extents, need_unbounded_clip (extents));
+ }
+
+ _cairo_traps_fini (&info.traps);
+ return status;
+}
+
+static cairo_int_status_t
+_composite_mask (void *closure,
+ cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ const cairo_pattern_t *mask_pattern = closure;
+ cairo_xcb_picture_t *src, *mask = NULL;
+ cairo_status_t status;
+
+ if (dst->base.is_clear) {
+ if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE && clip == NULL)
+ dst->deferred_clear = FALSE;
+
+ if (dst->deferred_clear) {
+ status = _cairo_xcb_surface_clear (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (src_pattern != NULL) {
+ src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
+ if (unlikely (src->base.status))
+ return src->base.status;
+
+ mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
+ if (unlikely (mask->base.status)) {
+ cairo_surface_destroy (&src->base);
+ return mask->base.status;
+ }
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ _render_operator (op),
+ src->picture,
+ mask->picture,
+ dst->picture,
+ extents->x + src->x, extents->y + src->y,
+ extents->x + mask->x, extents->y + mask->y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ cairo_surface_destroy (&mask->base);
+ cairo_surface_destroy (&src->base);
+ } else {
+ src = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
+ if (unlikely (src->base.status))
+ return src->base.status;
+
+ _cairo_xcb_connection_render_composite (dst->connection,
+ _render_operator (op),
+ src->picture,
+ XCB_NONE,
+ dst->picture,
+ extents->x + src->x, extents->y + src->y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ cairo_surface_destroy (&src->base);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct composite_box_info {
+ cairo_xcb_surface_t *dst;
+ cairo_xcb_picture_t *src;
+ uint8_t op;
+};
+
+static void composite_box(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct composite_box_info *info = closure;
+
+ if (coverage < 0xff00) {
+ cairo_xcb_picture_t *mask;
+ cairo_color_t color;
+
+ color.red_short = color.green_short = color.blue_short = 0;
+ color.alpha_short = coverage;
+
+ mask = _solid_picture (info->dst, &color);
+ if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_xcb_connection_render_composite (info->dst->connection,
+ info->op,
+ info->src->picture,
+ mask->picture,
+ info->dst->picture,
+ x + info->src->x, y + info->src->y,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+ cairo_surface_destroy (&mask->base);
+ } else {
+ _cairo_xcb_connection_render_composite (info->dst->connection,
+ info->op,
+ info->src->picture,
+ XCB_NONE,
+ info->dst->picture,
+ x + info->src->x, y + info->src->y,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+}
+
+static cairo_int_status_t
+_composite_mask_clip_boxes (void *closure,
+ cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ struct composite_box_info info;
+ cairo_status_t status;
+ int i;
+
+ assert (src_pattern == NULL);
+ assert (op == CAIRO_OPERATOR_ADD);
+ assert (dst->base.is_clear);
+
+ if (clip->num_boxes > 1) {
+ status = _cairo_xcb_surface_clear (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ info.op = XCB_RENDER_PICT_OP_SRC;
+ info.dst = dst;
+ info.src = _cairo_xcb_picture_for_pattern (dst, closure, extents);
+ if (unlikely (info.src->base.status))
+ return info.src->base.status;
+
+ info.src->x += dst_x;
+ info.src->y += dst_y;
+
+ for (i = 0; i < clip->num_boxes; i++)
+ do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
+ cairo_surface_destroy (&info.src->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_composite_mask_clip (void *closure,
+ cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ const cairo_pattern_t *mask_pattern = closure;
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ composite_traps_info_t info;
+ cairo_status_t status;
+
+ assert (src_pattern == NULL);
+ assert (op == CAIRO_OPERATOR_ADD);
+ assert (dst->base.is_clear);
+
+ status = _cairo_clip_get_polygon (clip, &polygon,
+ &fill_rule, &info.antialias);
+ if (unlikely (status))
+ return status;
+
+ _cairo_traps_init (&info.traps);
+ status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps,
+ &polygon,
+ fill_rule);
+ _cairo_polygon_fini (&polygon);
+ if (unlikely (status))
+ return status;
+
+ if (info.traps.has_intersections) {
+ if (info.traps.is_rectangular)
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
+ else if (info.traps.is_rectilinear)
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
+ else
+ status = _cairo_bentley_ottmann_tessellate_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status)) {
+ _cairo_traps_fini (&info.traps);
+ return status;
+ }
+ }
+
+ dst->deferred_clear = FALSE; /* assert(trap extents == extents); */
+
+ status = _composite_traps (&info,
+ dst, CAIRO_OPERATOR_SOURCE, mask_pattern,
+ dst_x, dst_y,
+ extents, NULL);
+ _cairo_traps_fini (&info.traps);
+
+ return status;
+}
+
+struct composite_opacity_info {
+ uint8_t op;
+ cairo_xcb_surface_t *dst;
+ cairo_xcb_picture_t *src;
+ double opacity;
+};
+
+static void composite_opacity(void *closure,
+ int16_t x, int16_t y,
+ int16_t w, int16_t h,
+ uint16_t coverage)
+{
+ struct composite_opacity_info *info = closure;
+ cairo_xcb_picture_t *mask;
+ cairo_color_t color;
+
+ color.red_short = color.green_short = color.blue_short = 0;
+ color.alpha_short = info->opacity * coverage;
+
+ mask = _solid_picture (info->dst, &color);
+ if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
+ if (info->src) {
+ _cairo_xcb_connection_render_composite (info->dst->connection,
+ info->op,
+ info->src->picture,
+ mask->picture,
+ info->dst->picture,
+ x + info->src->x, y + info->src->y,
+ 0, 0,
+ x, y,
+ w, h);
+ } else {
+ _cairo_xcb_connection_render_composite (info->dst->connection,
+ info->op,
+ mask->picture,
+ XCB_NONE,
+ info->dst->picture,
+ 0, 0,
+ 0, 0,
+ x, y,
+ w, h);
+ }
+ }
+
+ cairo_surface_destroy (&mask->base);
+}
+
+static cairo_int_status_t
+_composite_opacity_boxes (void *closure,
+ cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ const cairo_solid_pattern_t *mask_pattern = closure;
+ struct composite_opacity_info info;
+ cairo_status_t status;
+ int i;
+
+ if (dst->base.is_clear) {
+ if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ (clip == NULL ||
+ (clip->extents.width >= extents->width &&
+ clip->extents.height >= extents->height)))
+ dst->deferred_clear = FALSE;
+
+ if (dst->deferred_clear) {
+ status = _cairo_xcb_surface_clear (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ info.op = _render_operator (op);
+ info.dst = dst;
+
+ if (src_pattern != NULL) {
+ info.src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
+ if (unlikely (info.src->base.status))
+ return info.src->base.status;
+ } else
+ info.src = NULL;
+
+ info.opacity = mask_pattern->color.alpha;
+
+ /* XXX for lots of boxes create a clip region for the fully opaque areas */
+ if (clip) {
+ for (i = 0; i < clip->num_boxes; i++)
+ do_unaligned_box(composite_opacity, &info,
+ &clip->boxes[i], dst_x, dst_y);
+ } else {
+ composite_opacity(&info,
+ extents->x - dst_x,
+ extents->y - dst_y,
+ extents->width,
+ extents->height,
+ 0xffff);
+ }
+ cairo_surface_destroy (&info.src->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* high level rasteriser -> compositor */
+
+cairo_int_status_t
+_cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
+ cairo_operator_t op = composite->op;
+ cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_boxes_t boxes;
+ cairo_status_t status;
+
+ if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (composite->clip == NULL &&
+ source->type == CAIRO_PATTERN_TYPE_SOLID &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ op == CAIRO_OPERATOR_CLEAR ||
+ (surface->base.is_clear &&
+ (op == CAIRO_OPERATOR_ADD || op == CAIRO_OPERATOR_OVER))))
+ {
+ surface->deferred_clear = TRUE;
+ surface->deferred_clear_color = composite->source_pattern.solid.color;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_clip_steal_boxes(composite->clip, &boxes);
+ status = _clip_and_composite_boxes (surface, op, source, &boxes, composite);
+ _cairo_clip_unsteal_boxes (composite->clip, &boxes);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
+ cairo_operator_t op = composite->op;
+ cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_pattern_t *mask = &composite->mask_pattern.base;
+ cairo_status_t status;
+
+ if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if ((surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
+ composite->clip->path == NULL &&
+ ! _cairo_clip_is_region (composite->clip)) {
+ status = _clip_and_composite (surface, op, source,
+ _composite_opacity_boxes,
+ _composite_opacity_boxes,
+ (void *) mask,
+ composite, need_unbounded_clip (composite));
+ } else {
+ xcb_draw_func_t mask_func = NULL;
+ if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS)
+ mask_func = composite->clip->path ? _composite_mask_clip : _composite_mask_clip_boxes;
+ status = _clip_and_composite (surface, op, source,
+ _composite_mask, mask_func,
+ (void *) mask,
+ composite, need_bounded_clip (composite));
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ _cairo_polygon_init_with_clip (&polygon, extents->clip);
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _composite_polygon (dst, op, source,
+ &polygon, antialias,
+ CAIRO_FILL_RULE_WINDING,
+ extents);
+ }
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_stroke_via_mask (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *image;
+ cairo_status_t status;
+ cairo_clip_t *clip;
+ int x, y;
+
+ x = extents->bounded.x;
+ y = extents->bounded.y;
+ image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (image->status)) {
+ status = image->status;
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ clip = _cairo_clip_copy_region (extents->clip);
+ status = _cairo_surface_offset_stroke (image, x, y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ path, stroke_style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ _cairo_clip_destroy (clip);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ cairo_surface_pattern_t mask;
+
+ _cairo_pattern_init_for_surface (&mask, image);
+ mask.base.filter = CAIRO_FILTER_NEAREST;
+
+ cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
+ status = _clip_and_composite (dst, op, source,
+ _composite_mask, NULL, &mask.base,
+ extents, need_bounded_clip (extents));
+ _cairo_pattern_fini (&mask.base);
+ }
+
+ cairo_surface_finish (image);
+ cairo_surface_destroy (image);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_render_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
+ cairo_operator_t op = composite->op;
+ cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_int_status_t status;
+
+ if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, composite->clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, composite);
+ }
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
+ status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ composite);
+ } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
+ status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ composite);
+ } else {
+ ASSERT_NOT_REACHED;
+ }
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_fill_as_polygon (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ _cairo_polygon_init_with_clip (&polygon, extents->clip);
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _composite_polygon (dst, op, source,
+ &polygon,
+ antialias,
+ fill_rule,
+ extents);
+ }
+ _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_fill_via_mask (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *image;
+ cairo_status_t status;
+ cairo_clip_t *clip;
+ int x, y;
+
+ x = extents->bounded.x;
+ y = extents->bounded.y;
+ image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (image->status)) {
+ status = image->status;
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ clip = _cairo_clip_copy_region (extents->clip);
+ status = _cairo_surface_offset_fill (image, x, y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ path, fill_rule, tolerance, antialias,
+ clip);
+ _cairo_clip_destroy (clip);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ cairo_surface_pattern_t mask;
+
+ _cairo_pattern_init_for_surface (&mask, image);
+ mask.base.filter = CAIRO_FILTER_NEAREST;
+
+ cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
+ status = _clip_and_composite (dst, op, source,
+ _composite_mask, NULL, &mask.base,
+ extents, need_bounded_clip (extents));
+
+ _cairo_pattern_fini (&mask.base);
+ }
+
+ cairo_surface_finish (image);
+ cairo_surface_destroy (image);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_render_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
+ cairo_operator_t op = composite->op;
+ cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_int_status_t status;
+
+ if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+ CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, composite->clip);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, composite);
+ }
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
+ status = _cairo_xcb_surface_render_fill_as_polygon (surface, op, source, path,
+ fill_rule, tolerance, antialias,
+ composite);
+ } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
+ status = _cairo_xcb_surface_render_fill_via_mask (surface, op, source, path,
+ fill_rule, tolerance, antialias,
+ composite);
+ } else {
+ ASSERT_NOT_REACHED;
+ }
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_glyphs_via_mask (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_surface_t *image;
+ cairo_content_t content;
+ cairo_status_t status;
+ cairo_clip_t *clip;
+ int x, y;
+
+ content = CAIRO_CONTENT_ALPHA;
+ if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ x = extents->bounded.x;
+ y = extents->bounded.y;
+ image = _cairo_xcb_surface_create_similar_image (dst,
+ _cairo_format_from_content (content),
+ extents->bounded.width,
+ extents->bounded.height);
+
+ if (image == NULL)
+ return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+
+ if (unlikely (image->status)) {
+ status = image->status;
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ clip = _cairo_clip_copy_region (extents->clip);
+ status = _cairo_surface_offset_glyphs (image, x, y,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ scaled_font, glyphs, num_glyphs,
+ clip);
+ _cairo_clip_destroy (clip);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ cairo_surface_pattern_t mask;
+
+ _cairo_pattern_init_for_surface (&mask, image);
+ mask.base.filter = CAIRO_FILTER_NEAREST;
+ if (content & CAIRO_CONTENT_COLOR)
+ mask.base.has_component_alpha = TRUE;
+
+ cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
+ status = _clip_and_composite (dst, op, source,
+ _composite_mask, NULL, &mask.base,
+ extents, need_bounded_clip (extents));
+
+ _cairo_pattern_fini (&mask.base);
+ }
+
+ cairo_surface_finish (image);
+ cairo_surface_destroy (image);
+
+ return status;
+}
+
+/* Build a struct of the same size of #cairo_glyph_t that can be used both as
+ * an input glyph with double coordinates, and as "working" glyph with
+ * integer from-current-point offsets. */
+typedef union {
+ cairo_glyph_t d;
+ unsigned long index;
+ struct {
+ unsigned long index;
+ int x;
+ int y;
+ } i;
+} cairo_xcb_glyph_t;
+
+/* compile-time assert that #cairo_xcb_glyph_t is the same size as #cairo_glyph_t */
+COMPILE_TIME_ASSERT (sizeof (cairo_xcb_glyph_t) == sizeof (cairo_glyph_t));
+
+typedef struct {
+ cairo_scaled_font_t *font;
+ cairo_xcb_glyph_t *glyphs;
+ int num_glyphs;
+ cairo_bool_t use_mask;
+} composite_glyphs_info_t;
+
+static cairo_status_t
+_can_composite_glyphs (cairo_xcb_surface_t *dst,
+ cairo_rectangle_int_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs)
+{
+#define GLYPH_CACHE_SIZE 64
+ cairo_box_t bbox_cache[GLYPH_CACHE_SIZE];
+ unsigned long glyph_cache[GLYPH_CACHE_SIZE];
+#undef GLYPH_CACHE_SIZE
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_glyph_t *glyphs_end, *valid_glyphs;
+ const int max_glyph_size = dst->connection->maximum_request_length - 64;
+
+ /* We must initialize the cache with values that cannot match the
+ * "hash" to guarantee that when compared for the first time they
+ * will result in a mismatch. The hash function is simply modulus,
+ * so we cannot use 0 in glyph_cache[0], but we can use it in all
+ * other array cells.
+ */
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+ glyph_cache[0] = 1;
+
+ /* Scan for oversized glyphs or glyphs outside the representable
+ * range and fallback in that case, discard glyphs outside of the
+ * image.
+ */
+ valid_glyphs = glyphs;
+ for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) {
+ double x1, y1, x2, y2;
+ cairo_scaled_glyph_t *glyph;
+ cairo_box_t *bbox;
+ int width, height, len;
+ int g;
+
+ g = glyphs->index % ARRAY_LENGTH (glyph_cache);
+ if (glyph_cache[g] != glyphs->index) {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs->index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &glyph);
+ if (unlikely (status))
+ break;
+
+ glyph_cache[g] = glyphs->index;
+ bbox_cache[g] = glyph->bbox;
+ }
+ bbox = &bbox_cache[g];
+
+ /* Drop glyphs outside the clipping */
+ x1 = _cairo_fixed_to_double (bbox->p1.x);
+ y1 = _cairo_fixed_to_double (bbox->p1.y);
+ y2 = _cairo_fixed_to_double (bbox->p2.y);
+ x2 = _cairo_fixed_to_double (bbox->p2.x);
+ if (unlikely (glyphs->x + x2 <= extents->x ||
+ glyphs->y + y2 <= extents->y ||
+ glyphs->x + x1 >= extents->x + extents->width ||
+ glyphs->y + y1 >= extents->y + extents->height))
+ {
+ (*num_glyphs)--;
+ continue;
+ }
+
+ /* XRenderAddGlyph does not handle a glyph surface larger than
+ * the extended maximum XRequest size.
+ */
+ width = _cairo_fixed_integer_ceil (bbox->p2.x - bbox->p1.x);
+ height = _cairo_fixed_integer_ceil (bbox->p2.y - bbox->p1.y);
+ len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height;
+ if (unlikely (len >= max_glyph_size)) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ break;
+ }
+
+ /* The glyph coordinates must be representable in an int16_t.
+ * When possible, they will be expressed as an offset from the
+ * previous glyph, otherwise they will be an offset from the
+ * operation extents or from the surface origin. If the last
+ * two options are not valid, fallback.
+ */
+ if (unlikely (glyphs->x > INT16_MAX ||
+ glyphs->y > INT16_MAX ||
+ glyphs->x - extents->x < INT16_MIN ||
+ glyphs->y - extents->y < INT16_MIN))
+ {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ break;
+ }
+
+
+ if (unlikely (valid_glyphs != glyphs))
+ *valid_glyphs = *glyphs;
+ valid_glyphs++;
+ }
+
+ if (unlikely (valid_glyphs != glyphs)) {
+ for (; glyphs != glyphs_end; glyphs++) {
+ *valid_glyphs = *glyphs;
+ valid_glyphs++;
+ }
+ }
+
+ return status;
+}
+
+/* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs
+ * (Xrender limits each element to 252 glyphs, we limit them to 128)
+ *
+ * These same conditions need to be mirrored between
+ * _cairo_xcb_surface_emit_glyphs and _emit_glyph_chunks
+ */
+#define _start_new_glyph_elt(count, glyph) \
+ (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
+
+/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
+ * enough room for padding */
+typedef struct {
+ uint8_t len;
+ uint8_t pad1;
+ uint16_t pad2;
+ int16_t deltax;
+ int16_t deltay;
+} x_glyph_elt_t;
+#define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4)
+
+static void
+_cairo_xcb_font_destroy (cairo_xcb_font_t *font)
+{
+ int i;
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xcb_font_glyphset_info_t *info;
+
+ info = &font->glyphset_info[i];
+ free (info->pending_free_glyphs);
+ }
+
+ cairo_list_del (&font->base.link);
+ cairo_list_del (&font->link);
+
+ _cairo_xcb_connection_destroy (font->connection);
+
+ free (font);
+}
+
+static void
+_cairo_xcb_font_fini (cairo_scaled_font_private_t *abstract_private,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_xcb_font_t *font_private = (cairo_xcb_font_t *)abstract_private;
+ cairo_xcb_connection_t *connection;
+ cairo_bool_t have_connection;
+ cairo_status_t status;
+ int i;
+
+ connection = font_private->connection;
+
+ status = _cairo_xcb_connection_acquire (connection);
+ have_connection = status == CAIRO_STATUS_SUCCESS;
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xcb_font_glyphset_info_t *info;
+
+ info = &font_private->glyphset_info[i];
+ if (info->glyphset && status == CAIRO_STATUS_SUCCESS) {
+ _cairo_xcb_connection_render_free_glyph_set (connection,
+ info->glyphset);
+ }
+ }
+
+ if (have_connection)
+ _cairo_xcb_connection_release (connection);
+
+ _cairo_xcb_font_destroy (font_private);
+}
+
+
+static cairo_xcb_font_t *
+_cairo_xcb_font_create (cairo_xcb_connection_t *connection,
+ cairo_scaled_font_t *font)
+{
+ cairo_xcb_font_t *priv;
+ int i;
+
+ priv = malloc (sizeof (cairo_xcb_font_t));
+ if (unlikely (priv == NULL))
+ return NULL;
+
+ _cairo_scaled_font_attach_private (font, &priv->base, connection,
+ _cairo_xcb_font_fini);
+
+ priv->scaled_font = font;
+ priv->connection = _cairo_xcb_connection_reference (connection);
+ cairo_list_add (&priv->link, &connection->fonts);
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xcb_font_glyphset_info_t *info = &priv->glyphset_info[i];
+ switch (i) {
+ case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break;
+ case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break;
+ case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break;
+ default: ASSERT_NOT_REACHED; break;
+ }
+ info->xrender_format = 0;
+ info->glyphset = XCB_NONE;
+ info->pending_free_glyphs = NULL;
+ }
+
+ return priv;
+}
+
+void
+_cairo_xcb_font_close (cairo_xcb_font_t *font)
+{
+ cairo_scaled_font_t *scaled_font;
+
+ scaled_font = font->scaled_font;
+
+ //scaled_font->surface_private = NULL;
+ _cairo_scaled_font_reset_cache (scaled_font);
+
+ _cairo_xcb_font_destroy (font);
+}
+
+static void
+_cairo_xcb_render_free_glyphs (cairo_xcb_connection_t *connection,
+ cairo_xcb_font_glyphset_free_glyphs_t *to_free)
+{
+ _cairo_xcb_connection_render_free_glyphs (connection,
+ to_free->glyphset,
+ to_free->glyph_count,
+ to_free->glyph_indices);
+}
+
+static int
+_cairo_xcb_get_glyphset_index_for_format (cairo_format_t format)
+{
+ if (format == CAIRO_FORMAT_A8)
+ return GLYPHSET_INDEX_A8;
+ if (format == CAIRO_FORMAT_A1)
+ return GLYPHSET_INDEX_A1;
+
+ assert (format == CAIRO_FORMAT_ARGB32);
+ return GLYPHSET_INDEX_ARGB32;
+}
+
+
+
+static inline cairo_xcb_font_t *
+_cairo_xcb_font_get (const cairo_xcb_connection_t *c,
+ cairo_scaled_font_t *font)
+{
+ return (cairo_xcb_font_t *)_cairo_scaled_font_find_private (font, c);
+}
+
+
+static cairo_xcb_font_glyphset_info_t *
+_cairo_xcb_scaled_font_get_glyphset_info_for_format (cairo_xcb_connection_t *c,
+ cairo_scaled_font_t *font,
+ cairo_format_t format)
+{
+ cairo_xcb_font_t *priv;
+ cairo_xcb_font_glyphset_info_t *info;
+ int glyphset_index;
+
+ glyphset_index = _cairo_xcb_get_glyphset_index_for_format (format);
+
+ priv = _cairo_xcb_font_get (c, font);
+ if (priv == NULL) {
+ priv = _cairo_xcb_font_create (c, font);
+ if (priv == NULL)
+ return NULL;
+ }
+
+ info = &priv->glyphset_info[glyphset_index];
+ if (info->glyphset == XCB_NONE) {
+ info->glyphset = _cairo_xcb_connection_get_xid (c);
+ info->xrender_format = c->standard_formats[info->format];
+
+ _cairo_xcb_connection_render_create_glyph_set (c,
+ info->glyphset,
+ info->xrender_format);
+ }
+
+ return info;
+}
+
+static cairo_bool_t
+_cairo_xcb_glyphset_info_has_pending_free_glyph (
+ cairo_xcb_font_glyphset_info_t *info,
+ unsigned long glyph_index)
+{
+ if (info->pending_free_glyphs != NULL) {
+ cairo_xcb_font_glyphset_free_glyphs_t *to_free;
+ int i;
+
+ to_free = info->pending_free_glyphs;
+ for (i = 0; i < to_free->glyph_count; i++) {
+ if (to_free->glyph_indices[i] == glyph_index) {
+ to_free->glyph_count--;
+ memmove (&to_free->glyph_indices[i],
+ &to_free->glyph_indices[i+1],
+ (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0]));
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+typedef struct {
+ cairo_scaled_glyph_private_t base;
+
+ cairo_xcb_font_glyphset_info_t *glyphset;
+} cairo_xcb_glyph_private_t;
+
+static cairo_xcb_font_glyphset_info_t *
+_cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (cairo_xcb_connection_t *c,
+ cairo_scaled_font_t *font,
+ unsigned long glyph_index,
+ cairo_image_surface_t *surface)
+{
+ cairo_xcb_font_t *priv;
+ int i;
+
+ priv = _cairo_xcb_font_get (c, font);
+ if (priv == NULL)
+ return NULL;
+
+ if (surface != NULL) {
+ i = _cairo_xcb_get_glyphset_index_for_format (surface->format);
+
+ if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
+ &priv->glyphset_info[i],
+ glyph_index))
+ {
+ return &priv->glyphset_info[i];
+ }
+ } else {
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
+ &priv->glyphset_info[i],
+ glyph_index))
+ {
+ return &priv->glyphset_info[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_cairo_xcb_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
+ cairo_scaled_glyph_t *glyph,
+ cairo_scaled_font_t *font)
+{
+ cairo_xcb_glyph_private_t *priv = (cairo_xcb_glyph_private_t *)glyph_private;
+
+ if (! font->finished) {
+ cairo_xcb_font_glyphset_info_t *info = priv->glyphset;
+ cairo_xcb_font_glyphset_free_glyphs_t *to_free;
+ cairo_xcb_font_t *font_private;
+
+ font_private = _cairo_xcb_font_get (glyph_private->key, font);
+ assert (font_private);
+
+ to_free = info->pending_free_glyphs;
+ if (to_free != NULL &&
+ to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices))
+ {
+ _cairo_xcb_render_free_glyphs (font_private->connection, to_free);
+ to_free = info->pending_free_glyphs = NULL;
+ }
+
+ if (to_free == NULL) {
+ to_free = malloc (sizeof (cairo_xcb_font_glyphset_free_glyphs_t));
+ if (unlikely (to_free == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return; /* XXX cannot propagate failure */
+ }
+
+ to_free->glyphset = info->glyphset;
+ to_free->glyph_count = 0;
+ info->pending_free_glyphs = to_free;
+ }
+
+ to_free->glyph_indices[to_free->glyph_count++] =
+ _cairo_scaled_glyph_index (glyph);
+ }
+
+ cairo_list_del (&glyph_private->link);
+ free (glyph_private);
+}
+
+
+static cairo_status_t
+_cairo_xcb_glyph_attach (cairo_xcb_connection_t *c,
+ cairo_scaled_glyph_t *glyph,
+ cairo_xcb_font_glyphset_info_t *info)
+{
+ cairo_xcb_glyph_private_t *priv;
+
+ priv = malloc (sizeof (*priv));
+ if (unlikely (priv == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_scaled_glyph_attach_private (glyph, &priv->base, c,
+ _cairo_xcb_glyph_fini);
+ priv->glyphset = info;
+
+ glyph->dev_private = info;
+ glyph->dev_private_key = c;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection,
+ cairo_scaled_font_t *font,
+ cairo_scaled_glyph_t **scaled_glyph_out)
+{
+ xcb_render_glyphinfo_t glyph_info;
+ uint32_t glyph_index;
+ uint8_t *data;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_scaled_glyph_t *scaled_glyph = *scaled_glyph_out;
+ cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+ cairo_bool_t already_had_glyph_surface;
+ cairo_xcb_font_glyphset_info_t *info;
+
+ glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
+
+ /* check to see if we have a pending XRenderFreeGlyph for this glyph */
+ info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (connection, font, glyph_index, glyph_surface);
+ if (info != NULL)
+ return _cairo_xcb_glyph_attach (connection, scaled_glyph, info);
+
+ if (glyph_surface == NULL) {
+ status = _cairo_scaled_glyph_lookup (font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS |
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ scaled_glyph_out);
+ if (unlikely (status))
+ return status;
+
+ scaled_glyph = *scaled_glyph_out;
+ glyph_surface = scaled_glyph->surface;
+ already_had_glyph_surface = FALSE;
+ } else {
+ already_had_glyph_surface = TRUE;
+ }
+
+ info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (connection,
+ font,
+ glyph_surface->format);
+ if (unlikely (info == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+#if 0
+ /* If the glyph surface has zero height or width, we create
+ * a clear 1x1 surface, to avoid various X server bugs.
+ */
+ if (glyph_surface->width == 0 || glyph_surface->height == 0) {
+ cairo_surface_t *tmp_surface;
+
+ tmp_surface = cairo_image_surface_create (info->format, 1, 1);
+ status = tmp_surface->status;
+ if (unlikely (status))
+ goto BAIL;
+
+ tmp_surface->device_transform = glyph_surface->base.device_transform;
+ tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+ glyph_surface = (cairo_image_surface_t *) tmp_surface;
+ }
+#endif
+
+ /* If the glyph format does not match the font format, then we
+ * create a temporary surface for the glyph image with the font's
+ * format.
+ */
+ if (glyph_surface->format != info->format) {
+ glyph_surface = _cairo_image_surface_coerce_to_format (glyph_surface,
+ info->format);
+ status = glyph_surface->base.status;
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */
+ glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0);
+ glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0);
+ glyph_info.width = glyph_surface->width;
+ glyph_info.height = glyph_surface->height;
+ glyph_info.x_off = scaled_glyph->x_advance;
+ glyph_info.y_off = scaled_glyph->y_advance;
+
+ data = glyph_surface->data;
+
+ /* flip formats around */
+ switch (_cairo_xcb_get_glyphset_index_for_format (scaled_glyph->surface->format)) {
+ case GLYPHSET_INDEX_A1:
+ /* local bitmaps are always stored with bit == byte */
+ if (_cairo_is_little_endian() != (connection->root->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
+ int c = glyph_surface->stride * glyph_surface->height;
+ const uint8_t *d;
+ uint8_t *new, *n;
+
+ new = malloc (c);
+ if (unlikely (new == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ n = new;
+ d = data;
+ do {
+ uint8_t b = *d++;
+ b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
+ b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
+ b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
+ *n++ = b;
+ } while (--c);
+ data = new;
+ }
+ break;
+
+ case GLYPHSET_INDEX_A8:
+ break;
+
+ case GLYPHSET_INDEX_ARGB32:
+ if (_cairo_is_little_endian() != (connection->root->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
+ unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
+ const uint32_t *d;
+ uint32_t *new, *n;
+
+ 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 */
+
+ _cairo_xcb_connection_render_add_glyphs (connection,
+ info->glyphset,
+ 1, &glyph_index, &glyph_info,
+ glyph_surface->stride * glyph_surface->height,
+ data);
+
+ if (data != glyph_surface->data)
+ free (data);
+
+ status = _cairo_xcb_glyph_attach (connection, scaled_glyph, info);
+
+ BAIL:
+ if (glyph_surface != scaled_glyph->surface)
+ cairo_surface_destroy (&glyph_surface->base);
+
+ /* If the scaled glyph didn't already have a surface attached
+ * to it, release the created surface now that we have it
+ * uploaded to the X server. If the surface has already been
+ * there (e.g. because image backend requested it), leave it in
+ * the cache
+ */
+ if (! already_had_glyph_surface)
+ _cairo_scaled_glyph_set_surface (scaled_glyph, font, NULL);
+
+ return status;
+}
+
+typedef void (*cairo_xcb_render_composite_text_func_t)
+ (cairo_xcb_connection_t *connection,
+ uint8_t op,
+ xcb_render_picture_t src,
+ xcb_render_picture_t dst,
+ xcb_render_pictformat_t mask_format,
+ xcb_render_glyphset_t glyphset,
+ int16_t src_x,
+ int16_t src_y,
+ uint32_t len,
+ uint8_t *cmd);
+
+
+static cairo_status_t
+_emit_glyphs_chunk (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ cairo_xcb_picture_t *src,
+ /* info for this chunk */
+ cairo_xcb_glyph_t *glyphs,
+ int num_glyphs,
+ int width,
+ int estimated_req_size,
+ cairo_xcb_font_glyphset_info_t *info,
+ xcb_render_pictformat_t mask_format)
+{
+ cairo_xcb_render_composite_text_func_t composite_text_func;
+ uint8_t stack_buf[CAIRO_STACK_BUFFER_SIZE];
+ uint8_t *buf = stack_buf;
+ x_glyph_elt_t *elt = NULL; /* silence compiler */
+ uint32_t len;
+ int i;
+
+ if (estimated_req_size > ARRAY_LENGTH (stack_buf)) {
+ buf = malloc (estimated_req_size);
+ if (unlikely (buf == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ len = 0;
+ for (i = 0; i < num_glyphs; i++) {
+ if (_start_new_glyph_elt (i, &glyphs[i])) {
+ if (len & 3)
+ len += 4 - (len & 3);
+
+ elt = (x_glyph_elt_t *) (buf + len);
+ elt->len = 0;
+ elt->deltax = glyphs[i].i.x;
+ elt->deltay = glyphs[i].i.y;
+ len += sizeof (x_glyph_elt_t);
+ }
+
+ switch (width) {
+ case 1: *(uint8_t *) (buf + len) = glyphs[i].index; break;
+ case 2: *(uint16_t *) (buf + len) = glyphs[i].index; break;
+ default:
+ case 4: *(uint32_t *) (buf + len) = glyphs[i].index; break;
+ }
+ len += width;
+ elt->len++;
+ }
+ if (len & 3)
+ len += 4 - (len & 3);
+
+ switch (width) {
+ case 1:
+ composite_text_func = _cairo_xcb_connection_render_composite_glyphs_8;
+ break;
+ case 2:
+ composite_text_func = _cairo_xcb_connection_render_composite_glyphs_16;
+ break;
+ default:
+ case 4:
+ composite_text_func = _cairo_xcb_connection_render_composite_glyphs_32;
+ break;
+ }
+ composite_text_func (dst->connection,
+ _render_operator (op),
+ src->picture,
+ dst->picture,
+ mask_format,
+ info->glyphset,
+ src->x + glyphs[0].i.x,
+ src->y + glyphs[0].i.y,
+ len, buf);
+
+ if (buf != stack_buf)
+ free (buf);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_composite_glyphs (void *closure,
+ cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_clip_t *clip)
+{
+ composite_glyphs_info_t *info = closure;
+ cairo_scaled_glyph_t *glyph_cache[64];
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_fixed_t x = 0, y = 0;
+ cairo_xcb_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info;
+ const unsigned int max_request_size = dst->connection->maximum_request_length - 64;
+ cairo_xcb_picture_t *src;
+
+ unsigned long max_index = 0;
+ int width = 1;
+
+ unsigned int request_size = 0;
+ int i;
+
+ if (dst->deferred_clear) {
+ status = _cairo_xcb_surface_clear (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+ if (unlikely (src->base.status))
+ return src->base.status;
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+
+ for (i = 0; i < info->num_glyphs; i++) {
+ cairo_scaled_glyph_t *glyph;
+ unsigned long glyph_index = info->glyphs[i].index;
+ int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+ int old_width = width;
+ int this_x, this_y;
+
+ glyph = glyph_cache[cache_index];
+ if (glyph == NULL ||
+ _cairo_scaled_glyph_index (glyph) != glyph_index)
+ {
+ status = _cairo_scaled_glyph_lookup (info->font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &glyph);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&src->base);
+ return status;
+ }
+
+ /* Send unseen glyphs to the server */
+ if (glyph->dev_private_key != dst->connection) {
+ status = _cairo_xcb_surface_add_glyph (dst->connection,
+ info->font,
+ &glyph);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&src->base);
+ return status;
+ }
+ }
+
+ glyph_cache[cache_index] = glyph;
+ }
+
+ this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x;
+ this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y;
+
+ this_glyphset_info = glyph->dev_private;
+ if (glyphset_info == NULL)
+ glyphset_info = this_glyphset_info;
+
+ /* Update max glyph index */
+ if (glyph_index > max_index) {
+ max_index = glyph_index;
+ if (max_index >= 65536)
+ width = 4;
+ else if (max_index >= 256)
+ width = 2;
+ if (width != old_width)
+ request_size += (width - old_width) * i;
+ }
+
+ /* If we will pass the max request size by adding this glyph,
+ * flush current glyphs. Note that we account for a
+ * possible element being added below.
+ *
+ * Also flush if changing glyphsets, as Xrender limits one mask
+ * format per request, so we can either break up, or use a
+ * wide-enough mask format. We do the former. One reason to
+ * prefer the latter is the fact that Xserver ADDs all glyphs
+ * to the mask first, and then composes that to final surface,
+ * though it's not a big deal.
+ *
+ * If the glyph has a coordinate which cannot be represented
+ * as a 16-bit offset from the previous glyph, flush the
+ * current chunk. The current glyph will be the first one in
+ * the next chunk, thus its coordinates will be an offset from
+ * the destination origin. This offset is guaranteed to be
+ * representable as 16-bit offset in _can_composite_glyphs().
+ */
+ if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t ||
+ this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
+ this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
+ this_glyphset_info != glyphset_info)
+ {
+ status = _emit_glyphs_chunk (dst, op, src,
+ info->glyphs, i,
+ old_width, request_size,
+ glyphset_info,
+ info->use_mask ? glyphset_info->xrender_format : 0);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&src->base);
+ return status;
+ }
+
+ info->glyphs += i;
+ info->num_glyphs -= i;
+ i = 0;
+
+ max_index = info->glyphs[0].index;
+ width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
+
+ request_size = 0;
+
+ x = y = 0;
+ glyphset_info = this_glyphset_info;
+ }
+
+ /* Convert absolute glyph position to relative-to-current-point
+ * position */
+ info->glyphs[i].i.x = this_x - x;
+ info->glyphs[i].i.y = this_y - y;
+
+ /* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs.
+ *
+ * These same conditions are mirrored in _emit_glyphs_chunk().
+ */
+ if (_start_new_glyph_elt (i, &info->glyphs[i]))
+ request_size += _cairo_sz_x_glyph_elt_t;
+
+ /* adjust current-position */
+ x = this_x + glyph->x_advance;
+ y = this_y + glyph->y_advance;
+
+ request_size += width;
+ }
+
+ if (i) {
+ status = _emit_glyphs_chunk (dst, op, src,
+ info->glyphs, i,
+ width, request_size,
+ glyphset_info,
+ info->use_mask ? glyphset_info->xrender_format : 0);
+ }
+
+ cairo_surface_destroy (&src->base);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_render_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
+ cairo_operator_t op = composite->op;
+ cairo_pattern_t *source = &composite->source_pattern.base;
+ cairo_int_status_t status;
+
+ if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) {
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ status = _can_composite_glyphs (surface, &composite->bounded,
+ scaled_font, glyphs, &num_glyphs);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ composite_glyphs_info_t info;
+ unsigned flags = 0;
+
+ info.font = scaled_font;
+ info.glyphs = (cairo_xcb_glyph_t *) glyphs;
+ info.num_glyphs = num_glyphs;
+ info.use_mask =
+ overlap ||
+ ! composite->is_bounded ||
+ ! _cairo_clip_is_region(composite->clip);
+
+ if (composite->mask.width > composite->unbounded.width ||
+ composite->mask.height > composite->unbounded.height)
+ {
+ /* Glyphs are tricky since we do not directly control the
+ * geometry and their inked extents depend on the
+ * individual glyph-surface size. We must set a clip region
+ * so that the X server can trim the glyphs appropriately.
+ */
+ flags |= FORCE_CLIP_REGION;
+ }
+ status = _clip_and_composite (surface, op, source,
+ _composite_glyphs, NULL,
+ &info, composite,
+ need_bounded_clip (composite) |
+ flags);
+ }
+
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ assert (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE);
+ status =
+ _cairo_xcb_surface_render_glyphs_via_mask (surface, op, source,
+ scaled_font, glyphs, num_glyphs,
+ composite);
+ }
+
+ return status;
+}
+
+static xcb_render_fixed_t *
+_create_convolution_coef (double *convolution_matrix,
+ int col, int row, cairo_bool_t is_x_pass)
+{
+ xcb_render_fixed_t *values;
+ double v;
+ int length;
+ int i;
+ double *coef;
+
+ if (! convolution_matrix || col == 0 || row == 0)
+ return NULL;
+
+ if (is_x_pass) {
+ length = col;
+ values = _cairo_malloc_ab (length + 2, sizeof (xcb_render_fixed_t));
+ if (values == NULL)
+ return NULL;
+ v = col;
+ values[0] = _cairo_fixed_16_16_from_double (v);
+ values[1] = _cairo_fixed_16_16_from_double (1.0);
+ coef = _cairo_malloc_ab (length, sizeof (double));
+ if (coef == NULL) {
+ free (values);
+ return NULL;
+ }
+ memset (coef, 0, sizeof (double) * length);
+ compute_x_coef_to_double (convolution_matrix, row, col, coef);
+ }
+ else {
+ length = row;
+ values = _cairo_malloc_ab (length + 2, sizeof (xcb_render_fixed_t));
+ if (values == NULL)
+ return NULL;
+ values[0] = _cairo_fixed_16_16_from_double (1.0);
+ v = row;
+ values[1] = _cairo_fixed_16_16_from_double (v);
+ coef = _cairo_malloc_ab (length, sizeof (double));
+ if (coef == NULL) {
+ free (values);
+ return NULL;
+ }
+ memset (coef, 0, sizeof (double) * length);
+ compute_y_coef_to_double (convolution_matrix, row, col, coef);
+ }
+
+ for (i = 0; i < length; i++)
+ values[i+2] = _cairo_fixed_16_16_from_double (coef[i]);
+
+ free (coef);
+ return values;
+}
+
+cairo_xcb_picture_t *
+_cairo_xcb_gaussian_filter (cairo_xcb_surface_t *source,
+ cairo_xcb_picture_t *orig_pict,
+ const cairo_pattern_t *pattern)
+{
+ int row, col;
+ int width, height;
+ cairo_matrix_t matrix;
+ cairo_format_t format;
+
+ cairo_xcb_picture_t *shrinked_picture = NULL;
+ cairo_xcb_picture_t *scratch_pictures[] = { NULL, NULL };
+ cairo_xcb_picture_t *temp_pict;
+ cairo_xcb_picture_t *picture;
+
+ xcb_pixmap_t pixmap;
+ pixman_format_code_t pixman_format;
+ int depth;
+ xcb_render_pictformat_t xrender_format;
+
+ int src_width= orig_pict->width;
+ int src_height = orig_pict->height;
+ xcb_render_fixed_t *coef;
+ int i;
+
+ if (pattern->filter != CAIRO_FILTER_GAUSSIAN ||
+ pattern->convolution_matrix == NULL) {
+ return (cairo_xcb_picture_t *)cairo_surface_reference (&orig_pict->base);
+ }
+
+ row = pattern->y_radius * 2 + 1;
+ col = pattern->x_radius * 2 + 1;
+
+ width = src_width / pattern->shrink_factor_x;
+ height = src_height / pattern->shrink_factor_y;
+ format = _cairo_format_from_content (cairo_surface_get_content (&source->base));
+ pixman_format = _cairo_format_to_pixman_format_code (format);
+ depth = PIXMAN_FORMAT_DEPTH (pixman_format);
+ xrender_format = source->connection->standard_formats[format];
+
+ picture = _cairo_xcb_picture_create (source->screen,
+ pixman_format,
+ xrender_format,
+ src_width, src_height);
+ if (unlikely (picture->base.status)) {
+ cairo_surface_destroy (&picture->base);
+ picture = (cairo_xcb_picture_t *) cairo_surface_reference (&orig_pict->base);
+ return picture;
+ }
+
+ pixmap = _cairo_xcb_connection_create_pixmap (source->connection,
+ depth,
+ source->drawable,
+ src_width, src_height);
+
+ _cairo_xcb_connection_render_create_picture (source->connection,
+ picture->picture,
+ pixmap,
+ xrender_format,
+ 0, 0);
+ _cairo_xcb_connection_free_pixmap (source->connection, pixmap);
+
+ for (i = 0; i < 2; i++) {
+ scratch_pictures[i] = _cairo_xcb_picture_create (source->screen,
+ pixman_format,
+ xrender_format,
+ width, height);
+
+ if (unlikely (scratch_pictures[i]->base.status)) {
+ cairo_surface_destroy (&picture->base);
+ picture = (cairo_xcb_picture_t *) cairo_surface_reference (&orig_pict->base);
+ goto DONE;
+ }
+
+ pixmap = _cairo_xcb_connection_create_pixmap (source->connection,
+ depth,
+ source->drawable,
+ width, height);
+
+ _cairo_xcb_connection_render_create_picture (source->connection,
+ scratch_pictures[i]->picture,
+ pixmap,
+ xrender_format,
+ 0, 0);
+ _cairo_xcb_connection_free_pixmap (source->connection, pixmap);
+ }
+
+ if (width != src_width || height != src_height) {
+ shrinked_picture = _cairo_xcb_picture_create (source->screen,
+ pixman_format,
+ xrender_format,
+ width, height);
+
+ if (unlikely (shrinked_picture->base.status)) {
+ cairo_surface_destroy (&picture->base);
+ picture = (cairo_xcb_picture_t *) cairo_surface_reference (&orig_pict->base);
+ goto DONE;
+ }
+
+ pixmap = _cairo_xcb_connection_create_pixmap (source->connection,
+ depth,
+ source->drawable,
+ width, height);
+
+ _cairo_xcb_connection_render_create_picture (source->connection,
+ shrinked_picture->picture,
+ pixmap,
+ xrender_format,
+ 0, 0);
+
+ _cairo_xcb_connection_free_pixmap (source->connection, pixmap);
+
+ cairo_matrix_init_scale (&matrix,
+ (double) src_width / (double) width,
+ (double) src_height / (double) height);
+
+ _cairo_xcb_picture_set_matrix (orig_pict,
+ &matrix, CAIRO_FILTER_BILINEAR,
+ width/2, height/2);
+ _cairo_xcb_picture_set_extend (orig_pict, CAIRO_EXTEND_NONE);
+ _cairo_xcb_picture_set_component_alpha (orig_pict,
+ pattern->has_component_alpha);
+
+ _cairo_xcb_picture_set_filter (orig_pict, CAIRO_FILTER_NEAREST, 0, NULL);
+
+ _cairo_xcb_connection_render_composite (source->connection,
+ _render_operator (CAIRO_OPERATOR_SOURCE),
+ orig_pict->picture,
+ XCB_NONE,
+ shrinked_picture->picture,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ temp_pict = shrinked_picture;
+ } else
+ temp_pict = orig_pict;
+
+ /* filter with gaussian, first x pass */
+ coef = _create_convolution_coef (pattern->convolution_matrix,
+ col, row, TRUE);
+ if (coef == NULL) {
+ cairo_surface_destroy (&picture->base);
+ picture = (cairo_xcb_picture_t *) cairo_surface_reference (&orig_pict->base);
+ goto DONE;
+ }
+
+ cairo_matrix_init_identity (&matrix);
+
+ _cairo_xcb_picture_set_matrix (temp_pict,
+ &matrix, CAIRO_FILTER_NEAREST,
+ width/2, height/2);
+ _cairo_xcb_picture_set_extend (temp_pict, CAIRO_EXTEND_NONE);
+ _cairo_xcb_picture_set_component_alpha (temp_pict,
+ pattern->has_component_alpha);
+
+ _cairo_xcb_picture_set_filter (temp_pict, CAIRO_FILTER_GAUSSIAN, col+2, coef);
+ _cairo_xcb_connection_render_composite (source->connection,
+ _render_operator (CAIRO_OPERATOR_SOURCE),
+ temp_pict->picture,
+ XCB_NONE,
+ scratch_pictures[0]->picture,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ free (coef);
+
+ /* y pass */
+ coef = _create_convolution_coef (pattern->convolution_matrix,
+ col, row, FALSE);
+ if (coef == NULL) {
+ cairo_surface_destroy (&picture->base);
+ picture = (cairo_xcb_picture_t *) cairo_surface_reference (&orig_pict->base);
+ goto DONE;
+ }
+
+ cairo_matrix_init_identity (&matrix);
+
+ _cairo_xcb_picture_set_matrix (scratch_pictures[0],
+ &matrix, CAIRO_FILTER_NEAREST,
+ width/2, height/2);
+ _cairo_xcb_picture_set_extend (scratch_pictures[0], CAIRO_EXTEND_NONE);
+ _cairo_xcb_picture_set_component_alpha (scratch_pictures[0],
+ pattern->has_component_alpha);
+
+ _cairo_xcb_picture_set_filter (scratch_pictures[0], CAIRO_FILTER_GAUSSIAN, row+2, coef);
+ _cairo_xcb_connection_render_composite (source->connection,
+ _render_operator (CAIRO_OPERATOR_SOURCE),
+ scratch_pictures[0]->picture,
+ XCB_NONE,
+ scratch_pictures[1]->picture,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ free (coef);
+
+ /* enlarge back to picture */
+ cairo_matrix_init_scale (&matrix,
+ (double) width / (double) src_width,
+ (double) height / (double) src_height);
+
+ _cairo_xcb_picture_set_matrix (scratch_pictures[1],
+ &matrix, CAIRO_FILTER_BILINEAR,
+ src_width/2, src_height/2);
+ _cairo_xcb_picture_set_extend (scratch_pictures[1], CAIRO_EXTEND_NONE);
+ _cairo_xcb_picture_set_component_alpha (scratch_pictures[1],
+ pattern->has_component_alpha);
+
+ _cairo_xcb_picture_set_filter (scratch_pictures[1], CAIRO_FILTER_BILINEAR, 0, NULL);
+
+ _cairo_xcb_connection_render_composite (source->connection,
+ _render_operator (CAIRO_OPERATOR_SOURCE),
+ scratch_pictures[1]->picture,
+ XCB_NONE,
+ picture->picture,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ src_width, src_height);
+DONE:
+ if (shrinked_picture)
+ cairo_surface_destroy (&shrinked_picture->base);
+ for (i = 0; i < 2; i++) {
+ if (scratch_pictures[i])
+ cairo_surface_destroy (&scratch_pictures[i]->base);
+ }
+ return picture;
+}
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
new file mode 100755
index 000000000..7b74027d1
--- /dev/null
+++ b/src/cairo-xcb-surface.c
@@ -0,0 +1,1702 @@
+/* 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"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-list-inline.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-surface-shadow-private.h"
+
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_proto (cairo_xcb_surface_create);
+slim_hidden_proto (cairo_xcb_surface_create_for_bitmap);
+slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format);
+#endif
+
+/**
+ * SECTION:cairo-xcb
+ * @Title: XCB Surfaces
+ * @Short_Description: X Window System rendering using the XCB library
+ * @See_Also: #cairo_surface_t
+ *
+ * The XCB surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XCB library.
+ *
+ * Note that the XCB surface automatically takes advantage of the X render
+ * extension if it is available.
+ **/
+
+/**
+ * CAIRO_HAS_XCB_SURFACE:
+ *
+ * Defined if the xcb surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.12
+ **/
+
+cairo_surface_t *
+_cairo_xcb_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_xcb_surface_t *other = abstract_other;
+ cairo_xcb_surface_t *surface;
+ cairo_xcb_connection_t *connection;
+ xcb_pixmap_t pixmap;
+ cairo_status_t status;
+
+ if (unlikely(width > XLIB_COORD_MAX ||
+ height > XLIB_COORD_MAX ||
+ width <= 0 ||
+ height <= 0))
+ return cairo_image_surface_create (_cairo_format_from_content (content),
+ width, height);
+
+ if ((other->connection->flags & CAIRO_XCB_HAS_RENDER) == 0)
+ return _cairo_xcb_surface_create_similar_image (other,
+ _cairo_format_from_content (content),
+ width, height);
+
+ connection = other->connection;
+ status = _cairo_xcb_connection_acquire (connection);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (content == other->base.content) {
+ pixmap = _cairo_xcb_connection_create_pixmap (connection,
+ other->depth,
+ other->drawable,
+ width, height);
+
+ surface = (cairo_xcb_surface_t *)
+ _cairo_xcb_surface_create_internal (other->screen,
+ pixmap, TRUE,
+ other->pixman_format,
+ other->xrender_format,
+ width, height);
+ } else {
+ cairo_format_t format;
+ pixman_format_code_t pixman_format;
+
+ /* XXX find a compatible xrender format */
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ pixman_format = PIXMAN_a8;
+ format = CAIRO_FORMAT_A8;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ pixman_format = PIXMAN_x8r8g8b8;
+ format = CAIRO_FORMAT_RGB24;
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ pixman_format = PIXMAN_a8r8g8b8;
+ format = CAIRO_FORMAT_ARGB32;
+ break;
+ }
+
+ pixmap = _cairo_xcb_connection_create_pixmap (connection,
+ PIXMAN_FORMAT_DEPTH (pixman_format),
+ other->drawable,
+ width, height);
+
+ surface = (cairo_xcb_surface_t *)
+ _cairo_xcb_surface_create_internal (other->screen,
+ pixmap, TRUE,
+ pixman_format,
+ connection->standard_formats[format],
+ width, height);
+ }
+
+ if (unlikely (surface->base.status))
+ _cairo_xcb_connection_free_pixmap (connection, pixmap);
+
+ _cairo_xcb_connection_release (connection);
+
+ return &surface->base;
+}
+
+cairo_surface_t *
+_cairo_xcb_surface_create_similar_image (void *abstract_other,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_xcb_surface_t *other = abstract_other;
+ cairo_xcb_connection_t *connection = other->connection;
+
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ pixman_format_code_t pixman_format;
+
+ if (unlikely(width > XLIB_COORD_MAX ||
+ height > XLIB_COORD_MAX ||
+ width <= 0 ||
+ height <= 0))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ pixman_format = _cairo_format_to_pixman_format_code (format);
+
+ status = _cairo_xcb_shm_image_create (connection, pixman_format,
+ width, height, &image,
+ &shm_info);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (! image->base.is_clear) {
+ memset (image->data, 0, image->stride * image->height);
+ image->base.is_clear = TRUE;
+ }
+
+ return &image->base;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_finish (void *abstract_surface)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->fallback != NULL) {
+ cairo_surface_finish (&surface->fallback->base);
+ cairo_surface_destroy (&surface->fallback->base);
+ }
+ _cairo_boxes_fini (&surface->fallback_damage);
+
+ cairo_list_del (&surface->link);
+
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ if (surface->picture != XCB_NONE) {
+ _cairo_xcb_connection_render_free_picture (surface->connection,
+ surface->picture);
+ }
+
+ if (surface->owns_pixmap)
+ _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable);
+ _cairo_xcb_connection_release (surface->connection);
+ }
+
+ _cairo_xcb_connection_destroy (surface->connection);
+
+ return status;
+}
+
+static void
+_destroy_image (pixman_image_t *image, void *data)
+{
+ free (data);
+}
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+static cairo_surface_t *
+_cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection,
+ pixman_format_code_t pixman_format,
+ int width, int height,
+ cairo_bool_t might_reuse,
+ cairo_xcb_shm_info_t **shm_info_out)
+{
+ cairo_surface_t *image;
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_int_status_t status;
+ size_t stride;
+
+ *shm_info_out = NULL;
+
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width,
+ PIXMAN_FORMAT_BPP (pixman_format));
+ status = _cairo_xcb_connection_allocate_shm_info (connection,
+ stride * height,
+ might_reuse,
+ &shm_info);
+ if (unlikely (status)) {
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ return NULL;
+
+ return _cairo_surface_create_in_error (status);
+ }
+
+ image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
+ pixman_format,
+ width, height,
+ stride);
+ if (unlikely (image->status)) {
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return image;
+ }
+
+ status = _cairo_user_data_array_set_data (&image->user_data,
+ (const cairo_user_data_key_t *) connection,
+ shm_info,
+ (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ *shm_info_out = shm_info;
+ return image;
+}
+#endif
+
+static cairo_surface_t *
+_get_shm_image (cairo_xcb_surface_t *surface,
+ int x, int y,
+ int width, int height)
+{
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_surface_t *image;
+ cairo_status_t status;
+
+ if ((surface->connection->flags & CAIRO_XCB_HAS_SHM) == 0)
+ return NULL;
+
+ image = _cairo_xcb_surface_create_shm_image (surface->connection,
+ surface->pixman_format,
+ width, height,
+ TRUE,
+ &shm_info);
+ if (unlikely (image == NULL || image->status))
+ goto done;
+
+ status = _cairo_xcb_connection_shm_get_image (surface->connection,
+ surface->drawable,
+ x, y,
+ width, height,
+ shm_info->shm,
+ shm_info->offset);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ image = _cairo_surface_create_in_error (status);
+ }
+
+done:
+ return image;
+#else
+ return NULL;
+#endif
+}
+
+static cairo_surface_t *
+_get_image (cairo_xcb_surface_t *surface,
+ cairo_bool_t use_shm,
+ int x, int y,
+ int width, int height)
+{
+ cairo_surface_t *image = NULL;
+ cairo_xcb_connection_t *connection;
+ xcb_get_image_reply_t *reply;
+ cairo_int_status_t status;
+
+ assert (surface->fallback == NULL);
+ assert (x >= 0);
+ assert (y >= 0);
+ assert (x + width <= surface->width);
+ assert (y + height <= surface->height);
+
+ if (surface->deferred_clear) {
+ image =
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ surface->pixman_format,
+ width, height,
+ 0);
+ if (surface->deferred_clear_color.alpha_short > 0x00ff) {
+ cairo_solid_pattern_t solid;
+
+ _cairo_pattern_init_solid (&solid, &surface->deferred_clear_color);
+ status = _cairo_surface_paint (image,
+ CAIRO_OPERATOR_SOURCE,
+ &solid.base,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ image = _cairo_surface_create_in_error (status);
+ }
+ }
+ return image;
+ }
+
+ connection = surface->connection;
+
+ status = _cairo_xcb_connection_acquire (connection);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (use_shm) {
+ image = _get_shm_image (surface, x, y, width, height);
+ if (image) {
+ if (image->status == CAIRO_STATUS_SUCCESS) {
+ _cairo_xcb_connection_release (connection);
+ return image;
+ }
+ cairo_surface_destroy (image);
+ }
+ }
+
+ status = _cairo_xcb_connection_get_image (connection,
+ surface->drawable,
+ x, y,
+ width, height,
+ &reply);
+ if (unlikely (status))
+ goto FAIL;
+
+ if (reply == NULL && ! surface->owns_pixmap) {
+ /* xcb_get_image_t from a window is dangerous because it can
+ * produce errors if the window is unmapped or partially
+ * outside the screen. We could check for errors and
+ * retry, but to keep things simple, we just create a
+ * temporary pixmap
+ *
+ * If we hit this fallback too often, we should remember so and
+ * skip the round-trip from the above GetImage request,
+ * similar to what cairo-xlib does.
+ */
+ xcb_pixmap_t pixmap;
+ xcb_gcontext_t gc;
+
+ gc = _cairo_xcb_screen_get_gc (surface->screen,
+ surface->drawable,
+ surface->depth);
+ pixmap = _cairo_xcb_connection_create_pixmap (connection,
+ surface->depth,
+ surface->drawable,
+ width, height);
+
+ /* XXX IncludeInferiors? */
+ _cairo_xcb_connection_copy_area (connection,
+ surface->drawable,
+ pixmap, gc,
+ x, y,
+ 0, 0,
+ width, height);
+
+ _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
+
+ status = _cairo_xcb_connection_get_image (connection,
+ pixmap,
+ 0, 0,
+ width, 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_create_with_pixman_format
+ (xcb_get_image_data (reply),
+ surface->pixman_format,
+ width, height,
+ CAIRO_STRIDE_FOR_WIDTH_BPP (width,
+ PIXMAN_FORMAT_BPP (surface->pixman_format)));
+ status = image->status;
+ if (unlikely (status)) {
+ free (reply);
+ goto FAIL;
+ }
+
+ /* XXX */
+ pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply);
+
+ _cairo_xcb_connection_release (connection);
+
+ return image;
+
+FAIL:
+ cairo_surface_destroy (image);
+ _cairo_xcb_connection_release (connection);
+ return _cairo_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_xcb_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+
+ if (extents) {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ if (surface->fallback != NULL) {
+ image = cairo_surface_reference (&surface->fallback->base);
+ goto DONE;
+ }
+
+ image = _cairo_surface_has_snapshot (&surface->base,
+ &_cairo_image_surface_backend);
+ if (image != NULL) {
+ image = cairo_surface_reference (image);
+ goto DONE;
+ }
+
+ image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height);
+ if (unlikely (image->status)) {
+ status = image->status;
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ _cairo_surface_attach_snapshot (&surface->base, image, NULL);
+
+DONE:
+ *image_out = (cairo_image_surface_t *) image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xcb_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+cairo_bool_t
+_cairo_xcb_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ return TRUE;
+}
+
+static void
+_cairo_xcb_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ /* 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;
+
+ _cairo_xcb_connection_shm_put_image (surface->connection,
+ surface->drawable,
+ gc,
+ surface->width, surface->height,
+ 0, 0,
+ image->width, image->height,
+ image->base.device_transform_inverse.x0,
+ image->base.device_transform_inverse.y0,
+ image->depth,
+ shm_info->shm,
+ shm_info->offset);
+
+ return CAIRO_STATUS_SUCCESS;
+#else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+static cairo_status_t
+_put_image (cairo_xcb_surface_t *surface,
+ cairo_image_surface_t *image)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ /* XXX track damaged region? */
+
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (unlikely (status))
+ return status;
+
+ if (image->pixman_format == surface->pixman_format) {
+ xcb_gcontext_t gc;
+
+ assert (image->depth == surface->depth);
+ assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)));
+
+ gc = _cairo_xcb_screen_get_gc (surface->screen,
+ surface->drawable,
+ surface->depth);
+
+ status = _put_shm_image (surface, gc, image);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ _cairo_xcb_connection_put_image (surface->connection,
+ surface->drawable, gc,
+ image->width, image->height,
+ image->base.device_transform_inverse.x0,
+ image->base.device_transform_inverse.y0,
+ image->depth,
+ image->stride,
+ image->data);
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
+ } else {
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_xcb_connection_release (surface->connection);
+ return status;
+}
+
+static cairo_int_status_t
+_put_shm_image_boxes (cairo_xcb_surface_t *surface,
+ cairo_image_surface_t *image,
+ xcb_gcontext_t gc,
+ cairo_boxes_t *boxes)
+{
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ cairo_xcb_shm_info_t *shm_info;
+
+ shm_info = _cairo_user_data_array_get_data (&image->base.user_data,
+ (const cairo_user_data_key_t *) surface->connection);
+ if (shm_info != NULL) {
+ struct _cairo_boxes_chunk *chunk;
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ int i;
+
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+ int x = _cairo_fixed_integer_part (b->p1.x);
+ int y = _cairo_fixed_integer_part (b->p1.y);
+ int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x);
+ int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y);
+
+ _cairo_xcb_connection_shm_put_image (surface->connection,
+ surface->drawable,
+ gc,
+ surface->width, surface->height,
+ x, y,
+ width, height,
+ x, y,
+ image->depth,
+ shm_info->shm,
+ shm_info->offset);
+ }
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+#endif
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_put_image_boxes (cairo_xcb_surface_t *surface,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+ xcb_gcontext_t gc;
+
+ if (boxes->num_boxes == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* XXX track damaged region? */
+
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (unlikely (status))
+ return status;
+
+ assert (image->pixman_format == surface->pixman_format);
+ assert (image->depth == surface->depth);
+ assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)));
+
+ gc = _cairo_xcb_screen_get_gc (surface->screen,
+ surface->drawable,
+ surface->depth);
+
+ status = _put_shm_image_boxes (surface, image, gc, boxes);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ struct _cairo_boxes_chunk *chunk;
+
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ int i;
+
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+ int x = _cairo_fixed_integer_part (b->p1.x);
+ int y = _cairo_fixed_integer_part (b->p1.y);
+ int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x);
+ int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y);
+ _cairo_xcb_connection_put_image (surface->connection,
+ surface->drawable, gc,
+ width, height,
+ x, y,
+ image->depth,
+ image->stride,
+ image->data +
+ x * PIXMAN_FORMAT_BPP (image->pixman_format) / 8 +
+ y * image->stride);
+
+ }
+ }
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
+ _cairo_xcb_connection_release (surface->connection);
+ return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (likely (surface->fallback == NULL)) {
+ status = CAIRO_STATUS_SUCCESS;
+ if (! surface->base.finished && surface->deferred_clear)
+ status = _cairo_xcb_surface_clear (surface);
+
+ return status;
+ }
+
+ status = surface->base.status;
+ if (status == CAIRO_STATUS_SUCCESS &&
+ (! surface->base._finishing || ! surface->owns_pixmap)) {
+ status = cairo_surface_status (&surface->fallback->base);
+
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_bentley_ottmann_tessellate_boxes (&surface->fallback_damage,
+ CAIRO_FILL_RULE_WINDING,
+ &surface->fallback_damage);
+
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _put_image_boxes (surface,
+ surface->fallback,
+ &surface->fallback_damage);
+
+ if (status == CAIRO_STATUS_SUCCESS && ! surface->base._finishing) {
+ _cairo_surface_attach_snapshot (&surface->base,
+ &surface->fallback->base,
+ cairo_surface_finish);
+ }
+ }
+
+ _cairo_boxes_clear (&surface->fallback_damage);
+ cairo_surface_destroy (&surface->fallback->base);
+ surface->fallback = NULL;
+
+ return status;
+}
+
+static cairo_image_surface_t *
+_cairo_xcb_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+ cairo_status_t status;
+
+ if (surface->fallback)
+ return _cairo_surface_map_to_image (&surface->fallback->base, extents);
+
+ image = _get_image (surface, TRUE,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ status = cairo_surface_status (image);
+ if (unlikely (status)) {
+ cairo_surface_destroy(image);
+ return _cairo_image_surface_create_in_error (status);
+ }
+
+ /* Do we have a deferred clear and this image surface does NOT cover the
+ * whole xcb surface? Have to apply the clear in that case, else
+ * uploading the image will handle the problem for us.
+ */
+ if (surface->deferred_clear &&
+ ! (extents->width == surface->width &&
+ extents->height == surface->height)) {
+ status = _cairo_xcb_surface_clear (surface);
+ if (unlikely (status)) {
+ cairo_surface_destroy(image);
+ return _cairo_image_surface_create_in_error (status);
+ }
+ }
+ surface->deferred_clear = FALSE;
+
+ cairo_surface_set_device_offset (image, -extents->x, -extents->y);
+ return (cairo_image_surface_t *) image;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_unmap (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ if (surface->fallback)
+ return _cairo_surface_unmap_image (&surface->fallback->base, image);
+
+ status = _put_image (abstract_surface, image);
+
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_add_to_damage (composite,
+ &surface->fallback_damage);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (surface->fallback)
+ return &surface->fallback->base;
+
+ image = (cairo_image_surface_t *)
+ _get_image (surface, TRUE, 0, 0, surface->width, surface->height);
+
+ /* If there was a deferred clear, _get_image applied it */
+ if (image->base.status == CAIRO_STATUS_SUCCESS) {
+ surface->deferred_clear = FALSE;
+
+ surface->fallback = image;
+ } else {
+ cairo_surface_destroy (&image->base);
+ }
+ return &surface->fallback->base;
+}
+
+static cairo_int_status_t
+_cairo_xcb_fallback_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
+ cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
+
+ return _cairo_surface_paint (fallback, extents->op,
+ &extents->source_pattern.base,
+ extents->clip);
+}
+
+static cairo_int_status_t
+_cairo_xcb_fallback_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
+ cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
+
+ return _cairo_surface_mask (fallback, extents->op,
+ &extents->source_pattern.base,
+ &extents->mask_pattern.base,
+ extents->clip);
+}
+
+static cairo_int_status_t
+_cairo_xcb_fallback_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
+ cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
+
+ return _cairo_surface_stroke (fallback, extents->op,
+ &extents->source_pattern.base,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ extents->clip);
+}
+
+static cairo_int_status_t
+_cairo_xcb_fallback_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
+ cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
+
+ return _cairo_surface_fill (fallback, extents->op,
+ &extents->source_pattern.base,
+ path, fill_rule, tolerance,
+ antialias, extents->clip);
+}
+
+static cairo_int_status_t
+_cairo_xcb_fallback_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
+ cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
+
+ return _cairo_surface_show_text_glyphs (fallback, extents->op,
+ &extents->source_pattern.base,
+ NULL, 0, glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font, extents->clip);
+}
+
+static const cairo_compositor_t _cairo_xcb_fallback_compositor = {
+ &__cairo_no_compositor,
+
+ _cairo_xcb_fallback_compositor_paint,
+ _cairo_xcb_fallback_compositor_mask,
+ _cairo_xcb_fallback_compositor_stroke,
+ _cairo_xcb_fallback_compositor_fill,
+ _cairo_xcb_fallback_compositor_glyphs,
+};
+
+static const cairo_compositor_t _cairo_xcb_render_compositor = {
+ &_cairo_xcb_fallback_compositor,
+
+ _cairo_xcb_render_compositor_paint,
+ _cairo_xcb_render_compositor_mask,
+ _cairo_xcb_render_compositor_stroke,
+ _cairo_xcb_render_compositor_fill,
+ _cairo_xcb_render_compositor_glyphs,
+};
+
+static inline const cairo_compositor_t *
+get_compositor (cairo_surface_t **s)
+{
+ cairo_xcb_surface_t *surface = (cairo_xcb_surface_t * )*s;
+ if (surface->fallback) {
+ *s = &surface->fallback->base;
+ return ((cairo_image_surface_t *) *s)->compositor;
+ }
+
+ return &_cairo_xcb_render_compositor;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface = abstract_surface;
+ const cairo_compositor_t *compositor = get_compositor (&surface);
+ cairo_int_status_t status;
+
+ status = cairo_device_acquire (surface->device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_paint (surface, op, source, clip,
+ &source->shadow);
+ if (unlikely (status) ||
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ status = _cairo_compositor_paint (compositor, surface, op, source, clip);
+ cairo_device_release (surface->device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface = abstract_surface;
+ const cairo_compositor_t *compositor = get_compositor (&surface);
+ cairo_int_status_t status;
+
+ status = cairo_device_acquire (surface->device);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_shadow_mask (surface, op, source, mask, clip,
+ &source->shadow);
+ if (unlikely (status) ||
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ status = _cairo_compositor_mask (compositor, surface, op, source, mask, clip);
+ cairo_device_release (surface->device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface = abstract_surface;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ const cairo_compositor_t *compositor = get_compositor (&surface);
+ cairo_int_status_t status;
+
+ status = cairo_device_acquire (surface->device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (surface, op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip,
+ &source->shadow);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_stroke (compositor, surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_stroke (surface, op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip,
+ &source->shadow);
+ cairo_device_release (surface->device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t*path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface = abstract_surface;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ const cairo_compositor_t *compositor = get_compositor (&surface);
+ cairo_int_status_t status;
+
+ status = cairo_device_acquire (surface->device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (surface, op, source, path,
+ fill_rule, tolerance,
+ antialias, clip,
+ &source->shadow);
+ if (unlikely (status)) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only) {
+ if (! source->shadow.path_is_fill_with_spread ||
+ source->shadow.type != CAIRO_SHADOW_INSET)
+ status = _cairo_compositor_fill (compositor, surface, op,
+ source, path, fill_rule,
+ tolerance, antialias, clip);
+ else
+ status = _cairo_compositor_paint (compositor, surface, op,
+ source, clip);
+ }
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_fill (surface, op, source, path,
+ fill_rule, tolerance,
+ antialias, clip,
+ &source->shadow);
+
+ cairo_device_release (surface->device);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface = abstract_surface;
+ cairo_shadow_type_t shadow_type = source->shadow.type;
+ const cairo_compositor_t *compositor = get_compositor (&surface);
+ cairo_int_status_t status;
+
+ status = cairo_device_acquire (surface->device);
+ if (unlikely (status))
+ return status;
+
+ if (shadow_type != CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (surface, op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &source->shadow);
+ if (unlikely (status)) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_DROP &&
+ source->shadow.draw_shadow_only) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (! source->shadow.draw_shadow_only)
+ status = _cairo_compositor_glyphs (compositor, surface, op,
+ source, glyphs, num_glyphs,
+ scaled_font, clip);
+
+ if (unlikely (status)) {
+ cairo_device_release (surface->device);
+ return status;
+ }
+
+ if (shadow_type == CAIRO_SHADOW_INSET)
+ status = _cairo_surface_shadow_glyphs (surface, op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &source->shadow);
+ cairo_device_release (surface->device);
+ return status;
+}
+
+const cairo_surface_backend_t _cairo_xcb_surface_backend = {
+ CAIRO_SURFACE_TYPE_XCB,
+ _cairo_xcb_surface_finish,
+ _cairo_default_context_create,
+
+ _cairo_xcb_surface_create_similar,
+ _cairo_xcb_surface_create_similar_image,
+ _cairo_xcb_surface_map_to_image,
+ _cairo_xcb_surface_unmap,
+
+ _cairo_xcb_surface_source,
+ _cairo_xcb_surface_acquire_source_image,
+ _cairo_xcb_surface_release_source_image,
+ NULL, /* snapshot */
+
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_xcb_surface_get_extents,
+ _cairo_xcb_surface_get_font_options,
+
+ _cairo_xcb_surface_flush,
+ NULL,
+
+ _cairo_xcb_surface_paint,
+ _cairo_xcb_surface_mask,
+ _cairo_xcb_surface_stroke,
+ _cairo_xcb_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_xcb_surface_glyphs,
+};
+
+cairo_surface_t *
+_cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ cairo_bool_t owns_pixmap,
+ pixman_format_code_t pixman_format,
+ xcb_render_pictformat_t xrender_format,
+ int width,
+ int height)
+{
+ cairo_xcb_surface_t *surface;
+
+ surface = 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->drawable = drawable;
+ surface->owns_pixmap = owns_pixmap;
+
+ surface->deferred_clear = FALSE;
+ surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT;
+
+ surface->width = width;
+ surface->height = height;
+ surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format);
+
+ surface->picture = XCB_NONE;
+ if (screen->connection->force_precision != -1)
+ surface->precision = screen->connection->force_precision;
+ else
+ surface->precision = XCB_RENDER_POLY_MODE_IMPRECISE;
+
+ surface->pixman_format = pixman_format;
+ surface->xrender_format = xrender_format;
+
+ surface->fallback = NULL;
+ _cairo_boxes_init (&surface->fallback_damage);
+
+ return &surface->base;
+}
+
+static xcb_screen_t *
+_cairo_xcb_screen_from_visual (xcb_connection_t *connection,
+ xcb_visualtype_t *visual,
+ int *depth)
+{
+ xcb_depth_iterator_t d;
+ xcb_screen_iterator_t s;
+ xcb_setup_t *set_up = NULL;
+
+ set_up = xcb_get_setup (connection);
+ if(set_up == NULL)
+ return NULL;
+
+ s = xcb_setup_roots_iterator (set_up);
+ for (; s.rem; xcb_screen_next (&s)) {
+ if (s.data->root_visual == visual->visual_id) {
+ *depth = s.data->root_depth;
+ return s.data;
+ }
+
+ d = xcb_screen_allowed_depths_iterator(s.data);
+ for (; d.rem; xcb_depth_next (&d)) {
+ xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data);
+
+ for (; v.rem; xcb_visualtype_next (&v)) {
+ if (v.data->visual_id == visual->visual_id) {
+ *depth = d.data->depth;
+ return s.data;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * cairo_xcb_surface_create:
+ * @connection: an XCB connection
+ * @drawable: an XCB drawable
+ * @visual: the visual to use for drawing to @drawable. The depth
+ * of the visual must match the depth of the drawable.
+ * Currently, only TrueColor visuals are fully supported.
+ * @width: the current width of @drawable
+ * @height: the current height of @drawable
+ *
+ * Creates an XCB surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided visual.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xcb_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * When @drawable is a Window containing child windows then drawing to
+ * the created surface will be clipped by those child windows. When
+ * the created surface is used as a source, the contents of the
+ * children will be included.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_xcb_surface_create (xcb_connection_t *connection,
+ xcb_drawable_t drawable,
+ xcb_visualtype_t *visual,
+ int width,
+ int height)
+{
+ cairo_xcb_screen_t *screen;
+ xcb_screen_t *xcb_screen;
+ cairo_format_masks_t image_masks;
+ pixman_format_code_t pixman_format;
+ xcb_render_pictformat_t xrender_format;
+ int depth;
+
+ if (xcb_connection_has_error (connection))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+ if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ if (unlikely (width <= 0 || height <= 0))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ xcb_screen = _cairo_xcb_screen_from_visual (connection, visual, &depth);
+ if (unlikely (xcb_screen == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+ image_masks.alpha_mask = 0;
+ image_masks.red_mask = visual->red_mask;
+ image_masks.green_mask = visual->green_mask;
+ image_masks.blue_mask = visual->blue_mask;
+ if (depth == 32) /* XXX visuals have no alpha! */
+ image_masks.alpha_mask =
+ 0xffffffff & ~(visual->red_mask | visual->green_mask | visual->blue_mask);
+ if (depth > 16)
+ image_masks.bpp = 32;
+ else if (depth > 8)
+ image_masks.bpp = 16;
+ else if (depth > 1)
+ image_masks.bpp = 8;
+ else
+ image_masks.bpp = 1;
+
+ if (! _pixman_format_from_masks (&image_masks, &pixman_format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ screen = _cairo_xcb_screen_get (connection, xcb_screen);
+ if (unlikely (screen == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ xrender_format =
+ _cairo_xcb_connection_get_xrender_format_for_visual (screen->connection,
+ visual->visual_id);
+
+ return _cairo_xcb_surface_create_internal (screen, drawable, FALSE,
+ pixman_format,
+ xrender_format,
+ width, height);
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_create);
+#endif
+
+/**
+ * cairo_xcb_surface_create_for_bitmap:
+ * @connection: an XCB connection
+ * @screen: the XCB screen associated with @bitmap
+ * @bitmap: an XCB drawable (a Pixmap with depth 1)
+ * @width: the current width of @bitmap
+ * @height: the current height of @bitmap
+ *
+ * Creates an XCB surface that draws to the given bitmap.
+ * This will be drawn to as a %CAIRO_FORMAT_A1 object.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection,
+ xcb_screen_t *screen,
+ xcb_pixmap_t bitmap,
+ int width,
+ int height)
+{
+ cairo_xcb_screen_t *cairo_xcb_screen;
+
+ if (xcb_connection_has_error (connection))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ if (unlikely (width <= 0 || height <= 0))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen);
+ if (unlikely (cairo_xcb_screen == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _cairo_xcb_surface_create_internal (cairo_xcb_screen, bitmap, FALSE,
+ PIXMAN_a1,
+ cairo_xcb_screen->connection->standard_formats[CAIRO_FORMAT_A1],
+ width, height);
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_create_for_bitmap);
+#endif
+
+/**
+ * cairo_xcb_surface_create_with_xrender_format:
+ * @connection: an XCB connection
+ * @drawable: an XCB drawable
+ * @screen: the XCB screen associated with @drawable
+ * @format: the picture format to use for drawing to @drawable. The
+ * depth of @format mush match the depth of the drawable.
+ * @width: the current width of @drawable
+ * @height: the current height of @drawable
+ *
+ * Creates an XCB surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided picture format.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xcb_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * When @drawable is a Window containing child windows then drawing to
+ * the created surface will be clipped by those child windows. When
+ * the created surface is used as a source, the contents of the
+ * children will be included.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection,
+ xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ xcb_render_pictforminfo_t *format,
+ int width,
+ int height)
+{
+ cairo_xcb_screen_t *cairo_xcb_screen;
+ cairo_format_masks_t image_masks;
+ pixman_format_code_t pixman_format;
+
+ if (xcb_connection_has_error (connection))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ if (unlikely (width <= 0 || height <= 0))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ image_masks.alpha_mask =
+ (unsigned long) format->direct.alpha_mask << format->direct.alpha_shift;
+ image_masks.red_mask =
+ (unsigned long) format->direct.red_mask << format->direct.red_shift;
+ image_masks.green_mask =
+ (unsigned long) format->direct.green_mask << format->direct.green_shift;
+ image_masks.blue_mask =
+ (unsigned long) format->direct.blue_mask << format->direct.blue_shift;
+#if 0
+ image_masks.bpp = format->depth;
+#else
+ if (format->depth > 16)
+ image_masks.bpp = 32;
+ else if (format->depth > 8)
+ image_masks.bpp = 16;
+ else if (format->depth > 1)
+ image_masks.bpp = 8;
+ else
+ image_masks.bpp = 1;
+#endif
+
+ if (! _pixman_format_from_masks (&image_masks, &pixman_format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen);
+ if (unlikely (cairo_xcb_screen == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _cairo_xcb_surface_create_internal (cairo_xcb_screen,
+ drawable,
+ FALSE,
+ pixman_format,
+ format->id,
+ width, height);
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_create_with_xrender_format);
+#endif
+
+/* This does the necessary fixup when a surface's drawable or size changed. */
+static void
+_drawable_changed (cairo_xcb_surface_t *surface)
+{
+ _cairo_surface_set_error (&surface->base,
+ _cairo_surface_begin_modification (&surface->base));
+ _cairo_boxes_clear (&surface->fallback_damage);
+ cairo_surface_destroy (&surface->fallback->base);
+
+ surface->deferred_clear = FALSE;
+ surface->fallback = NULL;
+}
+
+/**
+ * cairo_xcb_surface_set_size:
+ * @surface: a #cairo_surface_t for the XCB backend
+ * @width: the new width of the surface
+ * @height: the new height of the surface
+ *
+ * Informs cairo of the new size of the XCB drawable underlying the
+ * surface. For a surface created for a window (rather than a pixmap),
+ * this function must be called each time the size of the window
+ * changes. (For a subwindow, you are normally resizing the window
+ * yourself, but for a toplevel window, it is necessary to listen for
+ * ConfigureNotify events.)
+ *
+ * A pixmap can never change size, so it is never necessary to call
+ * this function on a surface created for a pixmap.
+ *
+ * If cairo_surface_flush() wasn't called, some pending operations
+ * might be discarded.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_xcb_surface_t *surface;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+
+ if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ surface = (cairo_xcb_surface_t *) abstract_surface;
+
+ _drawable_changed(surface);
+ surface->width = width;
+ surface->height = height;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_set_size);
+#endif
+
+/**
+ * cairo_xcb_surface_set_drawable:
+ * @surface: a #cairo_surface_t for the XCB backend
+ * @drawable: the new drawable of the surface
+ * @width: the new width of the surface
+ * @height: the new height of the surface
+ *
+ * Informs cairo of the new drawable and size of the XCB drawable underlying the
+ * surface.
+ *
+ * If cairo_surface_flush() wasn't called, some pending operations
+ * might be discarded.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_xcb_surface_set_drawable (cairo_surface_t *abstract_surface,
+ xcb_drawable_t drawable,
+ int width,
+ int height)
+{
+ cairo_xcb_surface_t *surface;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+
+ if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ surface = (cairo_xcb_surface_t *) abstract_surface;
+
+ /* XXX: and what about this case? */
+ if (surface->owns_pixmap)
+ return;
+
+ _drawable_changed (surface);
+
+ if (surface->drawable != drawable) {
+ cairo_status_t status;
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (unlikely (status))
+ return;
+
+ if (surface->picture != XCB_NONE) {
+ _cairo_xcb_connection_render_free_picture (surface->connection,
+ surface->picture);
+ surface->picture = XCB_NONE;
+ }
+
+ _cairo_xcb_connection_release (surface->connection);
+
+ surface->drawable = drawable;
+ }
+ surface->width = width;
+ surface->height = height;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_set_drawable);
+#endif
diff --git a/src/cairo-xcb.h b/src/cairo-xcb.h
new file mode 100755
index 000000000..e321d8482
--- /dev/null
+++ b/src/cairo-xcb.h
@@ -0,0 +1,116 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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);
+
+cairo_public void
+cairo_xcb_surface_set_drawable (cairo_surface_t *surface,
+ xcb_drawable_t drawable,
+ int width,
+ int height);
+
+cairo_public xcb_connection_t *
+cairo_xcb_device_get_connection (cairo_device_t *device);
+
+/* debug interface */
+
+cairo_public void
+cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
+ int major_version,
+ int minor_version);
+
+cairo_public void
+cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
+ int major_version,
+ int minor_version);
+
+/*
+ * @precision: -1 implies automatically choose based on antialiasing mode,
+ * any other value overrides and sets the corresponding PolyMode.
+ */
+cairo_public void
+cairo_xcb_device_debug_set_precision (cairo_device_t *device,
+ int precision);
+
+cairo_public int
+cairo_xcb_device_debug_get_precision (cairo_device_t *device);
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_XCB_SURFACE */
+# error Cairo was not compiled with support for the xcb backend
+#endif /* CAIRO_HAS_XCB_SURFACE */
+
+#endif /* CAIRO_XCB_H */
diff --git a/src/cairo-xlib-core-compositor.c b/src/cairo-xlib-core-compositor.c
new file mode 100755
index 000000000..9398079b4
--- /dev/null
+++ b/src/cairo-xlib-core-compositor.c
@@ -0,0 +1,652 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Behdad Esfahbod <behdad@behdad.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+/* The original X drawing API was very restrictive in what it could handle,
+ * pixel-aligned fill/blits are all that map into Cairo's drawing model.
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+
+/* the low-level interface */
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+ return _cairo_xlib_display_acquire (dst->base.device, &dst->display);
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+
+ cairo_device_release (&dst->display->base);
+ dst->display = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct _fill_box {
+ Display *dpy;
+ Drawable drawable;
+ GC gc;
+ //cairo_surface_t *dither = NULL;
+};
+
+static cairo_bool_t fill_box (cairo_box_t *box, void *closure)
+{
+ struct _fill_box *data = closure;
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+ XFillRectangle (data->dpy, data->drawable, data->gc, x, y, width, height);
+ return TRUE;
+}
+
+static void
+_characterize_field (uint32_t mask, int *width, int *shift)
+{
+ *width = _cairo_popcount (mask);
+ /* The final '& 31' is to force a 0 mask to result in 0 shift. */
+ *shift = _cairo_popcount ((mask - 1) & ~mask) & 31;
+}
+
+static uint32_t
+color_to_pixel (cairo_xlib_surface_t *dst,
+ const cairo_color_t *color)
+{
+ uint32_t rgba = 0;
+ int width, shift;
+
+ _characterize_field (dst->a_mask, &width, &shift);
+ rgba |= color->alpha_short >> (16 - width) << shift;
+
+ _characterize_field (dst->r_mask, &width, &shift);
+ rgba |= color->red_short >> (16 - width) << shift;
+
+ _characterize_field (dst->g_mask, &width, &shift);
+ rgba |= color->green_short >> (16 - width) << shift;
+
+ _characterize_field (dst->b_mask, &width, &shift);
+ rgba |= color->blue_short >> (16 - width) << shift;
+
+ return rgba;
+}
+
+static cairo_int_status_t
+_fill_box_init (struct _fill_box *fb,
+ cairo_xlib_surface_t *dst,
+ const cairo_color_t *color)
+{
+ cairo_int_status_t status;
+
+ status = _cairo_xlib_surface_get_gc (dst->display, dst, &fb->gc);
+ if (unlikely (status))
+ return status;
+
+ fb->dpy = dst->display->display;
+ fb->drawable = dst->drawable;
+
+ if (dst->visual && dst->visual->class != TrueColor && 0) {
+#if 0
+ cairo_solid_pattern_t solid;
+ cairo_surface_attributes_t attrs;
+
+ _cairo_pattern_init_solid (&solid, color);
+ status = _cairo_pattern_acquire_surface (&solid.base, &dst->base,
+ 0, 0,
+ ARRAY_LENGTH (dither_pattern[0]),
+ ARRAY_LENGTH (dither_pattern),
+ CAIRO_PATTERN_ACQUIRE_NONE,
+ &dither,
+ &attrs);
+ if (unlikely (status)) {
+ _cairo_xlib_surface_put_gc (dst->display, dst, fb.gc);
+ return status;
+ }
+
+ XSetTSOrigin (fb->dpy, fb->gc,
+ - (dst->base.device_transform.x0 + attrs.x_offset),
+ - (dst->base.device_transform.y0 + attrs.y_offset));
+ XSetTile (fb->dpy, fb->gc, ((cairo_xlib_surface_t *) dither)->drawable);
+#endif
+ } else {
+ XGCValues gcv;
+
+ gcv.foreground = color_to_pixel (dst, color);
+ gcv.fill_style = FillSolid;
+
+ XChangeGC (fb->dpy, fb->gc, GCFillStyle | GCForeground, &gcv);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static void
+_fill_box_fini (struct _fill_box *fb,
+ cairo_xlib_surface_t *dst)
+{
+ _cairo_xlib_surface_put_gc (dst->display, dst, fb->gc);
+ //cairo_surface_destroy (fb->dither);
+}
+
+cairo_int_status_t
+_cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ cairo_int_status_t status;
+ struct _fill_box fb;
+
+ status = _fill_box_init (&fb, dst, color);
+ if (unlikely (status))
+ return status;
+
+ _cairo_boxes_for_each_box (boxes, fill_box, &fb);
+
+ _fill_box_fini (&fb, dst);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst,
+ const cairo_color_t *color,
+ int num_rects,
+ cairo_rectangle_int_t *rects)
+{
+ cairo_int_status_t status;
+ struct _fill_box fb;
+ int i;
+
+ status = _fill_box_init (&fb, dst, color);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < num_rects; i++)
+ XFillRectangle (fb.dpy, fb.drawable, fb.gc,
+ rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height);
+
+ _fill_box_fini (&fb, dst);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct _fallback_box {
+ cairo_xlib_surface_t *dst;
+ cairo_format_t format;
+ const cairo_pattern_t *pattern;
+};
+
+static cairo_bool_t fallback_box (cairo_box_t *box, void *closure)
+{
+ struct _fallback_box *data = closure;
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+ cairo_surface_t *image;
+ cairo_status_t status;
+
+ /* XXX for EXTEND_NONE and if the box is wholly outside we can just fill */
+
+ image = cairo_surface_create_similar_image (&data->dst->base, data->format,
+ width, height);
+ status = _cairo_surface_offset_paint (image, x, y,
+ CAIRO_OPERATOR_SOURCE,
+ data->pattern, NULL);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _cairo_xlib_surface_draw_image (data->dst,
+ (cairo_image_surface_t *)image,
+ 0, 0,
+ width, height,
+ x, y);
+ }
+ cairo_surface_destroy (image);
+
+ return status == CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fallback_boxes (cairo_xlib_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_boxes_t *boxes)
+{
+ struct _fallback_box fb;
+
+ /* XXX create_similar_image using pixman_format? */
+ switch (dst->depth) {
+ case 8: fb.format = CAIRO_FORMAT_A8; break;
+ case 16: fb.format = CAIRO_FORMAT_RGB16_565; break;
+ case 24: fb.format = CAIRO_FORMAT_RGB24; break;
+ case 30: fb.format = CAIRO_FORMAT_RGB30; break;
+ case 32: fb.format = CAIRO_FORMAT_ARGB32; break;
+ default: return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ fb.dst = dst;
+ fb.pattern = pattern;
+
+ if (! _cairo_boxes_for_each_box (boxes, fallback_box, &fb))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+render_boxes (cairo_xlib_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_boxes_t *boxes)
+{
+ double pad;
+
+ if (_cairo_pattern_analyze_filter (pattern, &pad) != CAIRO_FILTER_NEAREST)
+ return fallback_boxes (dst, pattern, boxes);
+
+ switch (pattern->extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_REFLECT:
+ case CAIRO_EXTEND_PAD:
+ return fallback_boxes (dst, pattern, boxes);
+
+ case CAIRO_EXTEND_REPEAT: /* XXX Use tiling */
+ return fallback_boxes (dst, pattern, boxes);
+ }
+}
+
+/* the mid-level: converts boxes into drawing operations */
+
+struct _box_data {
+ Display *dpy;
+ cairo_xlib_surface_t *dst;
+ cairo_surface_t *src;
+ GC gc;
+ int tx, ty;
+ int width, height;
+};
+
+static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure)
+{
+ struct _box_data *data = closure;
+
+ /* The box is pixel-aligned so the truncation is safe. */
+ return
+ _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 &&
+ _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 &&
+ _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width &&
+ _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height;
+}
+
+static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure)
+{
+ const struct _box_data *iub = closure;
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+ return _cairo_xlib_surface_draw_image (iub->dst,
+ (cairo_image_surface_t *)iub->src,
+ x + iub->tx, y + iub->ty,
+ width, height,
+ x, y) == CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+surface_matches_image_format (cairo_xlib_surface_t *surface,
+ cairo_image_surface_t *image)
+{
+ cairo_format_masks_t format;
+
+ return (_pixman_format_to_masks (image->pixman_format, &format) &&
+ (format.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
+ (format.red_mask == surface->r_mask || surface->r_mask == 0) &&
+ (format.green_mask == surface->g_mask || surface->g_mask == 0) &&
+ (format.blue_mask == surface->b_mask || surface->b_mask == 0));
+}
+
+static cairo_status_t
+upload_image_inplace (cairo_xlib_surface_t *dst,
+ const cairo_pattern_t *source,
+ cairo_boxes_t *boxes)
+{
+ const cairo_surface_pattern_t *pattern;
+ struct _box_data iub;
+ cairo_image_surface_t *image;
+
+ if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ image = (cairo_image_surface_t *) pattern->surface;
+ if (image->format == CAIRO_FORMAT_INVALID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (image->depth != dst->depth)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! surface_matches_image_format (dst, image))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* XXX subsurface */
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix,
+ &iub.tx, &iub.ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ iub.dst = dst;
+ iub.src = &image->base;
+ iub.width = image->width;
+ iub.height = image->height;
+
+ /* First check that the data is entirely within the image */
+ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &iub))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_boxes_for_each_box (boxes, image_upload_box, &iub))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t copy_box (cairo_box_t *box, void *closure)
+{
+ const struct _box_data *cb = closure;
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+ XCopyArea (cb->dpy,
+ ((cairo_xlib_surface_t *)cb->src)->drawable,
+ cb->dst->drawable,
+ cb->gc,
+ x + cb->tx, y + cb->ty,
+ width, height,
+ x, y);
+ return TRUE;
+}
+
+static cairo_status_t
+copy_boxes (cairo_xlib_surface_t *dst,
+ const cairo_pattern_t *source,
+ cairo_boxes_t *boxes)
+{
+ const cairo_surface_pattern_t *pattern;
+ struct _box_data cb;
+ cairo_xlib_surface_t *src;
+ cairo_status_t status;
+
+ if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* XXX subsurface */
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ if (pattern->surface->backend->type != CAIRO_SURFACE_TYPE_XLIB)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ src = (cairo_xlib_surface_t *) pattern->surface;
+ if (src->depth != dst->depth)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* We can only have a single control for subwindow_mode on the
+ * GC. If we have a Window destination, we need to set ClipByChildren,
+ * but if we have a Window source, we need IncludeInferiors. If we have
+ * both a Window destination and source, we must fallback. There is
+ * no convenient way to detect if a drawable is a Pixmap or Window,
+ * therefore we can only rely on those surfaces that we created
+ * ourselves to be Pixmaps, and treat everything else as a potential
+ * Window.
+ */
+ if (! src->owns_pixmap && ! dst->owns_pixmap)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_xlib_surface_same_screen (dst, src))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix,
+ &cb.tx, &cb.ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cb.dpy = dst->display->display;
+ cb.dst = dst;
+ cb.src = &src->base;
+ cb.width = src->width;
+ cb.height = src->height;
+
+ /* First check that the data is entirely within the image */
+ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_xlib_surface_get_gc (dst->display, dst, &cb.gc);
+ if (unlikely (status))
+ return status;
+
+ if (! src->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = IncludeInferiors;
+ XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv);
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb))
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! src->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = ClipByChildren;
+ XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv);
+ }
+
+ _cairo_xlib_surface_put_gc (dst->display, dst, cb.gc);
+
+ return status;
+}
+
+static cairo_status_t
+draw_boxes (cairo_composite_rectangles_t *extents,
+ cairo_boxes_t *boxes)
+{
+ cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface;
+ cairo_operator_t op = extents->op;
+ const cairo_pattern_t *src = &extents->source_pattern.base;
+ cairo_int_status_t status;
+
+ if (boxes->num_boxes == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_opaque (src, &extents->bounded))
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (dst->base.is_clear && op == CAIRO_OPERATOR_OVER)
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (op != CAIRO_OPERATOR_SOURCE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = acquire (dst);
+ if (unlikely (status))
+ return status;
+
+ if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
+ status = _cairo_xlib_core_fill_boxes
+ (dst, &((cairo_solid_pattern_t *) src)->color, boxes);
+ } else {
+ status = upload_image_inplace (dst, src, boxes);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ status = copy_boxes (dst, src, boxes);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ status = render_boxes (dst, src, boxes);
+ }
+
+ release (dst);
+
+ return status;
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_xlib_core_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_clip_is_region (extents->clip)) {
+ cairo_boxes_t boxes;
+
+ _cairo_clip_steal_boxes (extents->clip, &boxes);
+ status = draw_boxes (extents, &boxes);
+ _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xlib_core_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (extents->clip->path == NULL &&
+ _cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, extents->clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = draw_boxes (extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xlib_core_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (extents->clip->path == NULL &&
+ _cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, extents->clip);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = draw_boxes (extents, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ return status;
+}
+
+const cairo_compositor_t *
+_cairo_xlib_core_compositor_get (void)
+{
+ static cairo_compositor_t compositor;
+
+ if (compositor.delegate == NULL) {
+ compositor.delegate = _cairo_xlib_fallback_compositor_get ();
+
+ compositor.paint = _cairo_xlib_core_compositor_paint;
+ compositor.mask = NULL;
+ compositor.fill = _cairo_xlib_core_compositor_fill;
+ compositor.stroke = _cairo_xlib_core_compositor_stroke;
+ compositor.glyphs = NULL; /* XXX PolyGlyph? */
+ }
+
+ return &compositor;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
new file mode 100755
index 000000000..cc82b3a99
--- /dev/null
+++ b/src/cairo-xlib-display.c
@@ -0,0 +1,663 @@
+/* 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"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-inline.h"
+
+#include <X11/Xlibint.h> /* For XESetCloseDisplay */
+
+typedef int (*cairo_xlib_error_func_t) (Display *display,
+ XErrorEvent *event);
+
+static cairo_xlib_display_t *_cairo_xlib_display_list;
+
+static int
+_noop_error_handler (Display *display,
+ XErrorEvent *event)
+{
+ return False; /* return value is ignored */
+}
+
+static void
+_cairo_xlib_display_finish (void *abstract_display)
+{
+ cairo_xlib_display_t *display = abstract_display;
+ Display *dpy = display->display;
+
+ _cairo_xlib_display_fini_shm (display);
+
+ if (! cairo_device_acquire (&display->base)) {
+ cairo_xlib_error_func_t old_handler;
+
+ /* protect the notifies from triggering XErrors */
+ XSync (dpy, False);
+ old_handler = XSetErrorHandler (_noop_error_handler);
+
+ while (! cairo_list_is_empty (&display->fonts)) {
+ _cairo_xlib_font_close (cairo_list_first_entry (&display->fonts,
+ cairo_xlib_font_t,
+ link));
+ }
+
+ while (! cairo_list_is_empty (&display->screens)) {
+ _cairo_xlib_screen_destroy (display,
+ cairo_list_first_entry (&display->screens,
+ cairo_xlib_screen_t,
+ link));
+ }
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+
+ cairo_device_release (&display->base);
+ }
+}
+
+static void
+_cairo_xlib_display_destroy (void *abstract_display)
+{
+ cairo_xlib_display_t *display = abstract_display;
+
+ free (display);
+}
+
+static int
+_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
+{
+ cairo_xlib_display_t *display, **prev, *next;
+
+ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+ for (display = _cairo_xlib_display_list; display; display = display->next)
+ if (display->display == dpy)
+ break;
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+ if (display == NULL)
+ return 0;
+
+ cairo_device_finish (&display->base);
+
+ /*
+ * Unhook from the global list
+ */
+ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+ prev = &_cairo_xlib_display_list;
+ for (display = _cairo_xlib_display_list; display; display = next) {
+ next = display->next;
+ if (display->display == dpy) {
+ *prev = next;
+ break;
+ } else
+ prev = &display->next;
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+ if (display) {
+ display->display = NULL; /* catch any later invalid access */
+ cairo_device_destroy (&display->base);
+ }
+ /* Return value in accordance with requirements of
+ * XESetCloseDisplay */
+ return 0;
+}
+
+static const cairo_device_backend_t _cairo_xlib_device_backend = {
+ CAIRO_DEVICE_TYPE_XLIB,
+
+ NULL,
+ NULL,
+
+ NULL, /* flush */
+ _cairo_xlib_display_finish,
+ _cairo_xlib_display_destroy,
+};
+
+static void _cairo_xlib_display_select_compositor (cairo_xlib_display_t *display)
+{
+#if 0
+ if (display->render_major > 0 || display->render_minor >= 4)
+ display->compositor = _cairo_xlib_traps_compositor_get ();
+ else if (display->render_major > 0 || display->render_minor >= 0)
+ display->compositor = _cairo_xlib_mask_compositor_get ();
+ else
+ display->compositor = _cairo_xlib_core_compositor_get ();
+#else
+ display->compositor = _cairo_xlib_fallback_compositor_get ();
+#endif
+}
+
+/**
+ * _cairo_xlib_device_create:
+ * @dpy: the display to create the device for
+ *
+ * Gets the device belonging to @dpy, or creates it if it doesn't exist yet.
+ *
+ * Returns: the device belonging to @dpy
+ **/
+cairo_device_t *
+_cairo_xlib_device_create (Display *dpy)
+{
+ cairo_xlib_display_t *display;
+ cairo_xlib_display_t **prev;
+ cairo_device_t *device;
+ XExtCodes *codes;
+ const char *env;
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ /* There is an apparent deadlock between this mutex and the
+ * mutex for the display, but it's actually safe. For the
+ * app to call XCloseDisplay() while any other thread is
+ * inside this function would be an error in the logic
+ * app, and the CloseDisplay hook is the only other place we
+ * acquire this mutex.
+ */
+ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+
+ for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next)
+ {
+ if (display->display == dpy) {
+ /*
+ * MRU the list
+ */
+ if (prev != &_cairo_xlib_display_list) {
+ *prev = display->next;
+ display->next = _cairo_xlib_display_list;
+ _cairo_xlib_display_list = display;
+ }
+ device = cairo_device_reference (&display->base);
+ goto UNLOCK;
+ }
+ }
+
+ display = malloc (sizeof (cairo_xlib_display_t));
+ if (unlikely (display == NULL)) {
+ device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ goto UNLOCK;
+ }
+
+ _cairo_device_init (&display->base, &_cairo_xlib_device_backend);
+
+ display->display = dpy;
+ cairo_list_init (&display->screens);
+ cairo_list_init (&display->fonts);
+ display->closed = FALSE;
+
+ /* Xlib calls out to the extension close_display hooks in LIFO
+ * order. So we have to ensure that all extensions that we depend
+ * on in our close_display hook are properly initialized before we
+ * add our hook. For now, that means Render, so we call into its
+ * QueryVersion function to ensure it gets initialized.
+ */
+ display->render_major = display->render_minor = -1;
+ XRenderQueryVersion (dpy, &display->render_major, &display->render_minor);
+ env = getenv ("CAIRO_DEBUG");
+ if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) {
+ int max_render_major, max_render_minor;
+
+ env += sizeof ("xrender-version=") - 1;
+ if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
+ max_render_major = max_render_minor = -1;
+
+ if (max_render_major < display->render_major ||
+ (max_render_major == display->render_major &&
+ max_render_minor < display->render_minor))
+ {
+ display->render_major = max_render_major;
+ display->render_minor = max_render_minor;
+ }
+ }
+
+ _cairo_xlib_display_select_compositor (display);
+
+ display->white = NULL;
+ memset (display->alpha, 0, sizeof (display->alpha));
+ memset (display->solid, 0, sizeof (display->solid));
+ memset (display->solid_cache, 0, sizeof (display->solid_cache));
+ memset (display->last_solid_cache, 0, sizeof (display->last_solid_cache));
+
+ memset (display->cached_xrender_formats, 0,
+ sizeof (display->cached_xrender_formats));
+
+ display->force_precision = -1;
+
+ _cairo_xlib_display_init_shm (display);
+
+ /* Prior to Render 0.10, there is no protocol support for gradients and
+ * we call function stubs instead, which would silently consume the drawing.
+ */
+#if RENDER_MAJOR == 0 && RENDER_MINOR < 10
+ display->buggy_gradients = TRUE;
+#else
+ display->buggy_gradients = FALSE;
+#endif
+ display->buggy_pad_reflect = FALSE;
+ display->buggy_repeat = FALSE;
+
+ /* This buggy_repeat condition is very complicated because there
+ * are multiple X server code bases (with multiple versioning
+ * schemes within a code base), and multiple bugs.
+ *
+ * The X servers:
+ *
+ * 1. The Vendor=="XFree86" code base with release numbers such
+ * as 4.7.0 (VendorRelease==40700000).
+ *
+ * 2. The Vendor=="X.Org" code base (a descendant of the
+ * XFree86 code base). It originally had things like
+ * VendorRelease==60700000 for release 6.7.0 but then changed
+ * its versioning scheme so that, for example,
+ * VendorRelease==10400000 for the 1.4.0 X server within the
+ * X.Org 7.3 release.
+ *
+ * The bugs:
+ *
+ * 1. The original bug that led to the buggy_repeat
+ * workaround. This was a bug that Owen Taylor investigated,
+ * understood well, and characterized against 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 (_cairo_xlib_vendor_is_xorg (dpy)) {
+ if (VendorRelease (dpy) >= 60700000) {
+ if (VendorRelease (dpy) < 70000000)
+ display->buggy_repeat = TRUE;
+
+ /* We know that gradients simply do not work in early Xorg servers */
+ if (VendorRelease (dpy) < 70200000)
+ display->buggy_gradients = TRUE;
+
+ /* And the extended repeat modes were not fixed until much later */
+ display->buggy_pad_reflect = TRUE;
+ } else {
+ if (VendorRelease (dpy) < 10400000)
+ display->buggy_repeat = TRUE;
+
+ /* Too many bugs in the early drivers */
+ if (VendorRelease (dpy) < 10699000)
+ display->buggy_pad_reflect = TRUE;
+ }
+ } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
+ if (VendorRelease (dpy) <= 40500000)
+ display->buggy_repeat = TRUE;
+
+ display->buggy_gradients = TRUE;
+ display->buggy_pad_reflect = TRUE;
+ }
+
+ codes = XAddExtension (dpy);
+ if (unlikely (codes == NULL)) {
+ device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ free (display->shm);
+ free (display);
+ goto UNLOCK;
+ }
+
+ XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
+ cairo_device_reference (&display->base); /* add one for the CloseDisplay */
+
+ display->next = _cairo_xlib_display_list;
+ _cairo_xlib_display_list = display;
+
+ device = &display->base;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+ return device;
+}
+
+cairo_status_t
+_cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (device);
+ if (status)
+ return status;
+
+ *display = (cairo_xlib_display_t *) device;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format_for_pixman(cairo_xlib_display_t *display,
+ pixman_format_code_t format)
+{
+ Display *dpy = display->display;
+ XRenderPictFormat tmpl;
+ int mask;
+
+#define MASK(x) ((1<<(x))-1)
+
+ tmpl.depth = PIXMAN_FORMAT_DEPTH(format);
+ mask = PictFormatType | PictFormatDepth;
+
+ switch (PIXMAN_FORMAT_TYPE(format)) {
+ case PIXMAN_TYPE_ARGB:
+ tmpl.type = PictTypeDirect;
+
+ tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+ if (PIXMAN_FORMAT_A(format))
+ tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) +
+ PIXMAN_FORMAT_G(format) +
+ PIXMAN_FORMAT_B(format));
+
+ tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+ tmpl.direct.red = (PIXMAN_FORMAT_G(format) +
+ PIXMAN_FORMAT_B(format));
+
+ tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+ tmpl.direct.green = PIXMAN_FORMAT_B(format);
+
+ tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+ tmpl.direct.blue = 0;
+
+ mask |= PictFormatRed | PictFormatRedMask;
+ mask |= PictFormatGreen | PictFormatGreenMask;
+ mask |= PictFormatBlue | PictFormatBlueMask;
+ mask |= PictFormatAlpha | PictFormatAlphaMask;
+ break;
+
+ case PIXMAN_TYPE_ABGR:
+ tmpl.type = PictTypeDirect;
+
+ tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+ if (tmpl.direct.alphaMask)
+ tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) +
+ PIXMAN_FORMAT_G(format) +
+ PIXMAN_FORMAT_R(format));
+
+ tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+ tmpl.direct.blue = (PIXMAN_FORMAT_G(format) +
+ PIXMAN_FORMAT_R(format));
+
+ tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+ tmpl.direct.green = PIXMAN_FORMAT_R(format);
+
+ tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+ tmpl.direct.red = 0;
+
+ mask |= PictFormatRed | PictFormatRedMask;
+ mask |= PictFormatGreen | PictFormatGreenMask;
+ mask |= PictFormatBlue | PictFormatBlueMask;
+ mask |= PictFormatAlpha | PictFormatAlphaMask;
+ break;
+
+ case PIXMAN_TYPE_BGRA:
+ tmpl.type = PictTypeDirect;
+
+ tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+ tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format));
+
+ tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+ tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
+ PIXMAN_FORMAT_G(format));
+
+ tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+ tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
+ PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format));
+
+ tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+ tmpl.direct.alpha = 0;
+
+ mask |= PictFormatRed | PictFormatRedMask;
+ mask |= PictFormatGreen | PictFormatGreenMask;
+ mask |= PictFormatBlue | PictFormatBlueMask;
+ mask |= PictFormatAlpha | PictFormatAlphaMask;
+ break;
+
+ case PIXMAN_TYPE_A:
+ tmpl.type = PictTypeDirect;
+
+ tmpl.direct.alpha = 0;
+ tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+
+ mask |= PictFormatAlpha | PictFormatAlphaMask;
+ break;
+
+ case PIXMAN_TYPE_COLOR:
+ case PIXMAN_TYPE_GRAY:
+ /* XXX Find matching visual/colormap */
+ tmpl.type = PictTypeIndexed;
+ //tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid;
+ //mask |= PictFormatColormap;
+ return NULL;
+ }
+#undef MASK
+
+ /* XXX caching? */
+ return XRenderFindFormat(dpy, mask, &tmpl, 1);
+}
+
+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 = PictStandardNUM;
+
+ switch (format) {
+ case CAIRO_FORMAT_A1:
+ pict_format = PictStandardA1; break;
+ case CAIRO_FORMAT_A8:
+ pict_format = PictStandardA8; break;
+ case CAIRO_FORMAT_RGB24:
+ pict_format = PictStandardRGB24; break;
+ case CAIRO_FORMAT_RGB16_565:
+ xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
+ PIXMAN_r5g6b5);
+ break;
+ case CAIRO_FORMAT_RGB30:
+ xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
+ PIXMAN_x2r10g10b10);
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_ARGB32:
+ pict_format = PictStandardARGB32; break;
+ }
+ if (pict_format != PictStandardNUM)
+ xrender_format =
+ XRenderFindStandardFormat (display->display, pict_format);
+ display->cached_xrender_formats[format] = xrender_format;
+ }
+
+ return xrender_format;
+}
+
+cairo_xlib_screen_t *
+_cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
+ Screen *screen)
+{
+ cairo_xlib_screen_t *info;
+
+ cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) {
+ if (info->screen == screen) {
+ if (display->screens.next != &info->link)
+ cairo_list_move (&info->link, &display->screens);
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+cairo_bool_t
+_cairo_xlib_display_has_repeat (cairo_device_t *device)
+{
+ return ! ((cairo_xlib_display_t *) device)->buggy_repeat;
+}
+
+cairo_bool_t
+_cairo_xlib_display_has_reflect (cairo_device_t *device)
+{
+ return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect;
+}
+
+cairo_bool_t
+_cairo_xlib_display_has_gradients (cairo_device_t *device)
+{
+ return ! ((cairo_xlib_display_t *) device)->buggy_gradients;
+}
+
+/**
+ * cairo_xlib_device_debug_cap_xrender_version:
+ * @device: a #cairo_device_t for the Xlib backend
+ * @major_version: major version to restrict to
+ * @minor_version: minor version to restrict to
+ *
+ * Restricts all future Xlib surfaces for this devices to the specified version
+ * of the RENDER extension. This function exists solely for debugging purpose.
+ * It let's you find out how cairo would behave with an older version of
+ * the RENDER extension.
+ *
+ * Use the special values -1 and -1 for disabling the RENDER extension.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device,
+ int major_version,
+ int minor_version)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *) device;
+
+ if (device == NULL || device->status)
+ return;
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB)
+ return;
+
+ if (major_version < display->render_major ||
+ (major_version == display->render_major &&
+ minor_version < display->render_minor))
+ {
+ display->render_major = major_version;
+ display->render_minor = minor_version;
+ }
+
+ _cairo_xlib_display_select_compositor (display);
+}
+
+/**
+ * cairo_xlib_device_debug_set_precision:
+ * @device: a #cairo_device_t for the Xlib backend
+ * @precision: the precision to use
+ *
+ * Render supports two modes of precision when rendering trapezoids. Set
+ * the precision to the desired mode.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_xlib_device_debug_set_precision (cairo_device_t *device,
+ int precision)
+{
+ if (device == NULL || device->status)
+ return;
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return;
+ }
+
+ ((cairo_xlib_display_t *) device)->force_precision = precision;
+}
+
+/**
+ * cairo_xlib_device_debug_get_precision:
+ * @device: a #cairo_device_t for the Xlib backend
+ *
+ * Get the Xrender precision mode.
+ *
+ * Returns: the render precision mode
+ *
+ * Since: 1.12
+ **/
+int
+cairo_xlib_device_debug_get_precision (cairo_device_t *device)
+{
+ if (device == NULL || device->status)
+ return -1;
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return -1;
+ }
+
+ return ((cairo_xlib_display_t *) device)->force_precision;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-fallback-compositor.c b/src/cairo-xlib-fallback-compositor.c
new file mode 100755
index 000000000..ed2845db5
--- /dev/null
+++ b/src/cairo-xlib-fallback-compositor.c
@@ -0,0 +1,248 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Behdad Esfahbod <behdad@behdad.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-offset-private.h"
+
+static const cairo_compositor_t *
+_get_compositor (cairo_surface_t *surface)
+{
+ return ((cairo_image_surface_t *)surface)->compositor;
+}
+
+static cairo_bool_t
+unclipped (cairo_xlib_surface_t *xlib, cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t r;
+
+ r.x = r.y = 0;
+ r.width = xlib->width;
+ r.height = xlib->height;
+ return _cairo_clip_contains_rectangle (clip, &r);
+}
+
+static cairo_int_status_t
+_cairo_xlib_shm_compositor_paint (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
+ cairo_int_status_t status;
+ cairo_surface_t *shm;
+ cairo_bool_t overwrite;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ overwrite =
+ extents->op <= CAIRO_OPERATOR_SOURCE && unclipped (xlib, extents->clip);
+
+ shm = _cairo_xlib_surface_get_shm (xlib, overwrite);
+ if (shm == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_compositor_paint (_get_compositor (shm), shm,
+ extents->op,
+ &extents->source_pattern.base,
+ extents->clip);
+ if (unlikely (status))
+ return status;
+
+ xlib->base.is_clear =
+ extents->op == CAIRO_OPERATOR_CLEAR && unclipped (xlib, extents->clip);
+ xlib->base.serial++;
+ xlib->fallback++;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_xlib_shm_compositor_mask (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
+ cairo_int_status_t status;
+ cairo_surface_t *shm;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
+ if (shm == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_compositor_mask (_get_compositor (shm), shm,
+ extents->op,
+ &extents->source_pattern.base,
+ &extents->mask_pattern.base,
+ extents->clip);
+ if (unlikely (status))
+ return status;
+
+ xlib->base.is_clear = FALSE;
+ xlib->base.serial++;
+ xlib->fallback++;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_xlib_shm_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
+ cairo_int_status_t status;
+ cairo_surface_t *shm;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
+ if (shm == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_compositor_stroke (_get_compositor (shm), shm,
+ extents->op,
+ &extents->source_pattern.base,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ antialias,
+ extents->clip);
+ if (unlikely (status))
+ return status;
+
+ xlib->base.is_clear = FALSE;
+ xlib->base.serial++;
+ xlib->fallback++;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_xlib_shm_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
+ cairo_int_status_t status;
+ cairo_surface_t *shm;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
+ if (shm == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_compositor_fill (_get_compositor (shm), shm,
+ extents->op,
+ &extents->source_pattern.base,
+ path,
+ fill_rule, tolerance, antialias,
+ extents->clip);
+ if (unlikely (status))
+ return status;
+
+ xlib->base.is_clear = FALSE;
+ xlib->base.serial++;
+ xlib->fallback++;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_xlib_shm_compositor_glyphs (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
+ cairo_int_status_t status;
+ cairo_surface_t *shm;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
+ if (shm == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_compositor_glyphs (_get_compositor (shm), shm,
+ extents->op,
+ &extents->source_pattern.base,
+ glyphs, num_glyphs, scaled_font,
+ extents->clip);
+ if (unlikely (status))
+ return status;
+
+ xlib->base.is_clear = FALSE;
+ xlib->base.serial++;
+ xlib->fallback++;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static const cairo_compositor_t _cairo_xlib_shm_compositor = {
+ &_cairo_fallback_compositor,
+
+ _cairo_xlib_shm_compositor_paint,
+ _cairo_xlib_shm_compositor_mask,
+ _cairo_xlib_shm_compositor_stroke,
+ _cairo_xlib_shm_compositor_fill,
+ _cairo_xlib_shm_compositor_glyphs,
+};
+
+const cairo_compositor_t *
+_cairo_xlib_fallback_compositor_get (void)
+{
+ return &_cairo_xlib_shm_compositor;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
new file mode 100755
index 000000000..4fd725f93
--- /dev/null
+++ b/src/cairo-xlib-private.h
@@ -0,0 +1,468 @@
+/* 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"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-private.h"
+
+#include <pixman.h>
+#include <string.h>
+
+typedef struct _cairo_xlib_display cairo_xlib_display_t;
+typedef struct _cairo_xlib_shm_display cairo_xlib_shm_display_t;
+typedef struct _cairo_xlib_screen cairo_xlib_screen_t;
+typedef struct _cairo_xlib_source cairo_xlib_source_t;
+typedef struct _cairo_xlib_proxy cairo_xlib_proxy_t;
+typedef struct _cairo_xlib_surface cairo_xlib_surface_t;
+
+/* size of color cube */
+#define CUBE_SIZE 6
+/* size of gray ramp */
+#define RAMP_SIZE 16
+/* maximum number of cached GC's */
+#define GC_CACHE_SIZE 4
+
+struct _cairo_xlib_display {
+ cairo_device_t base;
+
+ cairo_xlib_display_t *next;
+
+ Display *display;
+ cairo_list_t screens;
+ cairo_list_t fonts;
+
+ cairo_xlib_shm_display_t *shm;
+
+ const cairo_compositor_t *compositor;
+
+ int render_major;
+ int render_minor;
+ XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1];
+
+ int force_precision;
+
+ cairo_surface_t *white;
+ cairo_surface_t *alpha[256];
+ cairo_surface_t *solid[32];
+ uint32_t solid_cache[32]; /* low 16 are opaque, high 16 transparent */
+ struct {
+ uint32_t color;
+ int index;
+ } last_solid_cache[2];
+
+ /* TRUE if the server has a bug with repeating pictures
+ *
+ * https://bugs.freedesktop.org/show_bug.cgi?id=3566
+ *
+ * We can't test for this because it depends on whether the
+ * picture is in video memory or not.
+ *
+ * We also use this variable as a guard against a second
+ * independent bug with transformed repeating pictures:
+ *
+ * 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;
+ unsigned int closed :1;
+};
+
+typedef struct _cairo_xlib_visual_info {
+ cairo_list_t link;
+ VisualID visualid;
+ struct { uint8_t a, r, g, b; } colors[256];
+ uint8_t cube_to_pseudocolor[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE];
+ uint8_t field8_to_cube[256];
+ int8_t dither8_to_cube[256];
+ uint8_t gray8_to_pseudocolor[256];
+} cairo_xlib_visual_info_t;
+
+struct _cairo_xlib_screen {
+ cairo_list_t link;
+
+ cairo_device_t *device;
+ Screen *screen;
+
+ cairo_list_t surfaces;
+
+ cairo_bool_t has_font_options;
+ cairo_font_options_t font_options;
+
+ GC gc[GC_CACHE_SIZE];
+ uint8_t gc_depths[GC_CACHE_SIZE];
+
+ cairo_list_t visuals;
+};
+
+enum {
+ GLYPHSET_INDEX_ARGB32,
+ GLYPHSET_INDEX_A8,
+ GLYPHSET_INDEX_A1,
+ NUM_GLYPHSETS
+};
+
+typedef struct _cairo_xlib_font_glyphset {
+ GlyphSet glyphset;
+ cairo_format_t format;
+ XRenderPictFormat *xrender_format;
+ struct _cairo_xlib_font_glyphset_free_glyphs {
+ int count;
+ unsigned long indices[128];
+ } to_free;
+} cairo_xlib_font_glyphset_t;
+
+typedef struct _cairo_xlib_font {
+ cairo_scaled_font_private_t base;
+ cairo_scaled_font_t *font;
+ cairo_device_t *device;
+ cairo_list_t link;
+ cairo_xlib_font_glyphset_t glyphset[NUM_GLYPHSETS];
+} cairo_xlib_font_t;
+
+struct _cairo_xlib_surface {
+ cairo_surface_t base;
+
+ Picture picture;
+ Drawable drawable;
+
+ const cairo_compositor_t *compositor;
+ cairo_surface_t *shm;
+ int fallback;
+
+ cairo_xlib_display_t *display;
+ cairo_xlib_screen_t *screen;
+ cairo_list_t link;
+
+ Display *dpy; /* only valid between acquire/release */
+ cairo_bool_t owns_pixmap;
+ Visual *visual;
+
+ int use_pixmap;
+
+ int width;
+ int height;
+ int depth;
+
+ int precision;
+ XRenderPictFormat *xrender_format;
+ /* XXX pixman_format instead of masks? */
+ uint32_t a_mask;
+ uint32_t r_mask;
+ uint32_t g_mask;
+ uint32_t b_mask;
+
+ struct _cairo_xlib_source {
+ cairo_surface_t base;
+
+ Picture picture;
+ Pixmap pixmap;
+ Display *dpy;
+
+ unsigned int filter:3;
+ unsigned int extend:3;
+ unsigned int has_matrix:1;
+ unsigned int has_component_alpha:1;
+ } embedded_source;
+};
+
+struct _cairo_xlib_proxy {
+ struct _cairo_xlib_source source;
+ cairo_surface_t *owner;
+};
+
+inline static cairo_bool_t
+_cairo_xlib_vendor_is_xorg (Display *dpy)
+{
+ const char *const vendor = ServerVendor (dpy);
+ return strstr (vendor, "X.Org") || strstr (vendor, "Xorg");
+}
+
+cairo_private cairo_status_t
+_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ GC *gc);
+
+cairo_private cairo_device_t *
+_cairo_xlib_device_create (Display *display);
+
+cairo_private void
+_cairo_xlib_display_init_shm (cairo_xlib_display_t *display);
+
+cairo_private void
+_cairo_xlib_display_fini_shm (cairo_xlib_display_t *display);
+
+cairo_private cairo_xlib_screen_t *
+_cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
+ Screen *screen);
+
+cairo_private cairo_status_t
+_cairo_xlib_display_acquire (cairo_device_t *device,
+ cairo_xlib_display_t **display);
+
+cairo_private cairo_bool_t
+_cairo_xlib_display_has_repeat (cairo_device_t *device);
+
+cairo_private cairo_bool_t
+_cairo_xlib_display_has_reflect (cairo_device_t *device);
+
+cairo_private cairo_bool_t
+_cairo_xlib_display_has_gradients (cairo_device_t *device);
+
+cairo_private void
+_cairo_xlib_display_set_precision(cairo_device_t *device,
+ int precision);
+
+cairo_private XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
+ cairo_format_t format);
+
+cairo_private XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format_for_pixman (cairo_xlib_display_t *display,
+ pixman_format_code_t format);
+
+cairo_private cairo_status_t
+_cairo_xlib_screen_get (Display *dpy,
+ Screen *screen,
+ cairo_xlib_screen_t **out);
+
+cairo_private void
+_cairo_xlib_screen_destroy (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info);
+
+cairo_private GC
+_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info,
+ int depth,
+ Drawable drawable);
+cairo_private void
+_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info,
+ int depth,
+ GC gc);
+
+cairo_private cairo_font_options_t *
+_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info);
+
+cairo_private cairo_status_t
+_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info,
+ Visual *visual,
+ cairo_xlib_visual_info_t **out);
+
+cairo_private cairo_status_t
+_cairo_xlib_visual_info_create (Display *dpy,
+ int screen,
+ VisualID visualid,
+ cairo_xlib_visual_info_t **out);
+
+cairo_private void
+_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_core_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_fallback_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_mask_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_traps_compositor_get (void);
+
+cairo_private void
+_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface);
+
+cairo_private void
+_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface,
+ cairo_antialias_t antialias);
+
+cairo_private cairo_int_status_t
+_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ cairo_surface_attributes_t *attributes,
+ double xc,
+ double yc);
+
+cairo_private cairo_status_t
+_cairo_xlib_surface_draw_image (cairo_xlib_surface_t *surface,
+ cairo_image_surface_t *image,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int dst_x,
+ int dst_y);
+
+cairo_private cairo_surface_t *
+_cairo_xlib_source_create_for_pattern (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y);
+
+cairo_private void
+_cairo_xlib_font_close (cairo_xlib_font_t *font);
+
+#define CAIRO_RENDER_AT_LEAST(surface, major, minor) \
+ (((surface)->render_major > major) || \
+ (((surface)->render_major == major) && ((surface)->render_minor >= minor)))
+
+#define CAIRO_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0)
+#define CAIRO_RENDER_HAS_COMPOSITE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0)
+#define CAIRO_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0)
+
+#define CAIRO_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 1)
+
+#define CAIRO_RENDER_HAS_DISJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2)
+#define CAIRO_RENDER_HAS_CONJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2)
+
+#define CAIRO_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_RENDER_HAS_TRIANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_RENDER_HAS_TRISTRIP(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_RENDER_HAS_TRIFAN(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+
+#define CAIRO_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6)
+#define CAIRO_RENDER_HAS_FILTERS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6)
+
+#define CAIRO_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10)
+#define CAIRO_RENDER_HAS_GRADIENTS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10)
+
+#define CAIRO_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 11)
+
+#define CAIRO_RENDER_SUPPORTS_OPERATOR(surface, op) \
+ ((op) <= CAIRO_OPERATOR_SATURATE || \
+ (CAIRO_RENDER_HAS_PDF_OPERATORS(surface) && \
+ (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY))
+
+/*
+ * Return whether two xlib surfaces share the same
+ * screen. Both core and Render drawing require this
+ * when using multiple drawables in an operation.
+ */
+static inline cairo_bool_t
+_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst,
+ cairo_xlib_surface_t *src)
+{
+ return dst->screen == src->screen;
+}
+
+cairo_private cairo_int_status_t
+_cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes);
+
+cairo_private cairo_int_status_t
+_cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst,
+ const cairo_color_t *color,
+ int num_rects,
+ cairo_rectangle_int_t *rects);
+
+static inline void
+_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ GC gc)
+{
+ _cairo_xlib_screen_put_gc (display,
+ surface->screen,
+ surface->depth,
+ gc);
+}
+
+cairo_private cairo_surface_t *
+_cairo_xlib_surface_create_similar_shm (void *surface,
+ cairo_format_t format,
+ int width, int height);
+
+cairo_private cairo_surface_t *
+_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
+ cairo_bool_t overwrite);
+
+cairo_private cairo_int_status_t
+_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface);
+
+cairo_private cairo_surface_t *
+_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
+ pixman_format_code_t format,
+ int width, int height);
+
+cairo_private cairo_surface_t *
+_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
+ pixman_format_code_t format,
+ int width, int height);
+
+cairo_private void
+_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
+ XImage *ximage);
+
+cairo_private void *
+_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface);
+
+cairo_private void
+_cairo_xlib_shm_surface_mark_active (cairo_surface_t *shm);
+
+cairo_private cairo_bool_t
+_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface);
+
+cairo_private cairo_bool_t
+_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface);
+
+cairo_private Pixmap
+_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface);
+
+cairo_private XRenderPictFormat *
+_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface);
+
+cairo_private pixman_format_code_t
+_pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface);
+
+#endif /* CAIRO_XLIB_PRIVATE_H */
diff --git a/src/cairo-xlib-render-compositor.c b/src/cairo-xlib-render-compositor.c
new file mode 100755
index 000000000..05cde708b
--- /dev/null
+++ b/src/cairo-xlib-render-compositor.c
@@ -0,0 +1,2019 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Behdad Esfahbod <behdad@behdad.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-damage-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-pattern-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-tristrip-private.h"
+
+static cairo_int_status_t
+check_composite (const cairo_composite_rectangles_t *extents)
+{
+ cairo_xlib_display_t *display = ((cairo_xlib_surface_t *)extents->surface)->display;
+
+ if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_int_status_t status;
+
+ status = _cairo_xlib_display_acquire (dst->base.device, &dst->display);
+ if (unlikely (status))
+ return status;
+
+ dst->dpy = dst->display->display;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+
+ cairo_device_release (&dst->display->base);
+ dst->dpy = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+ cairo_region_t *region)
+{
+ cairo_xlib_surface_t *surface = _surface;
+
+ _cairo_xlib_surface_ensure_picture (surface);
+
+ if (region != NULL) {
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
+ XRectangle *rects = stack_rects;
+ int n_rects, i;
+
+ n_rects = cairo_region_num_rectangles (region);
+ if (n_rects > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ for (i = 0; i < n_rects; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (region, i, &rect);
+
+ rects[i].x = rect.x;
+ rects[i].y = rect.y;
+ rects[i].width = rect.width;
+ rects[i].height = rect.height;
+ }
+ XRenderSetPictureClipRectangles (surface->dpy,
+ surface->picture,
+ 0, 0,
+ rects, n_rects);
+ if (rects != stack_rects)
+ free (rects);
+ } else {
+ XRenderPictureAttributes pa;
+ pa.clip_mask = None;
+ XRenderChangePicture (surface->dpy,
+ surface->picture,
+ CPClipMask, &pa);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+copy_image_boxes (void *_dst,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy)
+{
+ cairo_xlib_surface_t *dst = _dst;
+ struct _cairo_boxes_chunk *chunk;
+ cairo_int_status_t status;
+ Pixmap src;
+ GC gc;
+ int i, j;
+
+ assert (image->depth == dst->depth);
+
+ status = acquire (dst);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc);
+ if (unlikely (status)) {
+ release (dst);
+ return status;
+ }
+
+ src = _cairo_xlib_shm_surface_get_pixmap (&image->base);
+ if (boxes->num_boxes == 1) {
+ int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
+ int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
+ int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
+ int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
+
+ _cairo_xlib_shm_surface_mark_active (&image->base);
+ XCopyArea (dst->dpy, src, dst->drawable, gc,
+ x1 + dx, y1 + dy,
+ x2 - x1, y2 - y1,
+ x1, y1);
+ } else {
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+ XRectangle *rects = stack_rects;
+
+ if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ j = 0;
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ if (x2 > x1 && y2 > y1) {
+ rects[j].x = x1;
+ rects[j].y = y1;
+ rects[j].width = x2 - x1;
+ rects[j].height = y2 - y1;
+ j++;
+ }
+ }
+ }
+
+ XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted);
+ _cairo_xlib_shm_surface_mark_active (&image->base);
+ XCopyArea (dst->dpy, src, dst->drawable, gc,
+ 0, 0, image->width, image->height, -dx, -dy);
+ XSetClipMask (dst->dpy, gc, None);
+
+ if (rects != stack_rects)
+ free (rects);
+ }
+
+ _cairo_xlib_surface_put_gc (dst->display, dst, gc);
+ release (dst);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+boxes_cover_surface (cairo_boxes_t *boxes,
+ cairo_xlib_surface_t *surface)
+{
+ cairo_box_t *b;
+
+ if (boxes->num_boxes != 1)
+ return FALSE;
+
+ b = &boxes->chunks.base[0];
+
+ if (_cairo_fixed_integer_part (b->p1.x) > 0 ||
+ _cairo_fixed_integer_part (b->p1.y) > 0)
+ return FALSE;
+
+ if (_cairo_fixed_integer_part (b->p2.x) < surface->width ||
+ _cairo_fixed_integer_part (b->p2.y) < surface->height)
+ return FALSE;
+
+ return TRUE;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy)
+{
+ cairo_xlib_surface_t *dst = _dst;
+ struct _cairo_boxes_chunk *chunk;
+ cairo_image_surface_t *shm = NULL;
+ cairo_int_status_t status;
+ int i;
+
+ if (image->base.device == dst->base.device) {
+ if (image->depth != dst->depth)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (_cairo_xlib_shm_surface_get_pixmap (&image->base))
+ return copy_image_boxes (dst, image, boxes, dx, dy);
+
+ goto draw_image_boxes;
+ }
+
+ if (boxes_cover_surface (boxes, dst))
+ shm = (cairo_image_surface_t *) _cairo_xlib_surface_get_shm (dst, TRUE);
+ if (shm) {
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+ cairo_rectangle_int_t r;
+
+ r.x = _cairo_fixed_integer_part (b->p1.x);
+ r.y = _cairo_fixed_integer_part (b->p1.y);
+ r.width = _cairo_fixed_integer_part (b->p2.x) - r.x;
+ r.height = _cairo_fixed_integer_part (b->p2.y) - r.y;
+
+ if (shm->pixman_format != image->pixman_format ||
+ ! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data,
+ image->stride / sizeof (uint32_t),
+ shm->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (image->pixman_format),
+ PIXMAN_FORMAT_BPP (shm->pixman_format),
+ r.x + dx, r.y + dy,
+ r.x, r.y,
+ r.width, r.height))
+ {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, shm->pixman_image,
+ r.x + dx, r.y + dy,
+ 0, 0,
+ r.x, r.y,
+ r.width, r.height);
+ }
+
+ shm->base.damage =
+ _cairo_damage_add_rectangle (shm->base.damage, &r);
+ }
+ }
+ dst->base.is_clear = FALSE;
+ dst->fallback++;
+ dst->base.serial++;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ if (image->depth == dst->depth &&
+ ((cairo_xlib_display_t *)dst->display)->shm) {
+ cairo_box_t extents;
+ cairo_rectangle_int_t r;
+
+ _cairo_boxes_extents (boxes, &extents);
+ _cairo_box_round_to_rectangle (&extents, &r);
+
+ shm = (cairo_image_surface_t *)
+ _cairo_xlib_surface_create_shm (dst, image->pixman_format,
+ r.width, r.height);
+ if (shm) {
+ int tx = -r.x, ty = -r.y;
+
+ assert (shm->pixman_format == image->pixman_format);
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+
+ r.x = _cairo_fixed_integer_part (b->p1.x);
+ r.y = _cairo_fixed_integer_part (b->p1.y);
+ r.width = _cairo_fixed_integer_part (b->p2.x) - r.x;
+ r.height = _cairo_fixed_integer_part (b->p2.y) - r.y;
+
+ if (! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data,
+ image->stride / sizeof (uint32_t),
+ shm->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (image->pixman_format),
+ PIXMAN_FORMAT_BPP (shm->pixman_format),
+ r.x + dx, r.y + dy,
+ r.x + tx, r.y + ty,
+ r.width, r.height))
+ {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, shm->pixman_image,
+ r.x + dx, r.y + dy,
+ 0, 0,
+ r.x + tx, r.y + ty,
+ r.width, r.height);
+ }
+ }
+ }
+
+ dx = tx;
+ dy = ty;
+ image = shm;
+
+ if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) {
+ status = copy_image_boxes (dst, image, boxes, dx, dy);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto out;
+ }
+ }
+ }
+
+draw_image_boxes:
+ status = CAIRO_STATUS_SUCCESS;
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t *b = &chunk->base[i];
+ int x1 = _cairo_fixed_integer_part (b->p1.x);
+ int y1 = _cairo_fixed_integer_part (b->p1.y);
+ int x2 = _cairo_fixed_integer_part (b->p2.x);
+ int y2 = _cairo_fixed_integer_part (b->p2.y);
+ if (_cairo_xlib_surface_draw_image (dst, image,
+ x1 + dx, y1 + dy,
+ x2 - x1, y2 - y1,
+ x1, y1)) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto out;
+ }
+ }
+ }
+
+out:
+ cairo_surface_destroy (&shm->base);
+ return status;
+}
+
+static cairo_int_status_t
+copy_boxes (void *_dst,
+ cairo_surface_t *_src,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents,
+ int dx, int dy)
+{
+ cairo_xlib_surface_t *dst = _dst;
+ cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)_src;
+ struct _cairo_boxes_chunk *chunk;
+ cairo_int_status_t status;
+ GC gc;
+ Drawable d;
+ int i, j;
+
+ if (! _cairo_xlib_surface_same_screen (dst, src))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (dst->depth != src->depth)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = acquire (dst);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc);
+ if (unlikely (status)) {
+ release (dst);
+ return status;
+ }
+
+ if (src->fallback && src->shm->damage->dirty) {
+ assert (src != dst);
+ d = _cairo_xlib_shm_surface_get_pixmap (src->shm);
+ assert (d != 0);
+ } else {
+ if (! src->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = IncludeInferiors;
+ XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv);
+ }
+ d = src->drawable;
+ }
+
+ if (boxes->num_boxes == 1) {
+ int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
+ int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
+ int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
+ int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
+
+ XCopyArea (dst->dpy, d, dst->drawable, gc,
+ x1 + dx, y1 + dy,
+ x2 - x1, y2 - y1,
+ x1, y1);
+ } else {
+ /* We can only have a single control for subwindow_mode on the
+ * GC. If we have a Window destination, we need to set ClipByChildren,
+ * but if we have a Window source, we need IncludeInferiors. If we have
+ * both a Window destination and source, we must fallback. There is
+ * no convenient way to detect if a drawable is a Pixmap or Window,
+ * therefore we can only rely on those surfaces that we created
+ * ourselves to be Pixmaps, and treat everything else as a potential
+ * Window.
+ */
+ if (src == dst || (!src->owns_pixmap && !dst->owns_pixmap)) {
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+ XCopyArea (dst->dpy, d, dst->drawable, gc,
+ x1 + dx, y1 + dy,
+ x2 - x1, y2 - y1,
+ x1, y1);
+ }
+ }
+ } else {
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+ XRectangle *rects = stack_rects;
+
+ if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ j = 0;
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ rects[j].x = x1;
+ rects[j].y = y1;
+ rects[j].width = x2 - x1;
+ rects[j].height = y2 - y1;
+ j++;
+ }
+ }
+ assert (j == boxes->num_boxes);
+
+ XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted);
+
+ XCopyArea (dst->dpy, d, dst->drawable, gc,
+ extents->x + dx, extents->y + dy,
+ extents->width, extents->height,
+ extents->x, extents->y);
+
+ XSetClipMask (dst->dpy, gc, None);
+
+ if (rects != stack_rects)
+ free (rects);
+ }
+ }
+
+ if (src->fallback && src->shm->damage->dirty) {
+ _cairo_xlib_shm_surface_mark_active (src->shm);
+ } else if (! src->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = ClipByChildren;
+ XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv);
+ }
+
+ _cairo_xlib_surface_put_gc (dst->display, dst, gc);
+ release (dst);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+_render_operator (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return PictOpClear;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return PictOpSrc;
+ case CAIRO_OPERATOR_OVER:
+ return PictOpOver;
+ case CAIRO_OPERATOR_IN:
+ return PictOpIn;
+ case CAIRO_OPERATOR_OUT:
+ return PictOpOut;
+ case CAIRO_OPERATOR_ATOP:
+ return PictOpAtop;
+
+ case CAIRO_OPERATOR_DEST:
+ return PictOpDst;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return PictOpOverReverse;
+ case CAIRO_OPERATOR_DEST_IN:
+ return PictOpInReverse;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return PictOpOutReverse;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return PictOpAtopReverse;
+
+ case CAIRO_OPERATOR_XOR:
+ return PictOpXor;
+ case CAIRO_OPERATOR_ADD:
+ return PictOpAdd;
+ case CAIRO_OPERATOR_SATURATE:
+ return PictOpSaturate;
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ return PictOpMultiply;
+ case CAIRO_OPERATOR_SCREEN:
+ return PictOpScreen;
+ case CAIRO_OPERATOR_OVERLAY:
+ return PictOpOverlay;
+ case CAIRO_OPERATOR_DARKEN:
+ return PictOpDarken;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return PictOpLighten;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return PictOpColorDodge;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return PictOpColorBurn;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return PictOpHardLight;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return PictOpSoftLight;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return PictOpDifference;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return PictOpExclusion;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return PictOpHSLHue;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return PictOpHSLSaturation;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return PictOpHSLColor;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return PictOpHSLLuminosity;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return PictOpOver;
+ }
+}
+
+static cairo_bool_t
+fill_reduces_to_source (cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_xlib_surface_t *dst)
+{
+ if (dst->base.is_clear || CAIRO_COLOR_IS_OPAQUE (color)) {
+ if (op == CAIRO_OPERATOR_OVER)
+ return TRUE;
+ if (op == CAIRO_OPERATOR_ADD)
+ return (dst->base.content & CAIRO_CONTENT_COLOR) == 0;
+ }
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_xlib_surface_t *dst = abstract_surface;
+ XRenderColor render_color;
+ int i;
+
+ //X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
+
+ if (fill_reduces_to_source (op, color, dst))
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) {
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (op == CAIRO_OPERATOR_SOURCE)
+ status = _cairo_xlib_core_fill_rectangles (dst, color, num_rects, rects);
+ return status;
+ }
+
+ render_color.red = color->red_short;
+ render_color.green = color->green_short;
+ render_color.blue = color->blue_short;
+ render_color.alpha = color->alpha_short;
+
+ _cairo_xlib_surface_ensure_picture (dst);
+ if (num_rects == 1) {
+ /* Take advantage of the protocol compaction that libXrender performs
+ * to amalgamate sequences of XRenderFillRectangle().
+ */
+ XRenderFillRectangle (dst->dpy,
+ _render_operator (op),
+ dst->picture,
+ &render_color,
+ rects->x, rects->y,
+ rects->width, rects->height);
+ } else {
+ XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+ XRectangle *xrects = stack_xrects;
+
+ if (num_rects > ARRAY_LENGTH (stack_xrects)) {
+ xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle));
+ if (unlikely (xrects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < num_rects; i++) {
+ xrects[i].x = rects[i].x;
+ xrects[i].y = rects[i].y;
+ xrects[i].width = rects[i].width;
+ xrects[i].height = rects[i].height;
+ }
+
+ XRenderFillRectangles (dst->dpy,
+ _render_operator (op),
+ dst->picture,
+ &render_color, xrects, num_rects);
+
+ if (xrects != stack_xrects)
+ free (xrects);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fill_boxes (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ cairo_xlib_surface_t *dst = abstract_surface;
+ XRenderColor render_color;
+
+ if (fill_reduces_to_source (op, color, dst))
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) {
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (op == CAIRO_OPERATOR_SOURCE)
+ status = _cairo_xlib_core_fill_boxes (dst, color, boxes);
+ return status;
+ }
+
+ render_color.red = color->red_short;
+ render_color.green = color->green_short;
+ render_color.blue = color->blue_short;
+ render_color.alpha = color->alpha_short;
+
+ _cairo_xlib_surface_ensure_picture (dst);
+ if (boxes->num_boxes == 1) {
+ int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
+ int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
+ int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
+ int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
+
+ /* Take advantage of the protocol compaction that libXrender performs
+ * to amalgamate sequences of XRenderFillRectangle().
+ */
+ XRenderFillRectangle (dst->dpy,
+ _render_operator (op),
+ dst->picture,
+ &render_color,
+ x1, y1,
+ x2 - x1, y2 - y1);
+ } else {
+ XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+ XRectangle *xrects = stack_xrects;
+ struct _cairo_boxes_chunk *chunk;
+ int i, j;
+
+ if (boxes->num_boxes > ARRAY_LENGTH (stack_xrects)) {
+ xrects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
+ if (unlikely (xrects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ j = 0;
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ xrects[j].x = x1;
+ xrects[j].y = y1;
+ xrects[j].width = x2 - x1;
+ xrects[j].height = y2 - y1;
+ j++;
+ }
+ }
+
+ XRenderFillRectangles (dst->dpy,
+ _render_operator (op),
+ dst->picture,
+ &render_color, xrects, j);
+
+ if (xrects != stack_xrects)
+ free (xrects);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0
+check_composite ()
+ operation = _categorize_composite_operation (dst, op, src_pattern,
+ mask_pattern != NULL);
+ if (operation == DO_UNSUPPORTED)
+ return UNSUPPORTED ("unsupported operation");
+
+ //X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable));
+
+ operation = _recategorize_composite_operation (dst, op, src, &src_attr,
+ mask_pattern != NULL);
+ if (operation == DO_UNSUPPORTED) {
+ status = UNSUPPORTED ("unsupported operation");
+ goto BAIL;
+ }
+#endif
+
+static cairo_int_status_t
+composite (void *abstract_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
+
+ op = _render_operator (op);
+
+ _cairo_xlib_surface_ensure_picture (dst);
+ if (abstract_mask) {
+ cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
+
+ XRenderComposite (dst->dpy, op,
+ src->picture, mask->picture, dst->picture,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ } else {
+ XRenderComposite (dst->dpy, op,
+ src->picture, 0, dst->picture,
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp (void *abstract_dst,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
+ cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
+
+ _cairo_xlib_surface_ensure_picture (dst);
+ XRenderComposite (dst->dpy, PictOpOutReverse,
+ mask->picture, None, dst->picture,
+ mask_x, mask_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+ XRenderComposite (dst->dpy, PictOpAdd,
+ src->picture, mask->picture, dst->picture,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void *abstract_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+ Picture src = ((cairo_xlib_source_t *)abstract_src)->picture;
+ Picture mask = abstract_mask ? ((cairo_xlib_source_t *)abstract_mask)->picture : 0;
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+ XRectangle *rects = stack_rects;
+ struct _cairo_boxes_chunk *chunk;
+ int i, j;
+
+ op = _render_operator (op);
+ _cairo_xlib_surface_ensure_picture (dst);
+ if (boxes->num_boxes == 1) {
+ int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
+ int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
+ int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
+ int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
+
+ XRenderComposite (dst->dpy, op,
+ src, mask, dst->picture,
+ x1 + src_x, y1 + src_y,
+ x1 + mask_x, y1 + mask_y,
+ x1 - dst_x, y1 - dst_y,
+ x2 - x1, y2 - y1);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ j = 0;
+ for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ rects[j].x = x1 - dst_x;
+ rects[j].y = y1 - dst_y;
+ rects[j].width = x2 - x1;
+ rects[j].height = y2 - y1;
+ j++;
+ }
+ }
+ assert (j == boxes->num_boxes);
+
+ XRenderSetPictureClipRectangles (dst->dpy,
+ dst->picture,
+ 0, 0,
+ rects, j);
+ if (rects != stack_rects)
+ free (rects);
+
+ XRenderComposite (dst->dpy, op,
+ src, mask, dst->picture,
+ extents->x + src_x, extents->y + src_y,
+ extents->x + mask_x, extents->y + mask_y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ set_clip_region (dst, NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* font rendering */
+
+void
+_cairo_xlib_font_close (cairo_xlib_font_t *priv)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *)priv->base.key;
+ int i;
+
+ /* XXX All I really want is to do is zap my glyphs... */
+ _cairo_scaled_font_reset_cache (priv->font);
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_t *info;
+
+ info = &priv->glyphset[i];
+ if (info->glyphset)
+ XRenderFreeGlyphSet (display->display, info->glyphset);
+ }
+
+ /* XXX locking */
+ cairo_list_del (&priv->link);
+ cairo_list_del (&priv->base.link);
+ free (priv);
+}
+
+static void
+_cairo_xlib_font_fini (cairo_scaled_font_private_t *abstract_private,
+ cairo_scaled_font_t *font)
+{
+ cairo_xlib_font_t *priv = (cairo_xlib_font_t *) abstract_private;
+ cairo_status_t status;
+ cairo_xlib_display_t *display;
+ int i;
+
+ cairo_list_del (&priv->base.link);
+ cairo_list_del (&priv->link);
+
+ status = _cairo_xlib_display_acquire (priv->device, &display);
+ if (status)
+ goto BAIL;
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_t *info;
+
+ info = &priv->glyphset[i];
+ if (info->glyphset)
+ XRenderFreeGlyphSet (display->display, info->glyphset);
+ }
+
+ cairo_device_release (&display->base);
+BAIL:
+ cairo_device_destroy (&display->base);
+ free (priv);
+}
+
+static cairo_xlib_font_t *
+_cairo_xlib_font_create (cairo_xlib_display_t *display,
+ cairo_scaled_font_t *font)
+{
+ cairo_xlib_font_t *priv;
+ int i;
+
+ priv = malloc (sizeof (cairo_xlib_font_t));
+ if (unlikely (priv == NULL))
+ return NULL;
+
+ _cairo_scaled_font_attach_private (font, &priv->base, display,
+ _cairo_xlib_font_fini);
+
+ priv->device = cairo_device_reference (&display->base);
+ priv->font = font;
+ cairo_list_add (&priv->link, &display->fonts);
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_t *info = &priv->glyphset[i];
+ switch (i) {
+ case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break;
+ case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break;
+ case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break;
+ default: ASSERT_NOT_REACHED; break;
+ }
+ info->xrender_format = NULL;
+ info->glyphset = None;
+ info->to_free.count = 0;
+ }
+
+ return priv;
+}
+
+static int
+_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format)
+{
+ if (format == CAIRO_FORMAT_A8)
+ return GLYPHSET_INDEX_A8;
+ if (format == CAIRO_FORMAT_A1)
+ return GLYPHSET_INDEX_A1;
+
+ assert (format == CAIRO_FORMAT_ARGB32);
+ return GLYPHSET_INDEX_ARGB32;
+}
+
+static inline cairo_xlib_font_t *
+_cairo_xlib_font_get (const cairo_xlib_display_t *display,
+ cairo_scaled_font_t *font)
+{
+ return (cairo_xlib_font_t *)_cairo_scaled_font_find_private (font, display);
+}
+
+typedef struct {
+ cairo_scaled_glyph_private_t base;
+
+
+ cairo_xlib_font_glyphset_t *glyphset;
+} cairo_xlib_glyph_private_t;
+
+static void
+_cairo_xlib_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
+ cairo_scaled_glyph_t *glyph,
+ cairo_scaled_font_t *font)
+{
+ cairo_xlib_glyph_private_t *priv = (cairo_xlib_glyph_private_t *)glyph_private;
+
+ if (! font->finished) {
+ cairo_xlib_font_t *font_private;
+ struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
+ cairo_xlib_font_glyphset_t *info;
+
+ font_private = _cairo_xlib_font_get (glyph_private->key, font);
+ assert (font_private);
+
+ info = priv->glyphset;
+ to_free = &info->to_free;
+ if (to_free->count == ARRAY_LENGTH (to_free->indices)) {
+ cairo_xlib_display_t *display;
+
+ if (_cairo_xlib_display_acquire (font_private->device,
+ &display) == CAIRO_STATUS_SUCCESS) {
+ XRenderFreeGlyphs (display->display,
+ info->glyphset,
+ to_free->indices,
+ to_free->count);
+ cairo_device_release (&display->base);
+ }
+
+ to_free->count = 0;
+ }
+
+ to_free->indices[to_free->count++] =
+ _cairo_scaled_glyph_index (glyph);
+ }
+
+ cairo_list_del (&glyph_private->link);
+ free (glyph_private);
+}
+
+static cairo_status_t
+_cairo_xlib_glyph_attach (cairo_xlib_display_t *display,
+ cairo_scaled_glyph_t *glyph,
+ cairo_xlib_font_glyphset_t *info)
+{
+ cairo_xlib_glyph_private_t *priv;
+
+ priv = malloc (sizeof (*priv));
+ if (unlikely (priv == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_scaled_glyph_attach_private (glyph, &priv->base, display,
+ _cairo_xlib_glyph_fini);
+ priv->glyphset = info;
+
+ glyph->dev_private = info;
+ glyph->dev_private_key = display;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_xlib_font_glyphset_t *
+_cairo_xlib_font_get_glyphset_info_for_format (cairo_xlib_display_t *display,
+ cairo_scaled_font_t *font,
+ cairo_format_t format)
+{
+ cairo_xlib_font_t *priv;
+ cairo_xlib_font_glyphset_t *info;
+ int glyphset_index;
+
+ glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format);
+
+ priv = _cairo_xlib_font_get (display, font);
+ if (priv == NULL) {
+ priv = _cairo_xlib_font_create (display, font);
+ if (priv == NULL)
+ return NULL;
+ }
+
+ info = &priv->glyphset[glyphset_index];
+ if (info->glyphset == None) {
+ info->xrender_format =
+ _cairo_xlib_display_get_xrender_format (display, info->format);
+ if (info->xrender_format == NULL)
+ return NULL;
+
+ info->glyphset = XRenderCreateGlyphSet (display->display,
+ info->xrender_format);
+ }
+
+ return info;
+}
+
+static cairo_bool_t
+has_pending_free_glyph (cairo_xlib_font_glyphset_t *info,
+ unsigned long glyph_index)
+{
+ struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
+ int i;
+
+ to_free = &info->to_free;
+ for (i = 0; i < to_free->count; i++) {
+ if (to_free->indices[i] == glyph_index) {
+ to_free->count--;
+ memmove (&to_free->indices[i],
+ &to_free->indices[i+1],
+ (to_free->count - i) * sizeof (to_free->indices[0]));
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static cairo_xlib_font_glyphset_t *
+find_pending_free_glyph (cairo_xlib_display_t *display,
+ cairo_scaled_font_t *font,
+ unsigned long glyph_index,
+ cairo_image_surface_t *surface)
+{
+ cairo_xlib_font_t *priv;
+ int i;
+
+ priv = _cairo_xlib_font_get (display, font);
+ if (priv == NULL)
+ return NULL;
+
+ if (surface != NULL) {
+ i = _cairo_xlib_get_glyphset_index_for_format (surface->format);
+ if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
+ return &priv->glyphset[i];
+ } else {
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
+ return &priv->glyphset[i];
+ }
+ }
+
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
+ cairo_scaled_font_t *font,
+ cairo_scaled_glyph_t **pscaled_glyph)
+{
+ XGlyphInfo glyph_info;
+ unsigned long glyph_index;
+ unsigned char *data;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_scaled_glyph_t *glyph = *pscaled_glyph;
+ cairo_image_surface_t *glyph_surface = glyph->surface;
+ cairo_bool_t already_had_glyph_surface;
+ cairo_xlib_font_glyphset_t *info;
+
+ glyph_index = _cairo_scaled_glyph_index (glyph);
+
+ /* check to see if we have a pending XRenderFreeGlyph for this glyph */
+ info = find_pending_free_glyph (display, font, glyph_index, glyph_surface);
+ if (info != NULL)
+ return _cairo_xlib_glyph_attach (display, glyph, info);
+
+ if (glyph_surface == NULL) {
+ status = _cairo_scaled_glyph_lookup (font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS |
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ pscaled_glyph);
+ if (unlikely (status))
+ return status;
+
+ glyph = *pscaled_glyph;
+ glyph_surface = glyph->surface;
+ already_had_glyph_surface = FALSE;
+ } else {
+ already_had_glyph_surface = TRUE;
+ }
+
+ info = _cairo_xlib_font_get_glyphset_info_for_format (display, font,
+ glyph_surface->format);
+ if (info == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ goto BAIL;
+ }
+
+#if 0
+ /* If the glyph surface has zero height or width, we create
+ * a clear 1x1 surface, to avoid various X server bugs.
+ */
+ if (glyph_surface->width == 0 || glyph_surface->height == 0) {
+ cairo_surface_t *tmp_surface;
+
+ tmp_surface = cairo_image_surface_create (info->format, 1, 1);
+ status = tmp_surface->status;
+ if (unlikely (status))
+ goto BAIL;
+
+ tmp_surface->device_transform = glyph_surface->base.device_transform;
+ tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+ glyph_surface = (cairo_image_surface_t *) tmp_surface;
+ }
+#endif
+
+ /* If the glyph format does not match the font format, then we
+ * create a temporary surface for the glyph image with the font's
+ * format.
+ */
+ if (glyph_surface->format != info->format) {
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *tmp_surface;
+
+ tmp_surface = cairo_image_surface_create (info->format,
+ glyph_surface->width,
+ glyph_surface->height);
+ status = tmp_surface->status;
+ if (unlikely (status)) {
+ cairo_surface_destroy (tmp_surface);
+ goto BAIL;
+ }
+
+ tmp_surface->device_transform = glyph_surface->base.device_transform;
+ tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+ _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base);
+ status = _cairo_surface_paint (tmp_surface,
+ CAIRO_OPERATOR_SOURCE, &pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ glyph_surface = (cairo_image_surface_t *) tmp_surface;
+
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */
+ glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0);
+ glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0);
+ glyph_info.width = glyph_surface->width;
+ glyph_info.height = glyph_surface->height;
+ glyph_info.xOff = glyph->x_advance;
+ glyph_info.yOff = glyph->y_advance;
+
+ data = glyph_surface->data;
+
+ /* flip formats around */
+ switch (_cairo_xlib_get_glyphset_index_for_format (glyph->surface->format)) {
+ case GLYPHSET_INDEX_A1:
+ /* local bitmaps are always stored with bit == byte */
+ if (_cairo_is_little_endian() != (BitmapBitOrder (display->display) == LSBFirst)) {
+ int c = glyph_surface->stride * glyph_surface->height;
+ unsigned char *d;
+ unsigned char *new, *n;
+
+ 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 (_cairo_is_little_endian() != (ImageByteOrder (display->display) == LSBFirst)) {
+ unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
+ const uint32_t *d;
+ uint32_t *new, *n;
+
+ 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 */
+
+ XRenderAddGlyphs (display->display, info->glyphset,
+ &glyph_index, &glyph_info, 1,
+ (char *) data,
+ glyph_surface->stride * glyph_surface->height);
+
+ if (data != glyph_surface->data)
+ free (data);
+
+ status = _cairo_xlib_glyph_attach (display, glyph, info);
+
+ BAIL:
+ if (glyph_surface != glyph->surface)
+ cairo_surface_destroy (&glyph_surface->base);
+
+ /* if the scaled glyph didn't already have a surface attached
+ * to it, release the created surface now that we have it
+ * uploaded to the X server. If the surface has already been
+ * there (eg. because image backend requested it), leave it in
+ * the cache
+ */
+ if (!already_had_glyph_surface)
+ _cairo_scaled_glyph_set_surface (glyph, font, NULL);
+
+ return status;
+}
+
+typedef void (*cairo_xrender_composite_text_func_t)
+ (Display *dpy,
+ int op,
+ Picture src,
+ Picture dst,
+ _Xconst XRenderPictFormat *maskFormat,
+ int xSrc,
+ int ySrc,
+ int xDst,
+ int yDst,
+ _Xconst XGlyphElt8 *elts,
+ int nelt);
+
+/* Build a struct of the same size of #cairo_glyph_t that can be used both as
+ * an input glyph with double coordinates, and as "working" glyph with
+ * integer from-current-point offsets. */
+typedef union {
+ cairo_glyph_t d;
+ unsigned long index;
+ struct {
+ unsigned long index;
+ int x;
+ int y;
+ } i;
+} cairo_xlib_glyph_t;
+
+/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */
+COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t));
+
+/* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs
+ * (Xrender limits each element to 252 glyphs, we limit them to 128)
+ *
+ * These same conditions need to be mirrored between
+ * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks
+ */
+#define _start_new_glyph_elt(count, glyph) \
+ (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
+
+static cairo_status_t
+_emit_glyphs_chunk (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *dst,
+ int dst_x, int dst_y,
+ cairo_xlib_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *font,
+ cairo_bool_t use_mask,
+ cairo_operator_t op,
+ cairo_xlib_source_t *src,
+ int src_x, int src_y,
+ /* info for this chunk */
+ int num_elts,
+ int width,
+ cairo_xlib_font_glyphset_t *info)
+{
+ /* Which XRenderCompositeText function to use */
+ cairo_xrender_composite_text_func_t composite_text_func;
+ int size;
+
+ /* Element buffer stuff */
+ XGlyphElt8 *elts;
+ XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)];
+
+ /* Reuse the input glyph array for output char generation */
+ char *char8 = (char *) glyphs;
+ unsigned short *char16 = (unsigned short *) glyphs;
+ unsigned int *char32 = (unsigned int *) glyphs;
+
+ int i;
+ int nelt; /* Element index */
+ int n; /* Num output glyphs in current element */
+ int j; /* Num output glyphs so far */
+
+ switch (width) {
+ case 1:
+ /* don't cast the 8-variant, to catch possible mismatches */
+ composite_text_func = XRenderCompositeText8;
+ size = sizeof (char);
+ break;
+ case 2:
+ composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16;
+ size = sizeof (unsigned short);
+ break;
+ default:
+ case 4:
+ composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32;
+ size = sizeof (unsigned int);
+ }
+
+ /* Allocate element array */
+ if (num_elts <= ARRAY_LENGTH (stack_elts)) {
+ elts = stack_elts;
+ } else {
+ elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8));
+ if (unlikely (elts == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* Fill them in */
+ nelt = 0;
+ n = 0;
+ j = 0;
+ for (i = 0; i < num_glyphs; i++) {
+ /* Start a new element for first output glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs.
+ *
+ * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs()
+ */
+ if (_start_new_glyph_elt (j, &glyphs[i])) {
+ if (j) {
+ elts[nelt].nchars = n;
+ nelt++;
+ n = 0;
+ }
+ elts[nelt].chars = char8 + size * j;
+ elts[nelt].glyphset = info->glyphset;
+ elts[nelt].xOff = glyphs[i].i.x - dst_x;
+ elts[nelt].yOff = glyphs[i].i.y - dst_y;
+ }
+
+ switch (width) {
+ case 1: char8 [j] = (char) glyphs[i].index; break;
+ case 2: char16[j] = (unsigned short) glyphs[i].index; break;
+ default:
+ case 4: char32[j] = (unsigned int) glyphs[i].index; break;
+ }
+
+ n++;
+ j++;
+ }
+
+ if (n) {
+ elts[nelt].nchars = n;
+ nelt++;
+ }
+
+ /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the
+ * expected number of xGlyphElts. */
+ assert (nelt == num_elts);
+
+ composite_text_func (display->display, op,
+ src->picture,
+ dst->picture,
+ use_mask ? info->xrender_format : NULL,
+ src_x + elts[0].xOff + dst_x,
+ src_y + elts[0].yOff + dst_y,
+ elts[0].xOff, elts[0].yOff,
+ (XGlyphElt8 *) elts, nelt);
+
+ if (elts != stack_elts)
+ free (elts);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs)
+{
+ cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface;
+ cairo_xlib_display_t *display = dst->display;
+ int max_request_size, size;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* The glyph coordinates must be representable in an int16_t.
+ * When possible, they will be expressed as an offset from the
+ * previous glyph, otherwise they will be an offset from the
+ * surface origin. If we can't guarantee this to be possible,
+ * fallback.
+ */
+ if (extents->bounded.x + extents->bounded.width > INT16_MAX ||
+ extents->bounded.y + extents->bounded.height> INT16_MAX ||
+ extents->bounded.x < INT16_MIN ||
+ extents->bounded.y < INT16_MIN)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* Approximate the size of the largest glyph and fallback if we can not
+ * upload it to the xserver.
+ */
+ size = ceil (font->max_scale);
+ size = 4 * size * size;
+ max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display)
+ : XMaxRequestSize (display->display)) * 4 -
+ sz_xRenderAddGlyphsReq -
+ sz_xGlyphInfo -
+ 8;
+ if (size >= max_request_size)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
+ * enough room for padding */
+#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4)
+
+static cairo_int_status_t
+composite_glyphs (void *surface,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info)
+{
+ cairo_xlib_surface_t *dst = surface;
+ cairo_xlib_glyph_t *glyphs = (cairo_xlib_glyph_t *)info->glyphs;
+ cairo_xlib_source_t *src = (cairo_xlib_source_t *)_src;
+ cairo_xlib_display_t *display = dst->display;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+ cairo_scaled_glyph_t *glyph;
+ cairo_fixed_t x = 0, y = 0;
+ cairo_xlib_font_glyphset_t *glyphset = NULL, *this_glyphset_info;
+
+ unsigned long max_index = 0;
+ int width = 1;
+ int num_elts = 0;
+ int num_out_glyphs = 0;
+ int num_glyphs = info->num_glyphs;
+
+ int max_request_size = XMaxRequestSize (display->display) * 4
+ - MAX (sz_xRenderCompositeGlyphs8Req,
+ MAX(sz_xRenderCompositeGlyphs16Req,
+ sz_xRenderCompositeGlyphs32Req));
+ int request_size = 0;
+ int i;
+
+ op = _render_operator (op),
+ _cairo_xlib_surface_ensure_picture (dst);
+
+#if CAIRO_HAS_TG_SURFACE
+ _cairo_scaled_font_freeze_cache(info->font);
+#endif
+
+ for (i = 0; i < num_glyphs; i++) {
+ int this_x, this_y;
+ int old_width;
+
+ status = _cairo_scaled_glyph_lookup (info->font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &glyph);
+ if (unlikely (status))
+ goto done;
+
+ this_x = _cairo_lround (glyphs[i].d.x);
+ this_y = _cairo_lround (glyphs[i].d.y);
+
+ /* Send unsent glyphs to the server */
+ if (glyph->dev_private_key != display) {
+ status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph);
+ if (unlikely (status))
+ goto done;
+ }
+
+ this_glyphset_info = glyph->dev_private;
+ if (!glyphset)
+ glyphset = this_glyphset_info;
+
+ /* The invariant here is that we can always flush the glyphs
+ * accumulated before this one, using old_width, and they
+ * would fit in the request.
+ */
+ old_width = width;
+
+ /* Update max glyph index */
+ if (glyphs[i].index > max_index) {
+ max_index = glyphs[i].index;
+ if (max_index >= 65536)
+ width = 4;
+ else if (max_index >= 256)
+ width = 2;
+ if (width != old_width)
+ request_size += (width - old_width) * num_out_glyphs;
+ }
+
+ /* If we will pass the max request size by adding this glyph,
+ * flush current glyphs. Note that we account for a
+ * possible element being added below.
+ *
+ * Also flush if changing glyphsets, as Xrender limits one mask
+ * format per request, so we can either break up, or use a
+ * wide-enough mask format. We do the former. One reason to
+ * prefer the latter is the fact that Xserver ADDs all glyphs
+ * to the mask first, and then composes that to final surface,
+ * though it's not a big deal.
+ *
+ * If the glyph has a coordinate which cannot be represented
+ * as a 16-bit offset from the previous glyph, flush the
+ * current chunk. The current glyph will be the first one in
+ * the next chunk, thus its coordinates will be an offset from
+ * the destination origin. This offset is guaranteed to be
+ * representable as 16-bit offset (otherwise we would have
+ * fallen back).
+ */
+ if (request_size + width > max_request_size - _cairo_sz_xGlyphElt ||
+ this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
+ this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
+ (this_glyphset_info != glyphset)) {
+ status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
+ glyphs, i, info->font, info->use_mask,
+ op, src, src_x, src_y,
+ num_elts, old_width, glyphset);
+ if (unlikely (status))
+ goto done;
+
+ glyphs += i;
+ num_glyphs -= i;
+ i = 0;
+ max_index = glyphs[i].index;
+ width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
+ request_size = 0;
+ num_elts = 0;
+ num_out_glyphs = 0;
+ x = y = 0;
+ glyphset = this_glyphset_info;
+ }
+
+ /* Convert absolute glyph position to relative-to-current-point
+ * position */
+ glyphs[i].i.x = this_x - x;
+ glyphs[i].i.y = this_y - y;
+
+ /* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs.
+ *
+ * These same conditions are mirrored in _emit_glyphs_chunk().
+ */
+ if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) {
+ num_elts++;
+ request_size += _cairo_sz_xGlyphElt;
+ }
+
+ /* adjust current-position */
+ x = this_x + glyph->x_advance;
+ y = this_y + glyph->y_advance;
+
+ num_out_glyphs++;
+ request_size += width;
+ }
+
+ if (num_elts) {
+ status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
+ glyphs, i, info->font, info->use_mask,
+ op, src, src_x, src_y,
+ num_elts, width, glyphset);
+ }
+
+done:
+#if CAIRO_HAS_TG_SURFACE
+ _cairo_scaled_font_thaw_cache(info->font);
+#endif
+
+ return status;
+}
+
+const cairo_compositor_t *
+_cairo_xlib_mask_compositor_get (void)
+{
+ static cairo_mask_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_mask_compositor_init (&compositor,
+ _cairo_xlib_fallback_compositor_get ());
+
+ compositor.acquire = acquire;
+ compositor.release = release;
+ compositor.set_clip_region = set_clip_region;
+ compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
+ compositor.draw_image_boxes = draw_image_boxes;
+ compositor.fill_rectangles = fill_rectangles;
+ compositor.fill_boxes = fill_boxes;
+ compositor.copy_boxes = copy_boxes;
+ compositor.check_composite = check_composite;
+ compositor.composite = composite;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ compositor.check_composite_glyphs = check_composite_glyphs;
+ compositor.composite_glyphs = composite_glyphs;
+ }
+
+ return &compositor.base;
+}
+
+#define CAIRO_FIXED_16_16_MIN -32768
+#define CAIRO_FIXED_16_16_MAX 32767
+
+static cairo_bool_t
+line_exceeds_16_16 (const cairo_line_t *line)
+{
+ return
+ line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+ line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+ line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+ line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX);
+}
+
+static void
+project_line_x_onto_16_16 (const cairo_line_t *line,
+ cairo_fixed_t top,
+ cairo_fixed_t bottom,
+ XLineFixed *out)
+{
+ cairo_point_double_t p1, p2;
+ double m;
+
+ p1.x = _cairo_fixed_to_double (line->p1.x);
+ p1.y = _cairo_fixed_to_double (line->p1.y);
+
+ p2.x = _cairo_fixed_to_double (line->p2.x);
+ p2.y = _cairo_fixed_to_double (line->p2.y);
+
+ m = (p2.x - p1.x) / (p2.y - p1.y);
+ out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+ out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+#if 0
+static cairo_int_status_T
+check_composite_trapezoids ()
+{
+ operation = _categorize_composite_operation (dst, op, pattern, TRUE);
+ if (operation == DO_UNSUPPORTED)
+ return UNSUPPORTED ("unsupported operation");
+
+ operation = _recategorize_composite_operation (dst, op, src,
+ &attributes, TRUE);
+ if (operation == DO_UNSUPPORTED) {
+ status = UNSUPPORTED ("unsupported operation");
+ goto BAIL;
+ }
+
+}
+#endif
+
+static cairo_int_status_t
+composite_traps (void *abstract_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_xlib_display_t *display = dst->display;
+ cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
+ XRenderPictFormat *pict_format;
+ XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)];
+ XTrapezoid *xtraps = xtraps_stack;
+ int dx, dy;
+ int i;
+
+ //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
+
+ if (dst->base.is_clear &&
+ (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))
+ {
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ pict_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
+
+ if (traps->num_traps > ARRAY_LENGTH (xtraps_stack)) {
+ xtraps = _cairo_malloc_ab (traps->num_traps, sizeof (XTrapezoid));
+ if (unlikely (xtraps == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ dx = -dst_x << 16;
+ dy = -dst_y << 16;
+ for (i = 0; i < traps->num_traps; i++) {
+ cairo_trapezoid_t *t = &traps->traps[i];
+
+ /* top/bottom will be clamped to surface bounds */
+ xtraps[i].top = _cairo_fixed_to_16_16(t->top) + dy;
+ xtraps[i].bottom = _cairo_fixed_to_16_16(t->bottom) + dy;
+
+ /* However, all the other coordinates will have been left untouched so
+ * as not to introduce numerical error. Recompute them if they
+ * exceed the 16.16 limits.
+ */
+ if (unlikely (line_exceeds_16_16 (&t->left))) {
+ project_line_x_onto_16_16 (&t->left, t->top, t->bottom,
+ &xtraps[i].left);
+ xtraps[i].left.p1.x += dx;
+ xtraps[i].left.p2.x += dx;
+ xtraps[i].left.p1.y = xtraps[i].top;
+ xtraps[i].left.p2.y = xtraps[i].bottom;
+ } else {
+ xtraps[i].left.p1.x = _cairo_fixed_to_16_16(t->left.p1.x) + dx;
+ xtraps[i].left.p1.y = _cairo_fixed_to_16_16(t->left.p1.y) + dy;
+ xtraps[i].left.p2.x = _cairo_fixed_to_16_16(t->left.p2.x) + dx;
+ xtraps[i].left.p2.y = _cairo_fixed_to_16_16(t->left.p2.y) + dy;
+ }
+
+ if (unlikely (line_exceeds_16_16 (&t->right))) {
+ project_line_x_onto_16_16 (&t->right, t->top, t->bottom,
+ &xtraps[i].right);
+ xtraps[i].right.p1.x += dx;
+ xtraps[i].right.p2.x += dx;
+ xtraps[i].right.p1.y = xtraps[i].top;
+ xtraps[i].right.p2.y = xtraps[i].bottom;
+ } else {
+ xtraps[i].right.p1.x = _cairo_fixed_to_16_16(t->right.p1.x) + dx;
+ xtraps[i].right.p1.y = _cairo_fixed_to_16_16(t->right.p1.y) + dy;
+ xtraps[i].right.p2.x = _cairo_fixed_to_16_16(t->right.p2.x) + dx;
+ xtraps[i].right.p2.y = _cairo_fixed_to_16_16(t->right.p2.y) + dy;
+ }
+ }
+
+ if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
+ src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p1.x);
+ src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p1.y);
+ } else {
+ src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p2.x);
+ src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p2.y);
+ }
+ src_x += dst_x;
+ src_y += dst_y;
+
+ _cairo_xlib_surface_ensure_picture (dst);
+ _cairo_xlib_surface_set_precision (dst, antialias);
+ XRenderCompositeTrapezoids (dst->dpy,
+ _render_operator (op),
+ src->picture, dst->picture,
+ pict_format,
+ src_x, src_y,
+ xtraps, traps->num_traps);
+
+ if (xtraps != xtraps_stack)
+ free (xtraps);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_tristrip (void *abstract_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_tristrip_t *strip)
+{
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_xlib_display_t *display = dst->display;
+ cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
+ XRenderPictFormat *pict_format;
+ XPointFixed points_stack[CAIRO_STACK_ARRAY_LENGTH (XPointFixed)];
+ XPointFixed *points = points_stack;
+ int dx, dy;
+ int i;
+
+ //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
+
+ pict_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
+
+ if (strip->num_points > ARRAY_LENGTH (points_stack)) {
+ points = _cairo_malloc_ab (strip->num_points, sizeof (XPointFixed));
+ if (unlikely (points == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ dx = -dst_x << 16;
+ dy = -dst_y << 16;
+ for (i = 0; i < strip->num_points; i++) {
+ cairo_point_t *p = &strip->points[i];
+
+ points[i].x = _cairo_fixed_to_16_16(p->x) + dx;
+ points[i].y = _cairo_fixed_to_16_16(p->y) + dy;
+ }
+
+ src_x += _cairo_fixed_16_16_floor (points[0].x) + dst_x;
+ src_y += _cairo_fixed_16_16_floor (points[0].y) + dst_y;
+
+ _cairo_xlib_surface_ensure_picture (dst);
+ _cairo_xlib_surface_set_precision (dst, antialias);
+ XRenderCompositeTriStrip (dst->dpy,
+ _render_operator (op),
+ src->picture, dst->picture,
+ pict_format,
+ src_x, src_y,
+ points, strip->num_points);
+
+ if (points != points_stack)
+ free (points);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_compositor_t *
+_cairo_xlib_traps_compositor_get (void)
+{
+ static cairo_traps_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_traps_compositor_init (&compositor,
+ _cairo_xlib_mask_compositor_get ());
+
+ compositor.acquire = acquire;
+ compositor.release = release;
+ compositor.set_clip_region = set_clip_region;
+ compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
+ compositor.draw_image_boxes = draw_image_boxes;
+ compositor.copy_boxes = copy_boxes;
+ compositor.fill_boxes = fill_boxes;
+ compositor.check_composite = check_composite;
+ compositor.composite = composite;
+ compositor.lerp = lerp;
+ //FIXME:
+ compositor.lerp_color_glyph = lerp;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ //compositor.check_composite_traps = check_composite_traps;
+ compositor.composite_traps = composite_traps;
+ //compositor.check_composite_tristrip = check_composite_tristrip;
+ compositor.composite_tristrip = composite_tristrip;
+ compositor.check_composite_glyphs = check_composite_glyphs;
+ compositor.composite_glyphs = composite_glyphs;
+ }
+
+ return &compositor.base;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
new file mode 100755
index 000000000..119603e9a
--- /dev/null
+++ b/src/cairo-xlib-screen.c
@@ -0,0 +1,472 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Partially on code from xftdpy.c
+ *
+ * Copyright © 2000 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Keith Packard makes no
+ * representations about the suitability of this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ *
+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-xlib-surface-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-inline.h"
+
+#include "cairo-fontconfig-private.h"
+
+static int
+parse_boolean (const char *v)
+{
+ char c0, c1;
+
+ c0 = *v;
+ if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1')
+ return 1;
+ if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0')
+ return 0;
+ if (c0 == 'o')
+ {
+ c1 = v[1];
+ if (c1 == 'n' || c1 == 'N')
+ return 1;
+ if (c1 == 'f' || c1 == 'F')
+ return 0;
+ }
+
+ return -1;
+}
+
+static cairo_bool_t
+get_boolean_default (Display *dpy,
+ const char *option,
+ cairo_bool_t *value)
+{
+ char *v;
+ int i;
+
+ v = XGetDefault (dpy, "Xft", option);
+ if (v) {
+ i = parse_boolean (v);
+ if (i >= 0) {
+ *value = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static cairo_bool_t
+get_integer_default (Display *dpy,
+ const char *option,
+ int *value)
+{
+ char *v, *e;
+
+ v = XGetDefault (dpy, "Xft", option);
+ if (v) {
+#if CAIRO_HAS_FC_FONT
+ if (FcNameConstant ((FcChar8 *) v, value))
+ return TRUE;
+#endif
+
+ *value = strtol (v, &e, 0);
+ if (e != v)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+_cairo_xlib_init_screen_font_options (Display *dpy,
+ cairo_xlib_screen_t *info)
+{
+ cairo_bool_t xft_hinting;
+ cairo_bool_t xft_antialias;
+ int xft_hintstyle;
+ int xft_rgba;
+ int xft_lcdfilter;
+ cairo_antialias_t antialias;
+ cairo_subpixel_order_t subpixel_order;
+ cairo_lcd_filter_t lcd_filter;
+ cairo_hint_style_t hint_style;
+
+ if (!get_boolean_default (dpy, "antialias", &xft_antialias))
+ xft_antialias = TRUE;
+
+ if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) {
+ /* -1 is an non-existant Fontconfig constant used to differentiate
+ * the case when no lcdfilter property is available.
+ */
+ xft_lcdfilter = -1;
+ }
+
+ if (!get_boolean_default (dpy, "hinting", &xft_hinting))
+ xft_hinting = TRUE;
+
+ if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle))
+ xft_hintstyle = FC_HINT_FULL;
+
+ if (!get_integer_default (dpy, "rgba", &xft_rgba))
+ {
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device;
+
+ xft_rgba = FC_RGBA_UNKNOWN;
+
+#if RENDER_MAJOR > 0 || RENDER_MINOR >= 6
+ if (display->render_major > 0 || display->render_minor >= 6) {
+ int render_order = XRenderQuerySubpixelOrder (dpy,
+ XScreenNumberOfScreen (info->screen));
+
+ switch (render_order) {
+ default:
+ case SubPixelUnknown:
+ xft_rgba = FC_RGBA_UNKNOWN;
+ break;
+ case SubPixelHorizontalRGB:
+ xft_rgba = FC_RGBA_RGB;
+ break;
+ case SubPixelHorizontalBGR:
+ xft_rgba = FC_RGBA_BGR;
+ break;
+ case SubPixelVerticalRGB:
+ xft_rgba = FC_RGBA_VRGB;
+ break;
+ case SubPixelVerticalBGR:
+ xft_rgba = FC_RGBA_VBGR;
+ break;
+ case SubPixelNone:
+ xft_rgba = FC_RGBA_NONE;
+ break;
+ }
+ }
+#endif
+ }
+
+ if (xft_hinting) {
+ switch (xft_hintstyle) {
+ case FC_HINT_NONE:
+ hint_style = CAIRO_HINT_STYLE_NONE;
+ break;
+ case FC_HINT_SLIGHT:
+ hint_style = CAIRO_HINT_STYLE_SLIGHT;
+ break;
+ case FC_HINT_MEDIUM:
+ hint_style = CAIRO_HINT_STYLE_MEDIUM;
+ break;
+ case FC_HINT_FULL:
+ hint_style = CAIRO_HINT_STYLE_FULL;
+ break;
+ default:
+ hint_style = CAIRO_HINT_STYLE_DEFAULT;
+ }
+ } else {
+ hint_style = CAIRO_HINT_STYLE_NONE;
+ }
+
+ switch (xft_rgba) {
+ case FC_RGBA_RGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
+ break;
+ case FC_RGBA_BGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
+ break;
+ case FC_RGBA_VRGB:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
+ break;
+ case FC_RGBA_VBGR:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
+ break;
+ case FC_RGBA_UNKNOWN:
+ case FC_RGBA_NONE:
+ default:
+ subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+ }
+
+ switch (xft_lcdfilter) {
+ case FC_LCD_NONE:
+ lcd_filter = CAIRO_LCD_FILTER_NONE;
+ break;
+ case FC_LCD_DEFAULT:
+ lcd_filter = CAIRO_LCD_FILTER_FIR5;
+ break;
+ case FC_LCD_LIGHT:
+ lcd_filter = CAIRO_LCD_FILTER_FIR3;
+ break;
+ case FC_LCD_LEGACY:
+ lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
+ break;
+ default:
+ lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
+ break;
+ }
+
+ if (xft_antialias) {
+ if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
+ antialias = CAIRO_ANTIALIAS_GRAY;
+ else
+ antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+ } else {
+ antialias = CAIRO_ANTIALIAS_NONE;
+ }
+
+ cairo_font_options_set_hint_style (&info->font_options, hint_style);
+ cairo_font_options_set_antialias (&info->font_options, antialias);
+ cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order);
+ _cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter);
+ cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
+}
+
+void
+_cairo_xlib_screen_destroy (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info)
+{
+ Display *dpy;
+ int i;
+
+ dpy = display->display;
+
+ while (! cairo_list_is_empty (&info->surfaces)) {
+ cairo_xlib_surface_t *surface;
+
+ surface = cairo_list_first_entry (&info->surfaces,
+ cairo_xlib_surface_t,
+ link);
+ cairo_surface_finish (&surface->base);
+ }
+
+ for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+ if (info->gc_depths[i] != 0) {
+ XFreeGC (dpy, info->gc[i]);
+ info->gc_depths[i] = 0;
+ }
+ }
+
+ while (! cairo_list_is_empty (&info->visuals)) {
+ _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals,
+ cairo_xlib_visual_info_t,
+ link));
+ }
+
+ cairo_list_del (&info->link);
+
+ free (info);
+}
+
+cairo_status_t
+_cairo_xlib_screen_get (Display *dpy,
+ Screen *screen,
+ cairo_xlib_screen_t **out)
+{
+ cairo_xlib_display_t *display;
+ cairo_device_t *device;
+ cairo_xlib_screen_t *info;
+ cairo_status_t status;
+
+ device = _cairo_xlib_device_create (dpy);
+ status = device->status;
+ if (unlikely (status))
+ goto CLEANUP_DEVICE;
+
+ status = _cairo_xlib_display_acquire (device, &display);
+ if (unlikely (status))
+ goto CLEANUP_DEVICE;
+
+ info = _cairo_xlib_display_get_screen (display, screen);
+ if (info != NULL) {
+ *out = info;
+ goto CLEANUP_DISPLAY;
+ }
+
+ info = malloc (sizeof (cairo_xlib_screen_t));
+ if (unlikely (info == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_DISPLAY;
+ }
+
+ info->device = device;
+ info->screen = screen;
+ info->has_font_options = FALSE;
+ memset (info->gc_depths, 0, sizeof (info->gc_depths));
+ memset (info->gc, 0, sizeof (info->gc));
+
+ cairo_list_init (&info->surfaces);
+ cairo_list_init (&info->visuals);
+ cairo_list_add (&info->link, &display->screens);
+
+ *out = info;
+
+ CLEANUP_DISPLAY:
+ cairo_device_release (&display->base);
+
+ CLEANUP_DEVICE:
+ cairo_device_destroy (device);
+ return status;
+}
+
+GC
+_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info,
+ int depth,
+ Drawable drawable)
+{
+ GC gc = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+ if (info->gc_depths[i] == depth) {
+ info->gc_depths[i] = 0;
+ gc = info->gc[i];
+ break;
+ }
+ }
+
+ if (gc == NULL) {
+ XGCValues gcv;
+
+ gcv.graphics_exposures = False;
+ gcv.fill_style = FillTiled;
+ gc = XCreateGC (display->display,
+ drawable,
+ GCGraphicsExposures | GCFillStyle, &gcv);
+ }
+
+ return gc;
+}
+
+void
+_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info,
+ int depth,
+ GC gc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+ if (info->gc_depths[i] == 0)
+ break;
+ }
+
+ if (i == ARRAY_LENGTH (info->gc)) {
+ /* perform random substitution to ensure fair caching over depths */
+ i = rand () % ARRAY_LENGTH (info->gc);
+ XFreeGC(display->display, info->gc[i]);
+ }
+
+ info->gc[i] = gc;
+ info->gc_depths[i] = depth;
+}
+
+cairo_status_t
+_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info,
+ Visual *v,
+ cairo_xlib_visual_info_t **out)
+{
+ cairo_xlib_visual_info_t *visual;
+ cairo_status_t status;
+ int screen_number;
+
+ cairo_list_foreach_entry (visual,
+ cairo_xlib_visual_info_t,
+ &info->visuals,
+ link)
+ {
+ if (visual->visualid == v->visualid) {
+ *out = visual;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ screen_number = XScreenNumberOfScreen (info->screen);
+ if (screen_number < 0)
+ return CAIRO_STATUS_NEGATIVE_COUNT;
+
+ status = _cairo_xlib_visual_info_create (display->display,
+ screen_number,
+ v->visualid,
+ &visual);
+ if (unlikely (status))
+ return status;
+
+ cairo_list_add (&visual->link, &info->visuals);
+ *out = visual;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_font_options_t *
+_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info)
+{
+ if (! info->has_font_options) {
+ _cairo_font_options_init_default (&info->font_options);
+ _cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON);
+
+ if (info->screen != NULL) {
+ cairo_xlib_display_t *display;
+
+ if (! _cairo_xlib_display_acquire (info->device, &display)) {
+ _cairo_xlib_init_screen_font_options (display->display,
+ info);
+ cairo_device_release (&display->base);
+ }
+ }
+
+ info->has_font_options = TRUE;
+ }
+
+ return &info->font_options;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c
new file mode 100755
index 000000000..4e9babd79
--- /dev/null
+++ b/src/cairo-xlib-source.c
@@ -0,0 +1,1157 @@
+/* -*- 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
+ */
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-inline.h"
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+static cairo_xlib_surface_t *
+unwrap_source (const cairo_surface_pattern_t *pattern)
+{
+ cairo_rectangle_int_t limits;
+ return (cairo_xlib_surface_t *)_cairo_pattern_get_source (pattern, &limits);
+}
+
+static cairo_status_t
+_cairo_xlib_source_finish (void *abstract_surface)
+{
+ cairo_xlib_source_t *source = abstract_surface;
+
+ XRenderFreePicture (source->dpy, source->picture);
+ if (source->pixmap)
+ XFreePixmap (source->dpy, source->pixmap);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_xlib_source_backend = {
+ CAIRO_SURFACE_TYPE_XLIB,
+ _cairo_xlib_source_finish,
+ NULL, /* read-only wrapper */
+};
+
+static cairo_status_t
+_cairo_xlib_proxy_finish (void *abstract_surface)
+{
+ cairo_xlib_proxy_t *proxy = abstract_surface;
+
+ _cairo_xlib_shm_surface_mark_active (proxy->owner);
+ XRenderFreePicture (proxy->source.dpy, proxy->source.picture);
+ if (proxy->source.pixmap)
+ XFreePixmap (proxy->source.dpy, proxy->source.pixmap);
+ cairo_surface_destroy (proxy->owner);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_xlib_proxy_backend = {
+ CAIRO_SURFACE_TYPE_XLIB,
+ _cairo_xlib_proxy_finish,
+ NULL, /* read-only wrapper */
+};
+
+static cairo_surface_t *
+source (cairo_xlib_surface_t *dst, Picture picture, Pixmap pixmap)
+{
+ cairo_xlib_source_t *source;
+
+ if (picture == None)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ source = malloc (sizeof (*source));
+ if (unlikely (source == NULL)) {
+ XRenderFreePicture (dst->display->display, picture);
+ if (pixmap)
+ XFreePixmap (dst->display->display, pixmap);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&source->base,
+ &cairo_xlib_source_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ /* The source exists only within an operation */
+ source->picture = picture;
+ source->pixmap = pixmap;
+ source->dpy = dst->display->display;
+
+ return &source->base;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static const XTransform identity = {
+ {
+ { 1 << 16, 0x00000, 0x00000 },
+ { 0x00000, 1 << 16, 0x00000 },
+ { 0x00000, 0x00000, 1 << 16 },
+ }
+};
+
+static cairo_bool_t
+picture_set_matrix (cairo_xlib_display_t *display,
+ Picture picture,
+ const cairo_matrix_t *matrix,
+ cairo_filter_t filter,
+ double xc,
+ double yc,
+ int *x_offset,
+ int *y_offset)
+{
+ XTransform xtransform;
+ pixman_transform_t *pixman_transform;
+ cairo_int_status_t status;
+
+ /* Casting between pixman_transform_t and XTransform is safe because
+ * they happen to be the exact same type.
+ */
+ pixman_transform = (pixman_transform_t *) &xtransform;
+ status = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc,
+ pixman_transform,
+ x_offset, y_offset);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ return TRUE;
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+ return FALSE;
+
+ if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0)
+ return TRUE;
+
+ /* a late check in case we perturb the matrix too far */
+ if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display))
+ return FALSE;
+
+ XRenderSetPictureTransform (display->display, picture, &xtransform);
+ return TRUE;
+}
+
+static cairo_status_t
+picture_set_filter (Display *dpy,
+ Picture picture,
+ cairo_filter_t filter)
+{
+ const char *render_filter;
+
+ switch (filter) {
+ case CAIRO_FILTER_FAST:
+ render_filter = FilterFast;
+ break;
+ case CAIRO_FILTER_GOOD:
+ render_filter = FilterGood;
+ break;
+ case CAIRO_FILTER_BEST:
+ render_filter = FilterBest;
+ break;
+ case CAIRO_FILTER_NEAREST:
+ render_filter = FilterNearest;
+ break;
+ case CAIRO_FILTER_BILINEAR:
+ render_filter = FilterBilinear;
+ break;
+ case CAIRO_FILTER_GAUSSIAN:
+ /* XXX: The GAUSSIAN value has no implementation in cairo
+ * whatsoever, so it was really a mistake to have it in the
+ * API. We could fix this by officially deprecating it, or
+ * else inventing semantics and providing an actual
+ * implementation for it. */
+ default:
+ render_filter = FilterBest;
+ break;
+ }
+
+ XRenderSetPictureFilter (dpy, picture, (char *) render_filter, NULL, 0);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+extend_to_repeat (cairo_extend_t extend)
+{
+ switch (extend) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_EXTEND_NONE:
+ return RepeatNone;
+ case CAIRO_EXTEND_REPEAT:
+ return RepeatNormal;
+ case CAIRO_EXTEND_REFLECT:
+ return RepeatReflect;
+ case CAIRO_EXTEND_PAD:
+ return RepeatPad;
+ }
+}
+
+static cairo_bool_t
+picture_set_properties (cairo_xlib_display_t *display,
+ Picture picture,
+ const cairo_pattern_t *pattern,
+ const cairo_matrix_t *matrix,
+ const cairo_rectangle_int_t *extents,
+ int *x_off, int *y_off)
+{
+ XRenderPictureAttributes pa;
+ int mask = 0;
+
+ if (! picture_set_matrix (display, picture, matrix, pattern->filter,
+ extents->x + extents->width / 2,
+ extents->y + extents->height / 2,
+ x_off, y_off))
+ return FALSE;
+
+ picture_set_filter (display->display, picture, pattern->filter);
+
+ if (pattern->has_component_alpha) {
+ pa.component_alpha = 1;
+ mask |= CPComponentAlpha;
+ }
+
+ if (pattern->extend != CAIRO_EXTEND_NONE) {
+ pa.repeat = extend_to_repeat (pattern->extend);
+ mask |= CPRepeat;
+ }
+
+ if (mask)
+ XRenderChangePicture (display->display, picture, mask, &pa);
+
+ return TRUE;
+}
+
+static cairo_surface_t *
+render_pattern (cairo_xlib_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ int *src_x, int *src_y)
+{
+ Display *dpy = dst->display->display;
+ cairo_xlib_surface_t *src;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ cairo_rectangle_int_t map_extents;
+
+ src = (cairo_xlib_surface_t *)
+ _cairo_surface_create_similar_scratch (&dst->base,
+ is_mask ? CAIRO_CONTENT_ALPHA : CAIRO_CONTENT_COLOR_ALPHA,
+ extents->width,
+ extents->height);
+ if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ cairo_surface_destroy (&src->base);
+ return None;
+ }
+
+ map_extents = *extents;
+ map_extents.x = map_extents.y = 0;
+
+ image = _cairo_surface_map_to_image (&src->base, &map_extents);
+ status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y,
+ CAIRO_OPERATOR_SOURCE, pattern,
+ NULL);
+ status = _cairo_surface_unmap_image (&src->base, image);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&src->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ status = _cairo_xlib_surface_put_shm (src);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&src->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ src->picture = XRenderCreatePicture (dpy,
+ src->drawable, src->xrender_format,
+ 0, NULL);
+
+ *src_x = -extents->x;
+ *src_y = -extents->y;
+ return &src->base;
+}
+
+static cairo_surface_t *
+gradient_source (cairo_xlib_surface_t *dst,
+ const cairo_gradient_pattern_t *gradient,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ int *src_x, int *src_y)
+{
+ cairo_xlib_display_t *display = dst->display;
+ cairo_matrix_t matrix = gradient->base.matrix;
+ char buf[CAIRO_STACK_BUFFER_SIZE];
+ cairo_circle_double_t extremes[2];
+ XFixed *stops;
+ XRenderColor *colors;
+ Picture picture;
+ unsigned int i, n_stops;
+
+ /* The RENDER specification says that the inner circle has
+ * to be completely contained inside the outer one. */
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL &&
+ ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) gradient))
+ return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y);
+
+ assert (gradient->n_stops > 0);
+ n_stops = MAX (gradient->n_stops, 2);
+
+ if (n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor)))
+ {
+ stops = (XFixed *) buf;
+ }
+ else
+ {
+ stops =
+ _cairo_malloc_ab (n_stops,
+ sizeof (XFixed) + sizeof (XRenderColor));
+ if (unlikely (stops == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ colors = (XRenderColor *) (stops + n_stops);
+ for (i = 0; i < gradient->n_stops; i++) {
+ stops[i] =
+ _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
+
+ colors[i].red = gradient->stops[i].color.red_short;
+ colors[i].green = gradient->stops[i].color.green_short;
+ colors[i].blue = gradient->stops[i].color.blue_short;
+ colors[i].alpha = gradient->stops[i].color.alpha_short;
+ }
+
+ /* RENDER does not support gradients with less than 2
+ * stops. If a gradient has only a single stop, duplicate
+ * it to make RENDER happy. */
+ if (gradient->n_stops == 1) {
+ stops[1] =
+ _cairo_fixed_16_16_from_double (gradient->stops[0].offset);
+
+ colors[1].red = gradient->stops[0].color.red_short;
+ colors[1].green = gradient->stops[0].color.green_short;
+ colors[1].blue = gradient->stops[0].color.blue_short;
+ colors[1].alpha = gradient->stops[0].color.alpha_short;
+ }
+
+#if 0
+ /* For some weird reason the X server is sometimes getting
+ * CreateGradient requests with bad length. So far I've only seen
+ * XRenderCreateLinearGradient request with 4 stops sometime end up
+ * with length field matching 0 stops at the server side. I've
+ * looked at the libXrender code and I can't see anything that
+ * could cause this behavior. However, for some reason having a
+ * XSync call here seems to avoid the issue so I'll keep it here
+ * until it's solved.
+ */
+ XSync (display->display, False);
+#endif
+
+ _cairo_gradient_pattern_fit_to_range (gradient, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ XLinearGradient grad;
+
+ grad.p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ grad.p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ grad.p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ grad.p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+ picture = XRenderCreateLinearGradient (display->display, &grad,
+ stops, colors,
+ n_stops);
+ } else {
+ XRadialGradient grad;
+
+ grad.inner.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ grad.inner.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ grad.inner.radius = _cairo_fixed_16_16_from_double (extremes[0].radius);
+ grad.outer.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ grad.outer.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+ grad.outer.radius = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+ picture = XRenderCreateRadialGradient (display->display, &grad,
+ stops, colors,
+ n_stops);
+ }
+
+ if (stops != (XFixed *) buf)
+ free (stops);
+
+ *src_x = *src_y = 0;
+ if (! picture_set_properties (display, picture,
+ &gradient->base, &gradient->base.matrix,
+ extents,
+ src_x, src_y)) {
+ XRenderFreePicture (display->display, picture);
+ return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y);
+ }
+
+ return source (dst, picture, None);
+}
+
+static cairo_surface_t *
+color_source (cairo_xlib_surface_t *dst, const cairo_color_t *color)
+{
+ Display *dpy = dst->display->display;
+ XRenderColor xcolor;
+ Picture picture;
+ Pixmap pixmap = None;
+
+ xcolor.red = color->red_short;
+ xcolor.green = color->green_short;
+ xcolor.blue = color->blue_short;
+ xcolor.alpha = color->alpha_short;
+
+ if (CAIRO_RENDER_HAS_GRADIENTS(dst->display)) {
+ picture = XRenderCreateSolidFill (dpy, &xcolor);
+ } else {
+ XRenderPictureAttributes pa;
+ int mask = 0;
+
+ pa.repeat = RepeatNormal;
+ mask |= CPRepeat;
+
+ pixmap = XCreatePixmap (dpy, dst->drawable, 1, 1, 32);
+ picture = XRenderCreatePicture (dpy, pixmap,
+ _cairo_xlib_display_get_xrender_format (dst->display, CAIRO_FORMAT_ARGB32),
+ mask, &pa);
+
+ if (CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) {
+ XRectangle r = { 0, 0, 1, 1};
+ XRenderFillRectangles (dpy, PictOpSrc, picture, &xcolor, &r, 1);
+ } else {
+ XGCValues gcv;
+ GC gc;
+
+ gc = _cairo_xlib_screen_get_gc (dst->display, dst->screen,
+ 32, pixmap);
+ if (unlikely (gc == NULL)) {
+ XFreePixmap (dpy, pixmap);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ gcv.foreground = 0;
+ gcv.foreground |= color->alpha_short >> 8 << 24;
+ gcv.foreground |= color->red_short >> 8 << 16;
+ gcv.foreground |= color->green_short >> 8 << 8;
+ gcv.foreground |= color->blue_short >> 8 << 0;
+ gcv.fill_style = FillSolid;
+
+ XChangeGC (dpy, gc, GCFillStyle | GCForeground, &gcv);
+ XFillRectangle (dpy, pixmap, gc, 0, 0, 1, 1);
+
+ _cairo_xlib_screen_put_gc (dst->display, dst->screen, 32, gc);
+ }
+ }
+
+ return source (dst, picture, pixmap);
+}
+
+static cairo_surface_t *
+alpha_source (cairo_xlib_surface_t *dst, uint8_t alpha)
+{
+ cairo_xlib_display_t *display = dst->display;
+
+ if (display->alpha[alpha] == NULL) {
+ cairo_color_t color;
+
+ color.red_short = color.green_short = color.blue_short = 0;
+ color.alpha_short = alpha << 8 | alpha;
+
+ display->alpha[alpha] = color_source (dst, &color);
+ }
+
+ return cairo_surface_reference (display->alpha[alpha]);
+}
+
+static cairo_surface_t *
+white_source (cairo_xlib_surface_t *dst)
+{
+ cairo_xlib_display_t *display = dst->display;
+
+ if (display->white == NULL)
+ display->white = color_source (dst, CAIRO_COLOR_WHITE);
+
+ return cairo_surface_reference (display->white);
+}
+
+static cairo_surface_t *
+opaque_source (cairo_xlib_surface_t *dst, const cairo_color_t *color)
+{
+ cairo_xlib_display_t *display = dst->display;
+ uint32_t pixel =
+ 0xff000000 |
+ color->red_short >> 8 << 16 |
+ color->green_short >> 8 << 8 |
+ color->blue_short >> 8 << 0;
+ int i;
+
+ if (display->last_solid_cache[0].color == pixel)
+ return cairo_surface_reference (display->solid[display->last_solid_cache[0].index]);
+
+ for (i = 0; i < 16; i++) {
+ if (display->solid_cache[i] == pixel)
+ goto done;
+ }
+
+ i = hars_petruska_f54_1_random () % 16;
+ cairo_surface_destroy (display->solid[i]);
+
+ display->solid[i] = color_source (dst, color);
+ display->solid_cache[i] = pixel;
+
+done:
+ display->last_solid_cache[0].color = pixel;
+ display->last_solid_cache[0].index = i;
+ return cairo_surface_reference (display->solid[i]);
+}
+
+static cairo_surface_t *
+transparent_source (cairo_xlib_surface_t *dst, const cairo_color_t *color)
+{
+ cairo_xlib_display_t *display = dst->display;
+ uint32_t pixel =
+ color->alpha_short >> 8 << 24 |
+ color->red_short >> 8 << 16 |
+ color->green_short >> 8 << 8 |
+ color->blue_short >> 8 << 0;
+ int i;
+
+ if (display->last_solid_cache[1].color == pixel) {
+ assert (display->solid[display->last_solid_cache[1].index]);
+ return cairo_surface_reference (display->solid[display->last_solid_cache[1].index]);
+ }
+
+ for (i = 16; i < 32; i++) {
+ if (display->solid_cache[i] == pixel)
+ goto done;
+ }
+
+ i = 16 + (hars_petruska_f54_1_random () % 16);
+ cairo_surface_destroy (display->solid[i]);
+
+ display->solid[i] = color_source (dst, color);
+ display->solid_cache[i] = pixel;
+
+done:
+ display->last_solid_cache[1].color = pixel;
+ display->last_solid_cache[1].index = i;
+ assert (display->solid[i]);
+ return cairo_surface_reference (display->solid[i]);
+}
+
+static cairo_surface_t *
+solid_source (cairo_xlib_surface_t *dst,
+ const cairo_color_t *color)
+{
+ if ((color->red_short | color->green_short | color->blue_short) <= 0xff)
+ return alpha_source (dst, color->alpha_short >> 8);
+
+ if (CAIRO_ALPHA_SHORT_IS_OPAQUE (color->alpha_short)) {
+ if (color->red_short >= 0xff00 && color->green_short >= 0xff00 && color->blue_short >= 0xff00)
+ return white_source (dst);
+
+ return opaque_source (dst, color);
+ } else
+ return transparent_source (dst, color);
+}
+
+static cairo_xlib_source_t *init_source (cairo_xlib_surface_t *dst,
+ cairo_xlib_surface_t *src)
+{
+ Display *dpy = dst->display->display;
+ cairo_xlib_source_t *source = &src->embedded_source;
+
+ /* As these are frequent and meant to be fast, we track pictures for
+ * native surface and minimise update requests.
+ */
+ if (source->picture == None) {
+ XRenderPictureAttributes pa;
+
+ _cairo_surface_init (&source->base,
+ &cairo_xlib_source_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ pa.subwindow_mode = IncludeInferiors;
+ source->picture = XRenderCreatePicture (dpy,
+ src->drawable,
+ src->xrender_format,
+ CPSubwindowMode, &pa);
+
+ source->has_component_alpha = 0;
+ source->has_matrix = 0;
+ source->filter = CAIRO_FILTER_NEAREST;
+ source->extend = CAIRO_EXTEND_NONE;
+ }
+
+ return (cairo_xlib_source_t *) cairo_surface_reference (&source->base);
+}
+
+static cairo_surface_t *
+embedded_source (cairo_xlib_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *src_x, int *src_y,
+ cairo_xlib_source_t *source)
+{
+ Display *dpy = dst->display->display;
+ cairo_int_status_t status;
+ XTransform xtransform;
+ XRenderPictureAttributes pa;
+ unsigned mask = 0;
+
+ status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix,
+ pattern->base.filter,
+ extents->x + extents->width / 2,
+ extents->y + extents->height / 2,
+ (pixman_transform_t *)&xtransform,
+ src_x, src_y);
+
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ if (source->has_matrix) {
+ source->has_matrix = 0;
+ memcpy (&xtransform, &identity, sizeof (identity));
+ status = CAIRO_INT_STATUS_SUCCESS;
+ }
+ } else
+ source->has_matrix = 1;
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ XRenderSetPictureTransform (dpy, source->picture, &xtransform);
+
+ if (source->filter != pattern->base.filter) {
+ picture_set_filter (dpy, source->picture, pattern->base.filter);
+ source->filter = pattern->base.filter;
+ }
+
+ if (source->has_component_alpha != pattern->base.has_component_alpha) {
+ pa.component_alpha = pattern->base.has_component_alpha;
+ mask |= CPComponentAlpha;
+ source->has_component_alpha = pattern->base.has_component_alpha;
+ }
+
+ if (source->extend != pattern->base.extend) {
+ pa.repeat = extend_to_repeat (pattern->base.extend);
+ mask |= CPRepeat;
+ source->extend = pattern->base.extend;
+ }
+
+ if (mask)
+ XRenderChangePicture (dpy, source->picture, mask, &pa);
+
+ return &source->base;
+}
+
+static cairo_surface_t *
+subsurface_source (cairo_xlib_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_surface_subsurface_t *sub;
+ cairo_xlib_surface_t *src;
+ cairo_xlib_source_t *source;
+ Display *dpy = dst->display->display;
+ cairo_int_status_t status;
+ cairo_surface_pattern_t local_pattern;
+ XTransform xtransform;
+ XRenderPictureAttributes pa;
+ unsigned mask = 0;
+
+ sub = (cairo_surface_subsurface_t *) pattern->surface;
+
+ if (sample->x >= 0 && sample->y >= 0 &&
+ sample->x + sample->width <= sub->extents.width &&
+ sample->y + sample->height <= sub->extents.height)
+ {
+ src = (cairo_xlib_surface_t *) sub->target;
+ status = _cairo_surface_flush (&src->base, 0);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (pattern->base.filter == CAIRO_FILTER_NEAREST &&
+ _cairo_matrix_is_translation (&pattern->base.matrix))
+ {
+ *src_x += pattern->base.matrix.x0 + sub->extents.x;
+ *src_y += pattern->base.matrix.y0 + sub->extents.y;
+
+ _cairo_xlib_surface_ensure_picture (src);
+ return cairo_surface_reference (&src->base);
+ }
+ else
+ {
+ cairo_surface_pattern_t local_pattern = *pattern;
+ local_pattern.base.matrix.x0 += sub->extents.x;
+ local_pattern.base.matrix.y0 += sub->extents.y;
+ local_pattern.base.extend = CAIRO_EXTEND_NONE;
+ return embedded_source (dst, &local_pattern, extents,
+ src_x, src_y, init_source (dst, src));
+ }
+ }
+
+ if (sub->snapshot && sub->snapshot->type == CAIRO_SURFACE_TYPE_XLIB) {
+ src = (cairo_xlib_surface_t *) cairo_surface_reference (sub->snapshot);
+ source = &src->embedded_source;
+ } else {
+ src = (cairo_xlib_surface_t *)
+ _cairo_surface_create_similar_scratch (&dst->base,
+ sub->base.content,
+ sub->extents.width,
+ sub->extents.height);
+ if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ cairo_surface_destroy (&src->base);
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_pattern_init_for_surface (&local_pattern, sub->target);
+ cairo_matrix_init_translate (&local_pattern.base.matrix,
+ sub->extents.x, sub->extents.y);
+ local_pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (&src->base,
+ CAIRO_OPERATOR_SOURCE,
+ &local_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&local_pattern.base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&src->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ _cairo_xlib_surface_ensure_picture (src);
+ _cairo_surface_subsurface_set_snapshot (&sub->base, &src->base);
+
+ source = &src->embedded_source;
+ source->has_component_alpha = 0;
+ source->has_matrix = 0;
+ source->filter = CAIRO_FILTER_NEAREST;
+ source->extend = CAIRO_EXTEND_NONE;
+ }
+
+ status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix,
+ pattern->base.filter,
+ extents->x + extents->width / 2,
+ extents->y + extents->height / 2,
+ (pixman_transform_t *)&xtransform,
+ src_x, src_y);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ if (source->has_matrix) {
+ source->has_matrix = 0;
+ memcpy (&xtransform, &identity, sizeof (identity));
+ status = CAIRO_INT_STATUS_SUCCESS;
+ }
+ } else
+ source->has_matrix = 1;
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ XRenderSetPictureTransform (dpy, src->picture, &xtransform);
+
+ if (source->filter != pattern->base.filter) {
+ picture_set_filter (dpy, src->picture, pattern->base.filter);
+ source->filter = pattern->base.filter;
+ }
+
+ if (source->has_component_alpha != pattern->base.has_component_alpha) {
+ pa.component_alpha = pattern->base.has_component_alpha;
+ mask |= CPComponentAlpha;
+ source->has_component_alpha = pattern->base.has_component_alpha;
+ }
+
+ if (source->extend != pattern->base.extend) {
+ pa.repeat = extend_to_repeat (pattern->base.extend);
+ mask |= CPRepeat;
+ source->extend = pattern->base.extend;
+ }
+
+ if (mask)
+ XRenderChangePicture (dpy, src->picture, mask, &pa);
+
+ return &src->base;
+}
+
+static cairo_surface_t *
+native_source (cairo_xlib_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_xlib_surface_t *src;
+ cairo_int_status_t status;
+
+ if (_cairo_surface_is_subsurface (pattern->surface))
+ return subsurface_source (dst, pattern, is_mask,
+ extents, sample,
+ src_x, src_y);
+
+ src = unwrap_source (pattern);
+ status = _cairo_surface_flush (&src->base, 0);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (pattern->base.filter == CAIRO_FILTER_NEAREST &&
+ sample->x >= 0 && sample->y >= 0 &&
+ sample->x + sample->width <= src->width &&
+ sample->y + sample->height <= src->height &&
+ _cairo_matrix_is_translation (&pattern->base.matrix))
+ {
+ *src_x += pattern->base.matrix.x0;
+ *src_y += pattern->base.matrix.y0;
+ _cairo_xlib_surface_ensure_picture (src);
+ return cairo_surface_reference (&src->base);
+ }
+
+ return embedded_source (dst, pattern, extents, src_x, src_y,
+ init_source (dst, src));
+}
+
+static cairo_surface_t *
+recording_pattern_get_surface (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+ if (_cairo_surface_is_paginated (surface))
+ surface = _cairo_paginated_surface_get_recording (surface);
+ if (_cairo_surface_is_snapshot (surface))
+ surface = _cairo_surface_snapshot_get_target (surface);
+ return surface;
+}
+
+static cairo_surface_t *
+record_source (cairo_xlib_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_xlib_surface_t *src;
+ cairo_matrix_t matrix, m;
+ cairo_status_t status;
+ cairo_rectangle_int_t upload, limit;
+
+ upload = *sample;
+ if (_cairo_surface_get_extents (pattern->surface, &limit) &&
+ ! _cairo_rectangle_intersect (&upload, &limit))
+ {
+ if (pattern->base.extend == CAIRO_EXTEND_NONE)
+ return alpha_source (dst, 0);
+
+ upload = limit;
+ }
+
+ src = (cairo_xlib_surface_t *)
+ _cairo_surface_create_similar_scratch (&dst->base,
+ pattern->surface->content,
+ upload.width,
+ upload.height);
+ if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ cairo_surface_destroy (&src->base);
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ cairo_matrix_init_translate (&matrix, upload.x, upload.y);
+ status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (&pattern->base),
+ &matrix, &src->base,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&src->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ matrix = pattern->base.matrix;
+ if (upload.x | upload.y) {
+ cairo_matrix_init_translate (&m, -upload.x, -upload.y);
+ cairo_matrix_multiply (&matrix, &matrix, &m);
+ }
+
+ _cairo_xlib_surface_ensure_picture (src);
+ if (! picture_set_properties (src->display, src->picture,
+ &pattern->base, &matrix, extents,
+ src_x, src_y))
+ {
+ cairo_surface_destroy (&src->base);
+ return render_pattern (dst, &pattern->base, is_mask,
+ extents, src_x, src_y);
+ }
+
+ return &src->base;
+}
+
+static cairo_surface_t *
+surface_source (cairo_xlib_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_surface_t *src;
+ cairo_xlib_surface_t *xsrc;
+ cairo_surface_pattern_t local_pattern;
+ cairo_status_t status;
+ cairo_rectangle_int_t upload, limit;
+ XRenderPictFormat *format = NULL;
+
+ src = pattern->surface;
+ if (src->type == CAIRO_SURFACE_TYPE_IMAGE &&
+ src->device == dst->base.device &&
+ _cairo_xlib_shm_surface_get_pixmap (src)) {
+ cairo_xlib_proxy_t *proxy;
+
+ proxy = malloc (sizeof(*proxy));
+ if (unlikely (proxy == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&proxy->source.base,
+ &cairo_xlib_proxy_backend,
+ dst->base.device,
+ src->content);
+
+ proxy->source.dpy = dst->display->display;
+ format = _cairo_xlib_shm_surface_get_xrender_format(src);
+ if (format == NULL) {
+ free (proxy);
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NULL_POINTER);
+ }
+
+ proxy->source.picture = XRenderCreatePicture (proxy->source.dpy,
+ _cairo_xlib_shm_surface_get_pixmap (src),
+ format,
+ 0, NULL);
+ proxy->source.pixmap = None;
+
+ proxy->source.has_component_alpha = 0;
+ proxy->source.has_matrix = 0;
+ proxy->source.filter = CAIRO_FILTER_NEAREST;
+ proxy->source.extend = CAIRO_EXTEND_NONE;
+ proxy->owner = cairo_surface_reference (src);
+
+ return embedded_source (dst, pattern, extents, src_x, src_y,
+ &proxy->source);
+ }
+
+ upload = *sample;
+ if (_cairo_surface_get_extents (pattern->surface, &limit)) {
+ if (pattern->base.extend == CAIRO_EXTEND_NONE) {
+ if (! _cairo_rectangle_intersect (&upload, &limit))
+ return alpha_source (dst, 0);
+ } else {
+ if (upload.x < limit.x ||
+ upload.x + upload.width > limit.x + limit.width ||
+ upload.y < limit.y ||
+ upload.y + upload.height > limit.y + limit.height)
+ {
+ upload = limit;
+ }
+ }
+ }
+
+ xsrc = (cairo_xlib_surface_t *)
+ _cairo_surface_create_similar_scratch (&dst->base,
+ src->content,
+ upload.width,
+ upload.height);
+ if (xsrc->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ cairo_surface_destroy (src);
+ cairo_surface_destroy (&xsrc->base);
+ return None;
+ }
+
+ if (_cairo_surface_is_image (src)) {
+ status = _cairo_xlib_surface_draw_image (xsrc, (cairo_image_surface_t *)src,
+ upload.x, upload.y,
+ upload.width, upload.height,
+ 0, 0);
+ } else {
+ cairo_image_surface_t *image;
+ cairo_rectangle_int_t map_extents = { 0,0, upload.width,upload.height };
+
+ image = _cairo_surface_map_to_image (&xsrc->base, &map_extents);
+
+ _cairo_pattern_init_for_surface (&local_pattern, pattern->surface);
+ cairo_matrix_init_translate (&local_pattern.base.matrix,
+ upload.x, upload.y);
+
+ status = _cairo_surface_paint (&image->base,
+ CAIRO_OPERATOR_SOURCE,
+ &local_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&local_pattern.base);
+
+ status = _cairo_surface_unmap_image (&xsrc->base, image);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&xsrc->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ status = _cairo_xlib_surface_put_shm (xsrc);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&xsrc->base);
+ return _cairo_surface_create_in_error (status);
+ }
+ }
+
+ _cairo_pattern_init_static_copy (&local_pattern.base, &pattern->base);
+ if (upload.x | upload.y) {
+ cairo_matrix_t m;
+ cairo_matrix_init_translate (&m, -upload.x, -upload.y);
+ cairo_matrix_multiply (&local_pattern.base.matrix,
+ &local_pattern.base.matrix,
+ &m);
+ }
+
+ *src_x = *src_y = 0;
+ _cairo_xlib_surface_ensure_picture (xsrc);
+ if (! picture_set_properties (xsrc->display,
+ xsrc->picture,
+ &local_pattern.base,
+ &local_pattern.base.matrix,
+ extents,
+ src_x, src_y))
+ {
+ cairo_surface_destroy (&xsrc->base);
+ return render_pattern (dst, &pattern->base,
+ is_mask, extents,
+ src_x, src_y);
+ }
+
+ return &xsrc->base;
+}
+
+static cairo_bool_t
+pattern_is_supported (cairo_xlib_display_t *display,
+ const cairo_pattern_t *pattern)
+{
+ if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
+ return FALSE;
+
+ if (display->buggy_pad_reflect) {
+ if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_PAD)
+ return FALSE;
+ }
+
+ if (display->buggy_gradients) {
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+ return FALSE;
+ }
+
+ if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) {
+ if (!_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
+ return FALSE;
+ }
+
+ if (! CAIRO_RENDER_HAS_FILTERS (display)) {
+ /* No filters implies no transforms, so we optimise away BILINEAR */
+ }
+
+ return TRUE;
+}
+cairo_surface_t *
+_cairo_xlib_source_create_for_pattern (cairo_surface_t *_dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)_dst;
+
+ *src_x = *src_y = 0;
+
+ if (pattern == NULL || pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ if (pattern == NULL)
+ pattern = &_cairo_pattern_white.base;
+
+ return solid_source (dst, &((cairo_solid_pattern_t *)pattern)->color);
+ }
+
+ if (pattern_is_supported (dst->display, pattern)) {
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *)pattern;
+ if (spattern->surface->type == CAIRO_SURFACE_TYPE_XLIB &&
+ _cairo_xlib_surface_same_screen (dst,
+ unwrap_source (spattern)))
+ return native_source (dst, spattern, is_mask,
+ extents, sample,
+ src_x, src_y);
+
+ if (spattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+ return record_source (dst, spattern, is_mask,
+ extents, sample,
+ src_x, src_y);
+
+ return surface_source (dst, spattern, is_mask,
+ extents, sample,
+ src_x, src_y);
+ }
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ cairo_gradient_pattern_t *gpattern = (cairo_gradient_pattern_t *)pattern;
+ return gradient_source (dst, gpattern, is_mask, extents, src_x, src_y);
+ }
+ }
+
+ return render_pattern (dst, pattern, is_mask, extents, src_x, src_y);
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h
new file mode 100755
index 000000000..87db6962b
--- /dev/null
+++ b/src/cairo-xlib-surface-private.h
@@ -0,0 +1,44 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ */
+
+#ifndef CAIRO_XLIB_SURFACE_PRIVATE_H
+#define CAIRO_XLIB_SURFACE_PRIVATE_H
+
+#include "cairo-xlib.h"
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+
+#endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */
diff --git a/src/cairo-xlib-surface-shm.c b/src/cairo-xlib-surface-shm.c
new file mode 100755
index 000000000..fa7d3eb9b
--- /dev/null
+++ b/src/cairo-xlib-surface-shm.c
@@ -0,0 +1,1459 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#if !HAVE_X11_EXTENSIONS_XSHM_H || !(HAVE_X11_EXTENSIONS_SHMPROTO_H || HAVE_X11_EXTENSIONS_SHMSTR_H)
+void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) {}
+
+cairo_surface_t *
+_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
+ cairo_bool_t overwrite)
+{
+ return NULL;
+}
+
+cairo_int_status_t
+_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
+{
+ assert (!surface->fallback);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ return NULL;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ return NULL;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_similar_shm (void *other,
+ cairo_format_t format,
+ int width, int height)
+{
+ return cairo_image_surface_create (format, width, height);
+}
+
+void
+_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
+{
+ ASSERT_NOT_REACHED;
+}
+
+void
+_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
+ XImage *ximage)
+{
+ ASSERT_NOT_REACHED;
+}
+
+void *
+_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+Pixmap
+_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return 0;
+}
+
+XRenderPictFormat *
+_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return TRUE;
+}
+
+void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {}
+
+#else
+
+#include "cairo-damage-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-mempool-private.h"
+
+#include <X11/Xlibint.h>
+#include <X11/Xproto.h>
+#include <X11/extensions/XShm.h>
+#if HAVE_X11_EXTENSIONS_SHMPROTO_H
+#include <X11/extensions/shmproto.h>
+#elif HAVE_X11_EXTENSIONS_SHMSTR_H
+#include <X11/extensions/shmstr.h>
+#endif
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#define MIN_PIXMAP_SIZE 4096
+
+#define MIN_BITS 8
+#define MIN_SIZE (1<<(MIN_BITS-1))
+
+typedef struct _cairo_xlib_shm cairo_xlib_shm_t;
+typedef struct _cairo_xlib_shm_info cairo_xlib_shm_info_t;
+typedef struct _cairo_xlib_shm_surface cairo_xlib_shm_surface_t;
+
+struct _cairo_xlib_shm {
+ cairo_mempool_t mem;
+
+ XShmSegmentInfo shm;
+ unsigned long attached;
+ cairo_list_t link;
+};
+
+struct _cairo_xlib_shm_info {
+ unsigned long last_request;
+ void *mem;
+ size_t size;
+ cairo_xlib_shm_t *pool;
+};
+
+struct _cairo_xlib_shm_surface {
+ cairo_image_surface_t image;
+
+ cairo_list_t link;
+ cairo_xlib_shm_info_t *info;
+ Pixmap pixmap;
+ unsigned long active;
+ int idle;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+#define PQ_TOP(pq) ((pq)->elements[PQ_FIRST_ENTRY])
+
+struct pqueue {
+ int size, max_size;
+ cairo_xlib_shm_info_t **elements;
+};
+
+struct _cairo_xlib_shm_display {
+ int has_pixmaps;
+ int opcode;
+ int event;
+
+ Window window;
+ unsigned long last_request;
+ unsigned long last_event;
+
+ cairo_list_t surfaces;
+
+ cairo_list_t pool;
+ struct pqueue info;
+};
+
+static inline cairo_bool_t
+seqno_passed (unsigned long a, unsigned long b)
+{
+ return (long)(b - a) >= 0;
+}
+
+static inline cairo_bool_t
+seqno_before (unsigned long a, unsigned long b)
+{
+ return (long)(b - a) > 0;
+}
+
+static inline cairo_bool_t
+seqno_after (unsigned long a, unsigned long b)
+{
+ return (long)(a - b) > 0;
+}
+
+static inline cairo_status_t
+_pqueue_init (struct pqueue *pq)
+{
+ pq->max_size = 32;
+ pq->size = 0;
+
+ pq->elements = _cairo_malloc_ab (pq->max_size,
+ sizeof (cairo_xlib_shm_info_t *));
+ if (unlikely (pq->elements == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ PQ_TOP(pq) = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_fini (struct pqueue *pq)
+{
+ free (pq->elements);
+}
+
+static cairo_status_t
+_pqueue_grow (struct pqueue *pq)
+{
+ cairo_xlib_shm_info_t **new_elements;
+
+ new_elements = _cairo_realloc_ab (pq->elements,
+ 2 * pq->max_size,
+ sizeof (cairo_xlib_shm_info_t *));
+ if (unlikely (new_elements == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ pq->elements = new_elements;
+ pq->max_size *= 2;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_pqueue_shrink (struct pqueue *pq, int min_size)
+{
+ cairo_xlib_shm_info_t **new_elements;
+
+ if (min_size > pq->max_size)
+ return;
+
+ new_elements = _cairo_realloc_ab (pq->elements,
+ min_size,
+ sizeof (cairo_xlib_shm_info_t *));
+ if (unlikely (new_elements == NULL))
+ return;
+
+ pq->elements = new_elements;
+ pq->max_size = min_size;
+}
+
+static inline cairo_status_t
+_pqueue_push (struct pqueue *pq, cairo_xlib_shm_info_t *info)
+{
+ cairo_xlib_shm_info_t **elements;
+ int i, parent;
+
+ if (unlikely (pq->size + 1 == pq->max_size)) {
+ cairo_status_t status;
+
+ status = _pqueue_grow (pq);
+ if (unlikely (status))
+ return status;
+ }
+
+ elements = pq->elements;
+
+ for (i = ++pq->size;
+ i != PQ_FIRST_ENTRY &&
+ info->last_request < elements[parent = PQ_PARENT_INDEX (i)]->last_request;
+ i = parent)
+ {
+ elements[i] = elements[parent];
+ }
+
+ elements[i] = info;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_pop (struct pqueue *pq)
+{
+ cairo_xlib_shm_info_t **elements = pq->elements;
+ cairo_xlib_shm_info_t *tail;
+ int child, i;
+
+ tail = elements[pq->size--];
+ if (pq->size == 0) {
+ elements[PQ_FIRST_ENTRY] = NULL;
+ _pqueue_shrink (pq, 32);
+ return;
+ }
+
+ for (i = PQ_FIRST_ENTRY;
+ (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+ i = child)
+ {
+ if (child != pq->size &&
+ elements[child+1]->last_request < elements[child]->last_request)
+ {
+ child++;
+ }
+
+ if (elements[child]->last_request >= tail->last_request)
+ break;
+
+ elements[i] = elements[child];
+ }
+ elements[i] = tail;
+}
+
+static cairo_bool_t _x_error_occurred;
+
+static int
+_check_error_handler (Display *display,
+ XErrorEvent *event)
+{
+ _x_error_occurred = TRUE;
+ return False; /* ignored */
+}
+
+static cairo_bool_t
+can_use_shm (Display *dpy, int *has_pixmap)
+{
+ XShmSegmentInfo shm;
+ int (*old_handler) (Display *display, XErrorEvent *event);
+ Status success;
+ int major, minor;
+
+ if (! XShmQueryExtension (dpy))
+ return FALSE;
+
+ XShmQueryVersion (dpy, &major, &minor, has_pixmap);
+
+ shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
+ if (shm.shmid == -1)
+ return FALSE;
+
+ shm.readOnly = FALSE;
+ shm.shmaddr = shmat (shm.shmid, NULL, 0);
+ if (shm.shmaddr == (char *) -1) {
+ shmctl (shm.shmid, IPC_RMID, NULL);
+ return FALSE;
+ }
+
+ assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
+ _x_error_occurred = FALSE;
+
+ XLockDisplay (dpy);
+ XSync (dpy, False);
+ old_handler = XSetErrorHandler (_check_error_handler);
+
+ success = XShmAttach (dpy, &shm);
+ if (success)
+ XShmDetach (dpy, &shm);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XUnlockDisplay (dpy);
+
+ shmctl (shm.shmid, IPC_RMID, NULL);
+ shmdt (shm.shmaddr);
+
+ return success && ! _x_error_occurred;
+}
+
+static inline Display *
+peek_display (cairo_device_t *device)
+{
+ return ((cairo_xlib_display_t *)device)->display;
+}
+
+static inline unsigned long
+peek_processed (cairo_device_t *device)
+{
+ return LastKnownRequestProcessed (peek_display(device));
+}
+
+static void
+_cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display,
+ cairo_xlib_shm_t *pool)
+{
+ shmdt (pool->shm.shmaddr);
+ if (display->display) /* may be called after CloseDisplay */
+ XShmDetach (display->display, &pool->shm);
+
+ _cairo_mempool_fini (&pool->mem);
+
+ cairo_list_del (&pool->link);
+ free (pool);
+}
+
+static void send_event(cairo_xlib_display_t *display,
+ cairo_xlib_shm_info_t *info,
+ unsigned long seqno)
+{
+ XShmCompletionEvent ev;
+
+ if (! seqno_after (seqno, display->shm->last_event))
+ return;
+
+ ev.type = display->shm->event;
+ ev.send_event = 1; /* XXX or lie? */
+ ev.serial = NextRequest (display->display);
+ ev.drawable = display->shm->window;
+ ev.major_code = display->shm->opcode;
+ ev.minor_code = X_ShmPutImage;
+ ev.shmseg = info->pool->shm.shmid;
+ ev.offset = (char *)info->mem - (char *)info->pool->shm.shmaddr;
+
+ XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev);
+
+ display->shm->last_event = ev.serial;
+}
+
+static void sync (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_info_t *info;
+ struct pqueue *pq = &display->shm->info;
+
+ XSync (display->display, False);
+
+ while ((info = PQ_TOP(pq))) {
+ _cairo_mempool_free (&info->pool->mem, info->mem);
+ _pqueue_pop (&display->shm->info);
+ free (info);
+ }
+}
+
+static void
+_cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_info_t *info;
+ Display *dpy = display->display;
+ struct pqueue *pq = &display->shm->info;
+ unsigned long processed;
+
+ if (PQ_TOP(pq) == NULL)
+ return;
+
+ XEventsQueued (dpy, QueuedAfterReading);
+ processed = LastKnownRequestProcessed (dpy);
+
+ info = PQ_TOP(pq);
+ do {
+ if (! seqno_passed (info->last_request, processed)) {
+ send_event (display, info, display->shm->last_request);
+ return;
+ }
+
+ _cairo_mempool_free (&info->pool->mem, info->mem);
+ _pqueue_pop (&display->shm->info);
+ free (info);
+ } while ((info = PQ_TOP(pq)));
+}
+
+static cairo_xlib_shm_t *
+_cairo_xlib_shm_info_find (cairo_xlib_display_t *display, size_t size,
+ void **ptr, unsigned long *last_request)
+{
+ cairo_xlib_shm_info_t *info;
+ struct pqueue *pq = &display->shm->info;
+
+ if (PQ_TOP(pq) == NULL)
+ return NULL;
+
+ info = PQ_TOP(pq);
+ do {
+ cairo_xlib_shm_t *pool = info->pool;
+
+ *last_request = info->last_request;
+
+ _pqueue_pop (&display->shm->info);
+ _cairo_mempool_free (&pool->mem, info->mem);
+ free (info);
+
+ if (pool->mem.free_bytes >= size) {
+ void *mem = _cairo_mempool_alloc (&pool->mem, size);
+ if (mem != NULL) {
+ *ptr = mem;
+ return pool;
+ }
+ }
+ } while ((info = PQ_TOP(pq)));
+
+ return NULL;
+}
+
+static cairo_xlib_shm_t *
+_cairo_xlib_shm_pool_find (cairo_xlib_display_t *display,
+ size_t size,
+ void **ptr)
+{
+ cairo_xlib_shm_t *pool;
+
+ cairo_list_foreach_entry (pool, cairo_xlib_shm_t, &display->shm->pool, link) {
+ if (pool->mem.free_bytes >= size) {
+ void *mem = _cairo_mempool_alloc (&pool->mem, size);
+ if (mem != NULL) {
+ *ptr = mem;
+ return pool;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_cairo_xlib_shm_pool_cleanup (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_t *pool, *next;
+ unsigned long processed;
+
+ processed = LastKnownRequestProcessed (display->display);
+
+ cairo_list_foreach_entry_safe (pool, next, cairo_xlib_shm_t,
+ &display->shm->pool, link) {
+ if (! seqno_passed (pool->attached, processed))
+ break;
+
+ if (pool->mem.free_bytes == pool->mem.max_bytes)
+ _cairo_xlib_display_shm_pool_destroy (display, pool);
+ }
+}
+
+static cairo_xlib_shm_t *
+_cairo_xlib_shm_pool_create(cairo_xlib_display_t *display,
+ size_t size, void **ptr)
+{
+ Display *dpy = display->display;
+ cairo_xlib_shm_t *pool;
+ size_t bytes, maxbits = 16, minbits = MIN_BITS;
+ Status success;
+
+ pool = malloc (sizeof (cairo_xlib_shm_t));
+ if (pool == NULL)
+ return NULL;
+
+ bytes = 1 << maxbits;
+ while (bytes <= size)
+ bytes <<= 1, maxbits++;
+ bytes <<= 3;
+
+ minbits += (maxbits - 16) / 2;
+
+ pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
+ while (pool->shm.shmid == -1 && bytes >= 2*size) {
+ bytes >>= 1;
+ pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
+ }
+ if (pool->shm.shmid == -1)
+ goto cleanup;
+
+ pool->shm.readOnly = FALSE;
+ pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0);
+ if (pool->shm.shmaddr == (char *) -1) {
+ shmctl (pool->shm.shmid, IPC_RMID, NULL);
+ goto cleanup;
+ }
+
+ pool->attached = NextRequest (dpy);
+ success = XShmAttach (dpy, &pool->shm);
+#if !IPC_RMID_DEFERRED_RELEASE
+ XSync (dpy, FALSE);
+#endif
+ shmctl (pool->shm.shmid, IPC_RMID, NULL);
+
+ if (! success)
+ goto cleanup_shm;
+
+ if (_cairo_mempool_init (&pool->mem, pool->shm.shmaddr, bytes,
+ minbits, maxbits - minbits + 1))
+ goto cleanup_detach;
+
+ cairo_list_add (&pool->link, &display->shm->pool);
+
+ *ptr = _cairo_mempool_alloc (&pool->mem, size);
+ assert (*ptr != NULL);
+ return pool;
+
+cleanup_detach:
+ XShmDetach (dpy, &pool->shm);
+cleanup_shm:
+ shmdt (pool->shm.shmaddr);
+cleanup:
+ free (pool);
+ return NULL;
+}
+
+static cairo_xlib_shm_info_t *
+_cairo_xlib_shm_info_create (cairo_xlib_display_t *display,
+ size_t size, cairo_bool_t will_sync)
+{
+ cairo_xlib_shm_info_t *info;
+ cairo_xlib_shm_t *pool;
+ unsigned long last_request = 0;
+ void *mem = NULL;
+
+ _cairo_xlib_shm_info_cleanup (display);
+ pool = _cairo_xlib_shm_pool_find (display, size, &mem);
+ _cairo_xlib_shm_pool_cleanup (display);
+
+ if (pool == NULL && will_sync)
+ pool = _cairo_xlib_shm_info_find (display, size, &mem, &last_request);
+ if (pool == NULL)
+ pool = _cairo_xlib_shm_pool_create (display, size, &mem);
+ if (pool == NULL)
+ return NULL;
+
+ assert (mem != NULL);
+
+ info = malloc (sizeof (*info));
+ if (info == NULL) {
+ _cairo_mempool_free (&pool->mem, mem);
+ return NULL;
+ }
+
+ info->pool = pool;
+ info->mem = mem;
+ info->size = size;
+ info->last_request = last_request;
+
+ return info;
+}
+
+static cairo_status_t
+_cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_xlib_shm_surface_t *shm = abstract_surface;
+ cairo_xlib_display_t *display;
+ Display *dpy;
+ cairo_status_t status;
+
+ if (shm->active == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shm->image.base._finishing)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
+ shm->active = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ send_event (display, shm->info, shm->active);
+
+ dpy = display->display;
+ XEventsQueued (dpy, QueuedAfterReading);
+ while (! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))) {
+ LockDisplay(dpy);
+ _XReadEvents(dpy);
+ UnlockDisplay(dpy);
+ }
+
+ cairo_device_release (&display->base);
+ shm->active = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_bool_t
+active (cairo_xlib_shm_surface_t *shm, Display *dpy)
+{
+ return (shm->active &&
+ ! seqno_passed (shm->active, LastKnownRequestProcessed (dpy)));
+}
+
+static cairo_status_t
+_cairo_xlib_shm_surface_finish (void *abstract_surface)
+{
+ cairo_xlib_shm_surface_t *shm = abstract_surface;
+ cairo_xlib_display_t *display;
+ cairo_status_t status;
+
+ if (shm->image.base.damage) {
+ _cairo_damage_destroy (shm->image.base.damage);
+ shm->image.base.damage = _cairo_damage_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+ }
+
+ status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ if (shm->pixmap)
+ XFreePixmap (display->display, shm->pixmap);
+
+ if (active (shm, display->display)) {
+ shm->info->last_request = shm->active;
+ _pqueue_push (&display->shm->info, shm->info);
+ if (seqno_before (display->shm->last_request, shm->active))
+ display->shm->last_request = shm->active;
+ } else {
+ _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
+ free (shm->info);
+
+ _cairo_xlib_shm_pool_cleanup (display);
+ }
+
+ cairo_list_del (&shm->link);
+
+ cairo_device_release (&display->base);
+ return _cairo_image_surface_finish (abstract_surface);
+}
+
+static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_xlib_shm_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_image_surface_create_similar,
+ NULL, /* create similar image */
+ _cairo_image_surface_map_to_image,
+ _cairo_image_surface_unmap_image,
+
+ _cairo_image_surface_source,
+ _cairo_image_surface_acquire_source_image,
+ _cairo_image_surface_release_source_image,
+ _cairo_image_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_image_surface_get_extents,
+ _cairo_image_surface_get_font_options,
+
+ _cairo_xlib_shm_surface_flush,
+ NULL,
+
+ _cairo_image_surface_paint,
+ _cairo_image_surface_mask,
+ _cairo_image_surface_stroke,
+ _cairo_image_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_image_surface_glyphs,
+};
+
+static cairo_bool_t
+has_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
+ return display->shm != NULL;
+}
+
+static int
+has_shm_pixmaps (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
+ if (!display->shm)
+ return 0;
+
+ return display->shm->has_pixmaps;
+}
+
+static cairo_xlib_shm_surface_t *
+_cairo_xlib_shm_surface_create (cairo_xlib_surface_t *other,
+ pixman_format_code_t format,
+ int width, int height,
+ cairo_bool_t will_sync,
+ int create_pixmap)
+{
+ cairo_xlib_shm_surface_t *shm;
+ cairo_xlib_display_t *display;
+ pixman_image_t *image;
+ int stride, size;
+
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP(format));
+ size = stride * height;
+ if (size < MIN_SIZE)
+ return NULL;
+
+ shm = malloc (sizeof (*shm));
+ if (unlikely (shm == NULL))
+ return (cairo_xlib_shm_surface_t *)_cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&shm->image.base,
+ &cairo_xlib_shm_surface_backend,
+ other->base.device,
+ _cairo_content_from_pixman_format (format));
+
+ if (_cairo_xlib_display_acquire (other->base.device, &display))
+ goto cleanup_shm;
+
+ shm->info = _cairo_xlib_shm_info_create (display, size, will_sync);
+ if (shm->info == NULL)
+ goto cleanup_display;
+
+ image = pixman_image_create_bits (format, width, height,
+ (uint32_t *) shm->info->mem, stride);
+ if (image == NULL)
+ goto cleanup_info;
+
+ _cairo_image_surface_init (&shm->image, image, format);
+
+ shm->pixmap = 0;
+ if (create_pixmap && size >= create_pixmap) {
+ shm->pixmap = XShmCreatePixmap (display->display,
+ other->drawable,
+ shm->info->mem,
+ &shm->info->pool->shm,
+ shm->image.width,
+ shm->image.height,
+ shm->image.depth);
+ }
+ shm->active = shm->info->last_request;
+ shm->idle = -5;
+
+ assert (shm->active == 0 || will_sync);
+
+ cairo_list_add (&shm->link, &display->shm->surfaces);
+
+ cairo_device_release (&display->base);
+
+ return shm;
+
+cleanup_info:
+ _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
+ free(shm->info);
+cleanup_display:
+ cairo_device_release (&display->base);
+cleanup_shm:
+ free (shm);
+ return NULL;
+}
+
+static void
+_cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
+ cairo_xlib_display_t *display;
+ cairo_damage_t *damage;
+ GC gc;
+
+ damage = _cairo_damage_reduce (surface->base.damage);
+ surface->base.damage = _cairo_damage_create();
+
+ if (_cairo_xlib_display_acquire (surface->base.device, &display))
+ goto cleanup_damage;
+
+ if (_cairo_xlib_surface_get_gc (display, surface, &gc))
+ goto cleanup_display;
+
+ if (! surface->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = IncludeInferiors;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+ }
+
+ if (damage->region) {
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
+ XRectangle *rects = stack_rects;
+ cairo_rectangle_int_t r;
+ int n_rects, i;
+
+ n_rects = cairo_region_num_rectangles (damage->region);
+ if (n_rects == 0) {
+ } else if (n_rects == 1) {
+ cairo_region_get_rectangle (damage->region, 0, &r);
+ XCopyArea (display->display,
+ surface->drawable, shm->pixmap, gc,
+ r.x, r.y,
+ r.width, r.height,
+ r.x, r.y);
+ } else {
+ if (n_rects > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
+ if (unlikely (rects == NULL)) {
+ rects = stack_rects;
+ n_rects = ARRAY_LENGTH (stack_rects);
+ }
+ }
+ for (i = 0; i < n_rects; i++) {
+ cairo_region_get_rectangle (damage->region, i, &r);
+
+ rects[i].x = r.x;
+ rects[i].y = r.y;
+ rects[i].width = r.width;
+ rects[i].height = r.height;
+ }
+ XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
+
+ XCopyArea (display->display,
+ surface->drawable, shm->pixmap, gc,
+ 0, 0,
+ shm->image.width, shm->image.height,
+ 0, 0);
+
+ if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
+ XSetClipMask (display->display, gc, None);
+ }
+ } else {
+ XCopyArea (display->display,
+ surface->drawable, shm->pixmap, gc,
+ 0, 0,
+ shm->image.width, shm->image.height,
+ 0, 0);
+ }
+
+ if (! surface->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = ClipByChildren;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+ }
+
+ sync (display);
+ shm->active = 0;
+ shm->idle--;
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+cleanup_display:
+ cairo_device_release (&display->base);
+cleanup_damage:
+ _cairo_damage_destroy (damage);
+}
+
+static void
+_cairo_xlib_surface_clear_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
+
+ assert (shm->active == 0);
+
+ _cairo_damage_destroy (surface->base.damage);
+ surface->base.damage = _cairo_damage_create();
+
+ memset (shm->image.data, 0, shm->image.stride * shm->image.height);
+ shm->image.base.is_clear = TRUE;
+}
+
+static void inc_idle (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
+ shm->idle++;
+}
+
+static void dec_idle (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
+ shm->idle--;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
+ cairo_bool_t overwrite)
+{
+ if (surface->fallback) {
+ assert (surface->base.damage);
+ assert (surface->shm);
+ assert (surface->shm->damage);
+ goto done;
+ }
+
+ if (surface->shm == NULL) {
+ pixman_format_code_t pixman_format;
+ cairo_bool_t will_sync;
+
+ if (! has_shm_pixmaps (surface))
+ return NULL;
+
+ if ((surface->width | surface->height) < 32)
+ return NULL;
+
+ pixman_format = _pixman_format_for_xlib_surface (surface);
+ if (pixman_format == 0)
+ return NULL;
+
+ will_sync = !surface->base.is_clear && !overwrite;
+
+ surface->shm =
+ &_cairo_xlib_shm_surface_create (surface, pixman_format,
+ surface->width, surface->height,
+ will_sync, 1)->image.base;
+ if (surface->shm == NULL)
+ return NULL;
+
+ assert (surface->base.damage == NULL);
+ if (surface->base.serial || !surface->owns_pixmap) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ surface->base.damage =
+ _cairo_damage_add_rectangle (NULL, &rect);
+ } else
+ surface->base.damage = _cairo_damage_create ();
+
+ surface->shm->damage = _cairo_damage_create ();
+ }
+
+ if (overwrite) {
+ _cairo_damage_destroy (surface->base.damage);
+ surface->base.damage = _cairo_damage_create ();
+ }
+
+ if (!surface->base.is_clear && surface->base.damage->dirty)
+ _cairo_xlib_surface_update_shm (surface);
+
+ _cairo_xlib_shm_surface_flush (surface->shm, 1);
+
+ if (surface->base.is_clear && surface->base.damage->dirty)
+ _cairo_xlib_surface_clear_shm (surface);
+
+done:
+ dec_idle(surface->shm);
+ return surface->shm;
+}
+
+cairo_int_status_t
+_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ if (!surface->fallback) {
+ if (surface->shm)
+ inc_idle (surface->shm);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (surface->shm->damage->dirty) {
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface->shm;
+ cairo_xlib_display_t *display;
+ cairo_damage_t *damage;
+ GC gc;
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ damage = _cairo_damage_reduce (shm->image.base.damage);
+ shm->image.base.damage = _cairo_damage_create ();
+
+ TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__,
+ damage->region ? cairo_region_num_rectangles (damage->region) : 0));
+ if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) {
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
+ XRectangle *rects = stack_rects;
+ cairo_rectangle_int_t r;
+ int n_rects, i;
+
+ n_rects = cairo_region_num_rectangles (damage->region);
+ if (n_rects == 0)
+ goto out;
+
+ status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+ if (unlikely (status))
+ goto out;
+
+ if (n_rects == 1) {
+ cairo_region_get_rectangle (damage->region, 0, &r);
+ _cairo_xlib_shm_surface_mark_active (surface->shm);
+ XCopyArea (display->display,
+ shm->pixmap, surface->drawable, gc,
+ r.x, r.y,
+ r.width, r.height,
+ r.x, r.y);
+ } else {
+ if (n_rects > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
+ if (unlikely (rects == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+ goto out;
+ }
+ }
+ for (i = 0; i < n_rects; i++) {
+ cairo_region_get_rectangle (damage->region, i, &r);
+
+ rects[i].x = r.x;
+ rects[i].y = r.y;
+ rects[i].width = r.width;
+ rects[i].height = r.height;
+ }
+ XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
+
+ _cairo_xlib_shm_surface_mark_active (surface->shm);
+ XCopyArea (display->display,
+ shm->pixmap, surface->drawable, gc,
+ 0, 0,
+ shm->image.width, shm->image.height,
+ 0, 0);
+
+ if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
+ XSetClipMask (display->display, gc, None);
+ }
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+ }
+
+out:
+ _cairo_damage_destroy (damage);
+ cairo_device_release (&display->base);
+ }
+
+ return status;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ cairo_surface_t *surface;
+
+ surface = NULL;
+ if (has_shm (other))
+ surface = &_cairo_xlib_shm_surface_create (other, format, width, height,
+ FALSE, has_shm_pixmaps (other))->image.base;
+
+ return surface;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ if (! has_shm(surface))
+ return NULL;
+
+ return &_cairo_xlib_shm_surface_create (surface, format, width, height,
+ FALSE, 0)->image.base;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_similar_shm (void *other,
+ cairo_format_t format,
+ int width, int height)
+{
+ cairo_surface_t *surface;
+
+ surface = _cairo_xlib_surface_create_shm (other,
+ _cairo_format_to_pixman_format_code (format),
+ width, height);
+ if (surface) {
+ if (! surface->is_clear) {
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
+ assert (shm->active == 0);
+ memset (shm->image.data, 0, shm->image.stride * shm->image.height);
+ shm->image.base.is_clear = TRUE;
+ }
+ } else
+ surface = cairo_image_surface_create (format, width, height);
+
+ return surface;
+}
+
+void
+_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm;
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device;
+
+ shm->active = NextRequest (display->display);
+}
+
+void
+_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
+ XImage *ximage)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
+ int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+ cairo_format_masks_t image_masks;
+ int ret;
+
+ ret = _pixman_format_to_masks (shm->image.pixman_format, &image_masks);
+ assert (ret);
+
+ ximage->width = shm->image.width;
+ ximage->height = shm->image.height;
+ ximage->format = ZPixmap;
+ ximage->data = (char *) shm->image.data;
+ ximage->obdata = (char *)&shm->info->pool->shm;
+ ximage->byte_order = native_byte_order;
+ ximage->bitmap_unit = 32; /* always for libpixman */
+ ximage->bitmap_bit_order = native_byte_order;
+ ximage->bitmap_pad = 32; /* always for libpixman */
+ ximage->depth = shm->image.depth;
+ ximage->bytes_per_line = shm->image.stride;
+ ximage->bits_per_pixel = image_masks.bpp;
+ ximage->red_mask = image_masks.red_mask;
+ ximage->green_mask = image_masks.green_mask;
+ ximage->blue_mask = image_masks.blue_mask;
+ ximage->xoffset = 0;
+
+ ret = XInitImage (ximage);
+ assert (ret != 0);
+}
+
+void *
+_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *) surface->device;
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
+
+ display->shm->last_event = shm->active = NextRequest (display->display);
+ return &shm->info->pool->shm;
+}
+
+Pixmap
+_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ return shm->pixmap;
+}
+
+XRenderPictFormat *
+_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ if (shm->image.format != CAIRO_FORMAT_INVALID)
+ return _cairo_xlib_display_get_xrender_format ((cairo_xlib_display_t *)surface->device,
+ shm->image.format);
+
+ return _cairo_xlib_display_get_xrender_format_for_pixman((cairo_xlib_display_t *)surface->device,
+ shm->image.pixman_format);
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ if (shm->active == 0)
+ return FALSE;
+
+ if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
+ shm->active = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ return shm->idle > 0;
+}
+
+#define XORG_VERSION_ENCODE(major,minor,patch,snap) \
+ (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap)
+
+static cairo_bool_t
+has_broken_send_shm_event (cairo_xlib_display_t *display,
+ cairo_xlib_shm_display_t *shm)
+{
+ Display *dpy = display->display;
+ int (*old_handler) (Display *display, XErrorEvent *event);
+ XShmCompletionEvent ev;
+ XShmSegmentInfo info;
+
+ info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
+ if (info.shmid == -1)
+ return TRUE;
+
+ info.readOnly = FALSE;
+ info.shmaddr = shmat (info.shmid, NULL, 0);
+ if (info.shmaddr == (char *) -1) {
+ shmctl (info.shmid, IPC_RMID, NULL);
+ return TRUE;
+ }
+
+ ev.type = shm->event;
+ ev.send_event = 1;
+ ev.serial = 1;
+ ev.drawable = shm->window;
+ ev.major_code = shm->opcode;
+ ev.minor_code = X_ShmPutImage;
+
+ ev.shmseg = info.shmid;
+ ev.offset = 0;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
+ _x_error_occurred = FALSE;
+
+ XLockDisplay (dpy);
+ XSync (dpy, False);
+ old_handler = XSetErrorHandler (_check_error_handler);
+
+ XShmAttach (dpy, &info);
+ XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev);
+ XShmDetach (dpy, &info);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XUnlockDisplay (dpy);
+
+ shmctl (info.shmid, IPC_RMID, NULL);
+ shmdt (info.shmaddr);
+
+ return _x_error_occurred;
+}
+
+static cairo_bool_t
+xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display,
+ cairo_xlib_shm_display_t *shm)
+{
+ Display *dpy = display->display;
+
+ /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent,
+ * the Xserver may crash if it does not take care when processing
+ * the event type. For instance versions of Xorg prior to 1.11.1
+ * exhibited this bug, and was fixed by:
+ *
+ * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39
+ * Author: Sam Spilsbury <sam.spilsbury@canonical.com>
+ * Date: Wed Sep 14 09:58:34 2011 +0800
+ *
+ * Remove the SendEvent bit (0x80) before doing range checks on event type.
+ */
+ if (_cairo_xlib_vendor_is_xorg (dpy) &&
+ VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1))
+ return TRUE;
+
+ /* For everyone else check that no error is generated */
+ return has_broken_send_shm_event (display, shm);
+}
+
+void
+_cairo_xlib_display_init_shm (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_display_t *shm;
+ XSetWindowAttributes attr;
+ XExtCodes *codes;
+ int has_pixmap, scr;
+
+ display->shm = NULL;
+
+ if (!can_use_shm (display->display, &has_pixmap))
+ return;
+
+ shm = malloc (sizeof (*shm));
+ if (unlikely (shm == NULL))
+ return;
+
+ codes = XInitExtension (display->display, SHMNAME);
+ if (codes == NULL) {
+ free (shm);
+ return;
+ }
+
+ shm->opcode = codes ->major_opcode;
+ shm->event = codes->first_event;
+
+ if (unlikely (_pqueue_init (&shm->info))) {
+ free (shm);
+ return;
+ }
+
+ scr = DefaultScreen (display->display);
+ attr.override_redirect = 1;
+ shm->window = XCreateWindow (display->display,
+ DefaultRootWindow (display->display), -1, -1,
+ 1, 1, 0,
+ DefaultDepth (display->display, scr),
+ InputOutput,
+ DefaultVisual (display->display, scr),
+ CWOverrideRedirect, &attr);
+ shm->last_event = 0;
+ shm->last_request = 0;
+
+ if (xorg_has_buggy_send_shm_completion_event(display, shm))
+ has_pixmap = 0;
+
+ shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0;
+ cairo_list_init (&shm->pool);
+
+ cairo_list_init (&shm->surfaces);
+
+ display->shm = shm;
+}
+
+void
+_cairo_xlib_display_fini_shm (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_display_t *shm = display->shm;
+
+ if (shm == NULL)
+ return;
+
+ while (!cairo_list_is_empty (&shm->surfaces))
+ cairo_surface_finish (&cairo_list_first_entry (&shm->surfaces,
+ cairo_xlib_shm_surface_t,
+ link)->image.base);
+
+ _pqueue_fini (&shm->info);
+
+ while (!cairo_list_is_empty (&shm->pool)) {
+ cairo_xlib_shm_t *pool;
+
+ pool = cairo_list_first_entry (&shm->pool, cairo_xlib_shm_t, link);
+ _cairo_xlib_display_shm_pool_destroy (display, pool);
+ }
+
+ if (display->display)
+ XDestroyWindow (display->display, shm->window);
+
+ free (shm);
+ display->shm = NULL;
+}
+#endif
+#endif
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
new file mode 100755
index 000000000..2fb29e3d4
--- /dev/null
+++ b/src/cairo-xlib-surface.c
@@ -0,0 +1,2382 @@
+/* -*- 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"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-damage-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-pattern-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+#include <X11/Xutil.h> /* for XDestroyImage */
+
+#include <X11/extensions/XShm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#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;
+ va_list ap;
+
+ va_start (ap, fmt);
+ len = vsnprintf (buf, sizeof (buf), fmt, ap);
+ va_end (ap);
+
+ buf[len++] = '\0';
+ while (len & 3)
+ buf[len++] = '\0';
+
+ LockDisplay (dpy);
+ GetEmptyReq (NoOperation, req);
+
+ len_dwords = len >> 2;
+ SetReqLen (req, len_dwords, len_dwords);
+ Data (dpy, buf, len);
+
+ UnlockDisplay (dpy);
+ SyncHandle ();
+}
+#define X_DEBUG(x) _x_bread_crumb x
+#else
+#define X_DEBUG(x)
+#endif
+
+/**
+ * SECTION:cairo-xlib
+ * @Title: XLib Surfaces
+ * @Short_Description: X Window System rendering using XLib
+ * @See_Also: #cairo_surface_t
+ *
+ * The XLib surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XLib library.
+ *
+ * Note that the XLib surface automatically takes advantage of X render extension
+ * if it is available.
+ **/
+
+/**
+ * CAIRO_HAS_XLIB_SURFACE:
+ *
+ * Defined if the Xlib surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * SECTION:cairo-xlib-xrender
+ * @Title: XLib-XRender Backend
+ * @Short_Description: X Window System rendering using XLib and the X Render extension
+ * @See_Also: #cairo_surface_t
+ *
+ * The XLib surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XLib and Xrender libraries.
+ *
+ * Note that the XLib surface automatically takes advantage of X Render extension
+ * if it is available.
+ **/
+
+/**
+ * CAIRO_HAS_XLIB_XRENDER_SURFACE:
+ *
+ * Defined if the XLib/XRender surface functions are available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.6
+ **/
+
+/* Xlib doesn't define a typedef, so define one ourselves */
+typedef int (*cairo_xlib_error_func_t) (Display *display,
+ XErrorEvent *event);
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen,
+ Drawable drawable,
+ Visual *visual,
+ XRenderPictFormat *xrender_format,
+ int width,
+ int height,
+ int depth);
+
+static cairo_bool_t
+_cairo_surface_is_xlib (cairo_surface_t *surface);
+
+/*
+ * Instead of taking two round trips for each blending request,
+ * assume that if a particular drawable fails GetImage that it will
+ * fail for a "while"; use temporary pixmaps to avoid the errors
+ */
+
+#define CAIRO_ASSUME_PIXMAP 20
+
+static const XTransform identity = { {
+ { 1 << 16, 0x00000, 0x00000 },
+ { 0x00000, 1 << 16, 0x00000 },
+ { 0x00000, 0x00000, 1 << 16 },
+} };
+
+static Visual *
+_visual_for_xrender_format(Screen *screen,
+ XRenderPictFormat *xrender_format)
+{
+ int d, v;
+
+ /* XXX Consider searching through the list of known cairo_visual_t for
+ * the reverse mapping.
+ */
+
+ for (d = 0; d < screen->ndepths; d++) {
+ Depth *d_info = &screen->depths[d];
+
+ if (d_info->depth != xrender_format->depth)
+ continue;
+
+ for (v = 0; v < d_info->nvisuals; v++) {
+ Visual *visual = &d_info->visuals[v];
+
+ switch (visual->class) {
+ case TrueColor:
+ if (xrender_format->type != PictTypeDirect)
+ continue;
+ break;
+
+ case DirectColor:
+ /* Prefer TrueColor to DirectColor.
+ * (XRenderFindVisualFormat considers both TrueColor and DirectColor
+ * Visuals to match the same PictFormat.)
+ */
+ continue;
+
+ case StaticGray:
+ case GrayScale:
+ case StaticColor:
+ case PseudoColor:
+ if (xrender_format->type != PictTypeIndexed)
+ continue;
+ break;
+ }
+
+ if (xrender_format ==
+ XRenderFindVisualFormat (DisplayOfScreen(screen), visual))
+ return visual;
+ }
+ }
+
+ return NULL;
+}
+
+static cairo_content_t
+_xrender_format_to_content (XRenderPictFormat *xrender_format)
+{
+ cairo_content_t content;
+
+ /* This only happens when using a non-Render server. Let's punt
+ * and say there's no alpha here. */
+ if (xrender_format == NULL)
+ return CAIRO_CONTENT_COLOR;
+
+ content = 0;
+ if (xrender_format->direct.alphaMask)
+ content |= CAIRO_CONTENT_ALPHA;
+ if (xrender_format->direct.redMask |
+ xrender_format->direct.greenMask |
+ xrender_format->direct.blueMask)
+ content |= CAIRO_CONTENT_COLOR;
+
+ return content;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_xlib_surface_t *src = abstract_src;
+ XRenderPictFormat *xrender_format;
+ cairo_xlib_surface_t *surface;
+ cairo_xlib_display_t *display;
+ Pixmap pix;
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return NULL;
+
+ if (width == 0 || height == 0)
+ return NULL;
+
+ if (_cairo_xlib_display_acquire (src->base.device, &display))
+ return NULL;
+
+ /* If we never found an XRenderFormat or if it isn't compatible
+ * with the content being requested, then we fallback to just
+ * constructing a cairo_format_t instead, (which will fairly
+ * arbitrarily pick a visual/depth for the similar surface.
+ */
+ xrender_format = NULL;
+ if (src->xrender_format &&
+ _xrender_format_to_content (src->xrender_format) == content)
+ {
+ xrender_format = src->xrender_format;
+ }
+ if (xrender_format == NULL) {
+ xrender_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ _cairo_format_from_content (content));
+ }
+ if (xrender_format) {
+ Visual *visual;
+
+ /* We've got a compatible XRenderFormat now, which means the
+ * similar surface will match the existing surface as closely in
+ * visual/depth etc. as possible. */
+ pix = XCreatePixmap (display->display, src->drawable,
+ width, height, xrender_format->depth);
+
+ if (xrender_format == src->xrender_format)
+ visual = src->visual;
+ else
+ visual = _visual_for_xrender_format(src->screen->screen,
+ xrender_format);
+
+ surface = (cairo_xlib_surface_t *)
+ _cairo_xlib_surface_create_internal (src->screen, pix, visual,
+ xrender_format,
+ width, height,
+ xrender_format->depth);
+ }
+ else
+ {
+ Screen *screen = src->screen->screen;
+ int depth;
+
+ /* No compatible XRenderFormat, see if we can make an ordinary pixmap,
+ * so that we can still accelerate blits with XCopyArea(). */
+ if (content != CAIRO_CONTENT_COLOR) {
+ cairo_device_release (&display->base);
+ return NULL;
+ }
+
+ depth = DefaultDepthOfScreen (screen);
+
+ pix = XCreatePixmap (display->display, RootWindowOfScreen (screen),
+ width <= 0 ? 1 : width, height <= 0 ? 1 : height,
+ depth);
+
+ surface = (cairo_xlib_surface_t *)
+ _cairo_xlib_surface_create_internal (src->screen, pix,
+ DefaultVisualOfScreen (screen),
+ NULL,
+ width, height, depth);
+ }
+
+ if (likely (surface->base.status == CAIRO_STATUS_SUCCESS))
+ surface->owns_pixmap = TRUE;
+ else
+ XFreePixmap (display->display, pix);
+
+ cairo_device_release (&display->base);
+
+ return &surface->base;
+}
+
+static void
+_cairo_xlib_surface_discard_shm (cairo_xlib_surface_t *surface)
+{
+ if (surface->shm == NULL)
+ return;
+
+ /* Force the flush for an external surface */
+ if (!surface->owns_pixmap)
+ cairo_surface_flush (surface->shm);
+
+ cairo_surface_finish (surface->shm);
+ cairo_surface_destroy (surface->shm);
+ surface->shm = NULL;
+
+ _cairo_damage_destroy (surface->base.damage);
+ surface->base.damage = NULL;
+
+ surface->fallback = 0;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_finish (void *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_xlib_display_t *display;
+
+ X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable));
+
+ cairo_list_del (&surface->link);
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ if (surface->embedded_source.picture)
+ XRenderFreePicture (display->display, surface->embedded_source.picture);
+ if (surface->picture)
+ XRenderFreePicture (display->display, surface->picture);
+
+ _cairo_xlib_surface_discard_shm (surface);
+
+ if (surface->owns_pixmap)
+ XFreePixmap (display->display, surface->drawable);
+
+ cairo_device_release (&display->base);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ GC *gc)
+{
+ *gc = _cairo_xlib_screen_get_gc (display,
+ surface->screen,
+ surface->depth,
+ surface->drawable);
+ if (unlikely (*gc == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+_noop_error_handler (Display *display,
+ XErrorEvent *event)
+{
+ return False; /* return value is ignored */
+}
+
+static void
+_swap_ximage_2bytes (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint16_t *p = (uint16_t *) line;
+ for (i = ximage->width; i; i--) {
+ *p = bswap_16 (*p);
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_3bytes (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint8_t *p = (uint8_t *) line;
+ for (i = ximage->width; i; i--) {
+ uint8_t tmp;
+ tmp = p[2];
+ p[2] = p[0];
+ p[0] = tmp;
+ p += 3;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_4bytes (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint32_t *p = (uint32_t *) line;
+ for (i = ximage->width; i; i--) {
+ *p = bswap_32 (*p);
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_nibbles (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+
+ for (j = ximage->height; j; j--) {
+ uint8_t *p = (uint8_t *) line;
+ for (i = (ximage->width + 1) / 2; i; i--) {
+ *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf);
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_bits (XImage *ximage)
+{
+ int i, j;
+ char *line = ximage->data;
+ int unit = ximage->bitmap_unit;
+ int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8;
+
+ for (j = ximage->height; j; j--) {
+ char *p = line;
+
+ for (i = line_bytes; i; i--) {
+ char b = *p;
+ b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
+ b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
+ b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
+ *p = b;
+
+ p++;
+ }
+
+ line += ximage->bytes_per_line;
+ }
+}
+
+static void
+_swap_ximage_to_native (XImage *ximage)
+{
+ int unit_bytes = 0;
+ int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+
+ if (ximage->bits_per_pixel == 1 &&
+ ximage->bitmap_bit_order != native_byte_order)
+ {
+ _swap_ximage_bits (ximage);
+ if (ximage->bitmap_bit_order == ximage->byte_order)
+ return;
+ }
+
+ if (ximage->byte_order == native_byte_order)
+ return;
+
+ switch (ximage->bits_per_pixel) {
+ case 1:
+ unit_bytes = ximage->bitmap_unit / 8;
+ break;
+ case 4:
+ _swap_ximage_nibbles (ximage);
+ /* fall-through */
+ case 8:
+ case 16:
+ case 20:
+ case 24:
+ case 28:
+ case 30:
+ case 32:
+ unit_bytes = (ximage->bits_per_pixel + 7) / 8;
+ break;
+ default:
+ /* This could be hit on some rare but possible cases. */
+ ASSERT_NOT_REACHED;
+ }
+
+ switch (unit_bytes) {
+ case 1:
+ break;
+ case 2:
+ _swap_ximage_2bytes (ximage);
+ break;
+ case 3:
+ _swap_ximage_3bytes (ximage);
+ break;
+ case 4:
+ _swap_ximage_4bytes (ximage);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+}
+
+
+/* Given a mask, (with a single sequence of contiguous 1 bits), return
+ * the number of 1 bits in 'width' and the number of 0 bits to its
+ * right in 'shift'. */
+static void
+_characterize_field (uint32_t mask, int *width, int *shift)
+{
+ *width = _cairo_popcount (mask);
+ /* The final '& 31' is to force a 0 mask to result in 0 shift. */
+ *shift = _cairo_popcount ((mask - 1) & ~mask) & 31;
+}
+
+/* Convert a field of 'width' bits to 'new_width' bits with correct
+ * rounding. */
+static inline uint32_t
+_resize_field (uint32_t field, int width, int new_width)
+{
+ if (width == 0)
+ return 0;
+
+ if (width >= new_width) {
+ return field >> (width - new_width);
+ } else {
+ uint32_t result = field << (new_width - width);
+
+ while (width < new_width) {
+ result |= result >> width;
+ width <<= 1;
+ }
+ return result;
+ }
+}
+
+static inline uint32_t
+_adjust_field (uint32_t field, int adjustment)
+{
+ return MIN (255, MAX(0, (int)field + adjustment));
+}
+
+/* Given a shifted field value, (described by 'width' and 'shift),
+ * resize it 8-bits and return that value.
+ *
+ * Note that the original field value must not have any non-field bits
+ * set.
+ */
+static inline uint32_t
+_field_to_8 (uint32_t field, int width, int shift)
+{
+ return _resize_field (field >> shift, width, 8);
+}
+
+static inline uint32_t
+_field_to_8_undither (uint32_t field, int width, int shift,
+ int dither_adjustment)
+{
+ return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width);
+}
+
+/* Given an 8-bit value, convert it to a field of 'width', shift it up
+ * to 'shift, and return it. */
+static inline uint32_t
+_field_from_8 (uint32_t field, int width, int shift)
+{
+ return _resize_field (field, 8, width) << shift;
+}
+
+static inline uint32_t
+_field_from_8_dither (uint32_t field, int width, int shift,
+ int8_t dither_adjustment)
+{
+ return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift);
+}
+
+static inline uint32_t
+_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info,
+ uint32_t r, uint32_t g, uint32_t b,
+ int8_t dither_adjustment)
+{
+ if (r == g && g == b) {
+ dither_adjustment /= RAMP_SIZE;
+ return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)];
+ } else {
+ dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128];
+ return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]]
+ [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]]
+ [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]];
+ }
+}
+
+static inline uint32_t
+_pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info,
+ uint32_t pixel)
+{
+ uint32_t r, g, b;
+ pixel &= 0xff;
+ r = visual_info->colors[pixel].r;
+ g = visual_info->colors[pixel].g;
+ b = visual_info->colors[pixel].b;
+ return (r << 16) |
+ (g << 8) |
+ (b );
+}
+
+/* should range from -128 to 127 */
+#define X 16
+static const int8_t dither_pattern[4][4] = {
+ {-8*X, +0*X, -6*X, +2*X},
+ {+4*X, -4*X, +6*X, -2*X},
+ {-5*X, +4*X, -7*X, +1*X},
+ {+7*X, -1*X, +5*X, -3*X}
+};
+#undef X
+
+static int bits_per_pixel(cairo_xlib_surface_t *surface)
+{
+ if (surface->depth > 16)
+ return 32;
+ else if (surface->depth > 8)
+ return 16;
+ else if (surface->depth > 1)
+ return 8;
+ else
+ return 1;
+}
+
+pixman_format_code_t
+_pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface)
+{
+ cairo_format_masks_t masks;
+ pixman_format_code_t format;
+
+ masks.bpp = bits_per_pixel (surface);
+ masks.alpha_mask = surface->a_mask;
+ masks.red_mask = surface->r_mask;
+ masks.green_mask = surface->g_mask;
+ masks.blue_mask = surface->b_mask;
+ if (! _pixman_format_from_masks (&masks, &format))
+ return 0;
+
+ return format;
+}
+
+static cairo_surface_t *
+_get_image_surface (cairo_xlib_surface_t *surface,
+ const cairo_rectangle_int_t *extents,
+ int try_shm)
+{
+ cairo_int_status_t status;
+ cairo_image_surface_t *image = NULL;
+ XImage *ximage;
+ pixman_format_code_t pixman_format;
+ cairo_xlib_display_t *display;
+
+ assert (extents->x >= 0);
+ assert (extents->y >= 0);
+ assert (extents->x + extents->width <= surface->width);
+ assert (extents->y + extents->height <= surface->height);
+
+ if (surface->base.is_clear ||
+ (surface->base.serial == 0 && surface->owns_pixmap))
+ {
+ pixman_format = _pixman_format_for_xlib_surface (surface);
+ if (pixman_format)
+ {
+ return _cairo_image_surface_create_with_pixman_format (NULL,
+ pixman_format,
+ extents->width,
+ extents->height,
+ 0);
+ }
+ }
+
+ if (surface->shm) {
+ cairo_image_surface_t *src = (cairo_image_surface_t *) surface->shm;
+ cairo_surface_t *dst;
+ cairo_surface_pattern_t pattern;
+
+ dst = cairo_image_surface_create (src->format,
+ extents->width, extents->height);
+ if (unlikely (dst->status))
+ return dst;
+
+ _cairo_pattern_init_for_surface (&pattern, &src->base);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ extents->x, extents->y);
+ status = _cairo_surface_paint (dst, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (dst);
+ dst = _cairo_surface_create_in_error (status);
+ }
+
+ return dst;
+ }
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (status)
+ return _cairo_surface_create_in_error (status);
+
+ pixman_format = _pixman_format_for_xlib_surface (surface);
+ if (try_shm && pixman_format) {
+ image = (cairo_image_surface_t *)
+ _cairo_xlib_surface_create_shm__image (surface, pixman_format,
+ extents->width, extents->height);
+ if (image && image->base.status == CAIRO_STATUS_SUCCESS) {
+ cairo_xlib_error_func_t old_handler;
+ XImage shm_image;
+ Bool success;
+
+ _cairo_xlib_shm_surface_get_ximage (&image->base, &shm_image);
+
+ old_handler = XSetErrorHandler (_noop_error_handler);
+ success = XShmGetImage (display->display,
+ surface->drawable,
+ &shm_image,
+ extents->x, extents->y,
+ AllPlanes);
+ XSetErrorHandler (old_handler);
+
+ if (success) {
+ cairo_device_release (&display->base);
+ return &image->base;
+ }
+
+ cairo_surface_destroy (&image->base);
+ }
+ }
+
+ 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, extents->height,
+ surface->depth);
+ if (pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = IncludeInferiors;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+
+ XCopyArea (display->display, surface->drawable, pixmap, gc,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ 0, 0);
+
+ gcv.subwindow_mode = ClipByChildren;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+
+ ximage = XGetImage (display->display,
+ pixmap,
+ 0, 0,
+ extents->width, extents->height,
+ AllPlanes, ZPixmap);
+
+ XFreePixmap (display->display, pixmap);
+ }
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+
+ if (ximage == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ }
+
+ _swap_ximage_to_native (ximage);
+
+ /* We can't use pixman to simply write to image if:
+ * (a) the pixels are not appropriately aligned,
+ * (b) pixman does not the pixel format, or
+ * (c) if the image is palettized and we need to convert.
+ */
+ if (pixman_format &&
+ ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 &&
+ (surface->visual == NULL || surface->visual->class == TrueColor))
+ {
+ image = (cairo_image_surface_t*)
+ _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data,
+ pixman_format,
+ ximage->width,
+ ximage->height,
+ ximage->bytes_per_line);
+ status = image->base.status;
+ if (unlikely (status))
+ goto BAIL;
+
+ /* Let the surface take ownership of the data */
+ _cairo_image_surface_assume_ownership_of_data (image);
+ ximage->data = NULL;
+ } else {
+ /* The visual we are dealing with is not supported by the
+ * standard pixman formats. So we must first convert the data
+ * to a supported format. */
+
+ cairo_format_t format;
+ unsigned char *data;
+ uint32_t *row;
+ uint32_t in_pixel, out_pixel;
+ unsigned int rowstride;
+ uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0;
+ int a_width=0, r_width=0, g_width=0, b_width=0;
+ int a_shift=0, r_shift=0, g_shift=0, b_shift=0;
+ int x, y, x0, y0, x_off, y_off;
+ cairo_xlib_visual_info_t *visual_info = NULL;
+
+ if (surface->visual == NULL || surface->visual->class == TrueColor) {
+ cairo_bool_t has_alpha;
+ cairo_bool_t has_color;
+
+ has_alpha = surface->a_mask;
+ has_color = (surface->r_mask ||
+ surface->g_mask ||
+ surface->b_mask);
+
+ if (has_color) {
+ if (has_alpha) {
+ format = CAIRO_FORMAT_ARGB32;
+ } else {
+ format = CAIRO_FORMAT_RGB24;
+ }
+ } else {
+ /* XXX: Using CAIRO_FORMAT_A8 here would be more
+ * efficient, but would require slightly different code in
+ * the image conversion to put the alpha channel values
+ * into the right place. */
+ format = CAIRO_FORMAT_ARGB32;
+ }
+
+ a_mask = surface->a_mask;
+ r_mask = surface->r_mask;
+ g_mask = surface->g_mask;
+ b_mask = surface->b_mask;
+
+ _characterize_field (a_mask, &a_width, &a_shift);
+ _characterize_field (r_mask, &r_width, &r_shift);
+ _characterize_field (g_mask, &g_width, &g_shift);
+ _characterize_field (b_mask, &b_width, &b_shift);
+
+ } else {
+ format = CAIRO_FORMAT_RGB24;
+
+ status = _cairo_xlib_screen_get_visual_info (display,
+ surface->screen,
+ surface->visual,
+ &visual_info);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ image = (cairo_image_surface_t *) cairo_image_surface_create
+ (format, ximage->width, ximage->height);
+ status = image->base.status;
+ if (unlikely (status))
+ goto BAIL;
+
+ data = cairo_image_surface_get_data (&image->base);
+ if (data == NULL){
+ status = CAIRO_STATUS_NULL_POINTER;
+ goto BAIL;
+ }
+
+ 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)) {
+ cairo_surface_destroy (&image->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &image->base;
+}
+
+void
+_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface,
+ cairo_antialias_t antialias)
+{
+ cairo_xlib_display_t *display = surface->display;
+ int precision;
+
+ if (display->force_precision != -1)
+ precision = display->force_precision;
+ else switch (antialias) {
+ default:
+ case CAIRO_ANTIALIAS_DEFAULT:
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_NONE:
+ case CAIRO_ANTIALIAS_FAST:
+ case CAIRO_ANTIALIAS_GOOD:
+ precision = PolyModeImprecise;
+ break;
+ case CAIRO_ANTIALIAS_BEST:
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ precision = PolyModePrecise;
+ break;
+ }
+
+ if (surface->precision != precision) {
+ XRenderPictureAttributes pa;
+
+ pa.poly_mode = precision;
+ XRenderChangePicture (display->display, surface->picture,
+ CPPolyMode, &pa);
+
+ surface->precision = precision;
+ }
+}
+
+void
+_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_display_t *display = surface->display;
+ XRenderPictureAttributes pa;
+ int mask = 0;
+
+ if (surface->picture)
+ return;
+
+ if (display->force_precision != -1)
+ pa.poly_mode = display->force_precision;
+ else
+ pa.poly_mode = PolyModeImprecise;
+ if (pa.poly_mode)
+ mask |= CPPolyMode;
+
+ surface->precision = pa.poly_mode;
+ surface->picture = XRenderCreatePicture (display->display,
+ surface->drawable,
+ surface->xrender_format,
+ mask, &pa);
+}
+
+cairo_status_t
+_cairo_xlib_surface_draw_image (cairo_xlib_surface_t *surface,
+ cairo_image_surface_t *image,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int dst_x,
+ int dst_y)
+{
+ cairo_xlib_display_t *display;
+ XImage ximage;
+ cairo_format_masks_t image_masks;
+ int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+ cairo_surface_t *shm_image = NULL;
+ pixman_image_t *pixman_image = NULL;
+ cairo_status_t status;
+ cairo_bool_t own_data = FALSE;
+ cairo_bool_t is_rgb_image;
+ GC gc;
+
+ ximage.width = image->width;
+ ximage.height = image->height;
+ ximage.format = ZPixmap;
+ ximage.byte_order = native_byte_order;
+ ximage.bitmap_unit = 32; /* always for libpixman */
+ ximage.bitmap_bit_order = native_byte_order;
+ ximage.bitmap_pad = 32; /* always for libpixman */
+ ximage.depth = surface->depth;
+ ximage.red_mask = surface->r_mask;
+ ximage.green_mask = surface->g_mask;
+ ximage.blue_mask = surface->b_mask;
+ ximage.xoffset = 0;
+ ximage.obdata = NULL;
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ is_rgb_image = _pixman_format_to_masks (image->pixman_format, &image_masks);
+
+ if (is_rgb_image &&
+ (image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
+ (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) &&
+ (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) &&
+ (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0))
+ {
+ int ret;
+
+ ximage.bits_per_pixel = image_masks.bpp;
+ ximage.bytes_per_line = image->stride;
+ ximage.data = (char *)image->data;
+ if (image->base.device != surface->base.device) {
+ /* If PutImage will break the image up into chunks, prefer to
+ * send it all in one pass with ShmPutImage. For larger images,
+ * it is further advantageous to reduce the number of copies,
+ * albeit at the expense of more SHM bookkeeping.
+ */
+ int max_request_size = XExtendedMaxRequestSize (display->display);
+ if (max_request_size == 0)
+ max_request_size = XMaxRequestSize (display->display);
+ if (max_request_size > 8192)
+ max_request_size = 8192;
+ if (width * height * 4 > max_request_size) {
+ shm_image = _cairo_xlib_surface_create_shm__image (surface,
+ image->pixman_format,
+ width, height);
+ if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
+ cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, clone->pixman_image,
+ src_x, src_y,
+ 0, 0,
+ 0, 0,
+ width, height);
+ ximage.obdata = _cairo_xlib_shm_surface_get_obdata (shm_image);
+ ximage.data = (char *)clone->data;
+ ximage.bytes_per_line = clone->stride;
+ ximage.width = width;
+ ximage.height = height;
+ src_x = src_y = 0;
+ }
+ }
+ } else
+ ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&image->base);
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+ }
+ else if (surface->visual == NULL || surface->visual->class == TrueColor)
+ {
+ pixman_format_code_t intermediate_format;
+ int ret;
+
+ image_masks.alpha_mask = surface->a_mask;
+ image_masks.red_mask = surface->r_mask;
+ image_masks.green_mask = surface->g_mask;
+ image_masks.blue_mask = surface->b_mask;
+ image_masks.bpp = bits_per_pixel (surface);
+ ret = _pixman_format_from_masks (&image_masks, &intermediate_format);
+ assert (ret);
+
+ shm_image = _cairo_xlib_surface_create_shm__image (surface,
+ intermediate_format,
+ width, height);
+ if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
+ cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image,
+ NULL,
+ clone->pixman_image,
+ src_x, src_y,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ ximage.data = (char *) clone->data;
+ ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&clone->base);
+ ximage.bytes_per_line = clone->stride;
+ } else {
+ pixman_image = pixman_image_create_bits (intermediate_format,
+ width, height, NULL, 0);
+ if (pixman_image == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image,
+ NULL,
+ pixman_image,
+ src_x, src_y,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ ximage.data = (char *) pixman_image_get_data (pixman_image);
+ ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
+ }
+
+ ximage.width = width;
+ ximage.height = height;
+ ximage.bits_per_pixel = image_masks.bpp;
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+
+ src_x = src_y = 0;
+ }
+ else
+ {
+ unsigned int stride, rowstride;
+ int x, y, x0, y0, x_off, y_off;
+ uint32_t in_pixel, out_pixel, *row;
+ int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0;
+ int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0;
+ int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0;
+ int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0;
+ cairo_xlib_visual_info_t *visual_info = NULL;
+ cairo_bool_t true_color;
+ int ret;
+
+ ximage.bits_per_pixel = bits_per_pixel(surface);
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width,
+ ximage.bits_per_pixel);
+ ximage.bytes_per_line = stride;
+ ximage.data = _cairo_malloc_ab (stride, ximage.height);
+ if (unlikely (ximage.data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ own_data = TRUE;
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+
+ _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift);
+ _characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift);
+ _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift);
+ _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift);
+
+ true_color = surface->visual == NULL ||
+ surface->visual->class == TrueColor;
+ if (true_color) {
+ _characterize_field (surface->a_mask, &o_a_width, &o_a_shift);
+ _characterize_field (surface->r_mask, &o_r_width, &o_r_shift);
+ _characterize_field (surface->g_mask, &o_g_width, &o_g_shift);
+ _characterize_field (surface->b_mask, &o_b_width, &o_b_shift);
+ } else {
+ status = _cairo_xlib_screen_get_visual_info (display,
+ surface->screen,
+ surface->visual,
+ &visual_info);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ rowstride = image->stride >> 2;
+ row = (uint32_t *) image->data;
+ x0 = dst_x + surface->base.device_transform.x0;
+ y0 = dst_y + surface->base.device_transform.y0;
+ for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
+ y < ximage.height;
+ y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern))
+ {
+ const int8_t *dither_row = dither_pattern[y_off];
+
+ for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
+ x < ximage.width;
+ x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0]))
+ {
+ int dither_adjustment = dither_row[x_off];
+ int a, r, g, b;
+
+ if (image_masks.bpp == 1)
+ in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7)));
+ else if (image_masks.bpp <= 8)
+ in_pixel = ((uint8_t*)row)[x];
+ else if (image_masks.bpp <= 16)
+ in_pixel = ((uint16_t*)row)[x];
+ else if (image_masks.bpp <= 24)
+#ifdef WORDS_BIGENDIAN
+ in_pixel = ((uint8_t*)row)[3 * x] << 16 |
+ ((uint8_t*)row)[3 * x + 1] << 8 |
+ ((uint8_t*)row)[3 * x + 2];
+#else
+ in_pixel = ((uint8_t*)row)[3 * x] |
+ ((uint8_t*)row)[3 * x + 1] << 8 |
+ ((uint8_t*)row)[3 * x + 2] << 16;
+#endif
+ else
+ in_pixel = row[x];
+
+ /* If the incoming image has no alpha channel, then the input
+ * is opaque and the output should have the maximum alpha value.
+ * For all other channels, their absence implies 0.
+ */
+ if (image_masks.alpha_mask == 0x0)
+ a = 0xff;
+ else
+ a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift);
+ r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift);
+ g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift);
+ b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift);
+
+ if (true_color) {
+ out_pixel = _field_from_8 (a, o_a_width, o_a_shift) |
+ _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) |
+ _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) |
+ _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment);
+ } else {
+ out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment);
+ }
+
+ XPutPixel (&ximage, x, y, out_pixel);
+ }
+
+ row += rowstride;
+ }
+ }
+
+ status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (ximage.obdata)
+ XShmPutImage (display->display, surface->drawable, gc, &ximage,
+ src_x, src_y, dst_x, dst_y, width, height, True);
+ else
+ XPutImage (display->display, surface->drawable, gc, &ximage,
+ src_x, src_y, dst_x, dst_y, width, height);
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+
+ BAIL:
+ cairo_device_release (&display->base);
+
+ if (own_data)
+ free (ximage.data);
+ if (shm_image)
+ cairo_surface_destroy (shm_image);
+ if (pixman_image)
+ pixman_image_unref (pixman_image);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_source(void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ if (extents) {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_rectangle_int_t extents;
+
+ *image_extra = NULL;
+ *image_out = (cairo_image_surface_t *)
+ _cairo_xlib_surface_get_shm (abstract_surface, FALSE);
+ if (*image_out)
+ return (*image_out)->base.status;
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ *image_out = (cairo_image_surface_t*)
+ _get_image_surface (surface, &extents, TRUE);
+ return (*image_out)->base.status;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_snapshot (void *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_rectangle_int_t extents;
+
+ extents.x = extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ return _get_image_surface (surface, &extents, FALSE);
+}
+
+static void
+_cairo_xlib_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ if (&image->base == surface->shm)
+ return;
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_image_surface_t *
+_cairo_xlib_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+
+ image = _cairo_xlib_surface_get_shm (abstract_surface, FALSE);
+ if (image) {
+ assert (surface->base.damage);
+ surface->fallback++;
+ return _cairo_image_surface_map_to_image (image, extents);
+ }
+
+ image = _get_image_surface (abstract_surface, extents, TRUE);
+ cairo_surface_set_device_offset (image, -extents->x, -extents->y);
+
+ return (cairo_image_surface_t *) image;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ if (surface->shm) {
+ cairo_rectangle_int_t r;
+
+ assert (surface->fallback);
+ assert (surface->base.damage);
+
+ r.x = image->base.device_transform_inverse.x0;
+ r.y = image->base.device_transform_inverse.y0;
+ r.width = image->width;
+ r.height = image->height;
+
+ TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n",
+ __FUNCTION__, r.x, r.y, r.width, r.height));
+ surface->shm->damage =
+ _cairo_damage_add_rectangle (surface->shm->damage, &r);
+
+ return _cairo_image_surface_unmap_image (surface->shm, image);
+ }
+
+ status = _cairo_xlib_surface_draw_image (abstract_surface, image,
+ 0, 0,
+ image->width, image->height,
+ image->base.device_transform_inverse.x0,
+ image->base.device_transform_inverse.y0);
+
+ cairo_surface_finish (&image->base);
+ cairo_surface_destroy (&image->base);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_xlib_surface_put_shm (surface);
+ if (unlikely (status))
+ return status;
+
+ surface->fallback >>= 1;
+ if (surface->shm && _cairo_xlib_shm_surface_is_idle (surface->shm))
+ _cairo_xlib_surface_discard_shm (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_xlib_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static void
+_cairo_xlib_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+
+ *options = *_cairo_xlib_screen_get_font_options (surface->screen);
+}
+
+static inline cairo_int_status_t
+get_compositor (cairo_xlib_surface_t **surface,
+ const cairo_compositor_t **compositor)
+{
+ cairo_xlib_surface_t *s = *surface;
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;;
+
+ if (s->fallback) {
+ assert (s->base.damage != NULL);
+ assert (s->shm != NULL);
+ assert (s->shm->damage != NULL);
+ if (! _cairo_xlib_shm_surface_is_active (s->shm)) {
+ *surface = (cairo_xlib_surface_t *) s->shm;
+ *compositor = ((cairo_image_surface_t *) s->shm)->compositor;
+ s->fallback++;
+ } else {
+ status = _cairo_xlib_surface_put_shm (s);
+ s->fallback = 0;
+ *compositor = s->compositor;
+ }
+ } else
+ *compositor = s->compositor;
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_paint (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_paint (compositor, &surface->base,
+ op, source,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_mask (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_mask (compositor, &surface->base,
+ op, source, mask,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_stroke (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_stroke (compositor, &surface->base,
+ op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_fill (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_fill (compositor, &surface->base,
+ op, source,
+ path, fill_rule, tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_glyphs (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_surface_t *surface = _surface;
+ const cairo_compositor_t *compositor;
+ cairo_int_status_t status;
+
+ status = get_compositor (&surface, &compositor);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_compositor_glyphs (compositor, &surface->base,
+ op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+}
+
+static const cairo_surface_backend_t cairo_xlib_surface_backend = {
+ CAIRO_SURFACE_TYPE_XLIB,
+ _cairo_xlib_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_xlib_surface_create_similar,
+ _cairo_xlib_surface_create_similar_shm,
+ _cairo_xlib_surface_map_to_image,
+ _cairo_xlib_surface_unmap_image,
+
+ _cairo_xlib_surface_source,
+ _cairo_xlib_surface_acquire_source_image,
+ _cairo_xlib_surface_release_source_image,
+ _cairo_xlib_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_xlib_surface_get_extents,
+ _cairo_xlib_surface_get_font_options,
+
+ _cairo_xlib_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_xlib_surface_paint,
+ _cairo_xlib_surface_mask,
+ _cairo_xlib_surface_stroke,
+ _cairo_xlib_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_xlib_surface_glyphs,
+};
+
+/**
+ * _cairo_surface_is_xlib:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a #cairo_xlib_surface_t
+ *
+ * Return value: True if the surface is an xlib surface
+ **/
+static cairo_bool_t
+_cairo_surface_is_xlib (cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_xlib_surface_backend;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen,
+ Drawable drawable,
+ Visual *visual,
+ XRenderPictFormat *xrender_format,
+ int width,
+ int height,
+ int depth)
+{
+ cairo_xlib_surface_t *surface;
+ cairo_xlib_display_t *display;
+ cairo_status_t status;
+
+ if (depth == 0) {
+ if (xrender_format) {
+ depth = xrender_format->depth;
+
+ /* XXX find matching visual for core/dithering fallbacks? */
+ } else if (visual) {
+ Screen *scr = screen->screen;
+
+ if (visual == DefaultVisualOfScreen (scr)) {
+ depth = DefaultDepthOfScreen (scr);
+ } else {
+ int j, k;
+
+ /* This is ugly, but we have to walk over all visuals
+ * for the display to find the correct depth.
+ */
+ depth = 0;
+ for (j = 0; j < scr->ndepths; j++) {
+ Depth *d = &scr->depths[j];
+ for (k = 0; k < d->nvisuals; k++) {
+ if (&d->visuals[k] == visual) {
+ depth = d->depth;
+ goto found;
+ }
+ }
+ }
+ }
+ }
+
+ if (depth == 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+found:
+ ;
+ }
+
+ surface = malloc (sizeof (cairo_xlib_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ status = _cairo_xlib_display_acquire (screen->device, &display);
+ if (unlikely (status)) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (status));
+ }
+
+ surface->display = display;
+ if (CAIRO_RENDER_HAS_CREATE_PICTURE (display)) {
+ if (!xrender_format) {
+ if (visual) {
+ xrender_format = XRenderFindVisualFormat (display->display, visual);
+ } else if (depth == 1) {
+ xrender_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ CAIRO_FORMAT_A1);
+ }
+ }
+ }
+
+ cairo_device_release (&display->base);
+
+ _cairo_surface_init (&surface->base,
+ &cairo_xlib_surface_backend,
+ screen->device,
+ _xrender_format_to_content (xrender_format));
+
+ surface->screen = screen;
+ surface->compositor = display->compositor;
+ surface->shm = NULL;
+ surface->fallback = 0;
+
+ surface->drawable = drawable;
+ surface->owns_pixmap = FALSE;
+ surface->use_pixmap = 0;
+ surface->width = width;
+ surface->height = height;
+
+ surface->picture = None;
+ surface->precision = PolyModePrecise;
+
+ surface->embedded_source.picture = None;
+
+ surface->visual = visual;
+ surface->xrender_format = xrender_format;
+ surface->depth = depth;
+
+ /*
+ * Compute the pixel format masks from either a XrenderFormat or
+ * else from a visual; failing that we assume the drawable is an
+ * alpha-only pixmap as it could only have been created that way
+ * through the cairo_xlib_surface_create_for_bitmap function.
+ */
+ if (xrender_format) {
+ surface->a_mask = (unsigned long)
+ surface->xrender_format->direct.alphaMask
+ << surface->xrender_format->direct.alpha;
+ surface->r_mask = (unsigned long)
+ surface->xrender_format->direct.redMask
+ << surface->xrender_format->direct.red;
+ surface->g_mask = (unsigned long)
+ surface->xrender_format->direct.greenMask
+ << surface->xrender_format->direct.green;
+ surface->b_mask = (unsigned long)
+ surface->xrender_format->direct.blueMask
+ << surface->xrender_format->direct.blue;
+ } else if (visual) {
+ surface->a_mask = 0;
+ surface->r_mask = visual->red_mask;
+ surface->g_mask = visual->green_mask;
+ surface->b_mask = visual->blue_mask;
+ } else {
+ if (depth < 32)
+ surface->a_mask = (1 << depth) - 1;
+ else
+ surface->a_mask = 0xffffffff;
+ surface->r_mask = 0;
+ surface->g_mask = 0;
+ surface->b_mask = 0;
+ }
+
+ cairo_list_add (&surface->link, &screen->surfaces);
+
+ return &surface->base;
+}
+
+static Screen *
+_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual)
+{
+ int s, d, v;
+
+ for (s = 0; s < ScreenCount (dpy); s++) {
+ Screen *screen;
+
+ screen = ScreenOfDisplay (dpy, s);
+ if (visual == DefaultVisualOfScreen (screen))
+ return screen;
+
+ for (d = 0; d < screen->ndepths; d++) {
+ Depth *depth;
+
+ depth = &screen->depths[d];
+ for (v = 0; v < depth->nvisuals; v++)
+ if (visual == &depth->visuals[v])
+ return screen;
+ }
+ }
+
+ return NULL;
+}
+
+static cairo_bool_t valid_size (int width, int height)
+{
+ /* Note: the minimum surface size allowed in the X protocol is 1x1.
+ * However, as we historically did not check the minimum size we
+ * allowed applications to lie and set the correct size later (one hopes).
+ * To preserve compatability we must allow applications to use
+ * 0x0 surfaces.
+ */
+ return (width >= 0 && width <= XLIB_COORD_MAX &&
+ height >= 0 && height <= XLIB_COORD_MAX);
+}
+
+/**
+ * cairo_xlib_surface_create:
+ * @dpy: an X Display
+ * @drawable: an X Drawable, (a Pixmap or a Window)
+ * @visual: the visual to use for drawing to @drawable. The depth
+ * of the visual must match the depth of the drawable.
+ * Currently, only TrueColor visuals are fully supported.
+ * @width: the current width of @drawable.
+ * @height: the current height of @drawable.
+ *
+ * Creates an Xlib surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided visual.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xlib_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * When @drawable is a Window containing child windows then drawing to
+ * the created surface will be clipped by those child windows. When
+ * the created surface is used as a source, the contents of the
+ * children will be included.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create (Display *dpy,
+ Drawable drawable,
+ Visual *visual,
+ int width,
+ int height)
+{
+ Screen *scr;
+ cairo_xlib_screen_t *screen;
+ cairo_status_t status;
+
+ if (! valid_size (width, height)) {
+ /* you're lying, and you know it! */
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ }
+
+ scr = _cairo_xlib_screen_from_visual (dpy, visual);
+ if (scr == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+ status = _cairo_xlib_screen_get (dpy, scr, &screen);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable));
+
+ return _cairo_xlib_surface_create_internal (screen, drawable,
+ visual, NULL,
+ width, height, 0);
+}
+
+/**
+ * cairo_xlib_surface_create_for_bitmap:
+ * @dpy: an X Display
+ * @bitmap: an X Drawable, (a depth-1 Pixmap)
+ * @screen: the X Screen associated with @bitmap
+ * @width: the current width of @bitmap.
+ * @height: the current height of @bitmap.
+ *
+ * Creates an Xlib surface that draws to the given bitmap.
+ * This will be drawn to as a %CAIRO_FORMAT_A1 object.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create_for_bitmap (Display *dpy,
+ Pixmap bitmap,
+ Screen *scr,
+ int width,
+ int height)
+{
+ cairo_xlib_screen_t *screen;
+ cairo_status_t status;
+
+ if (! valid_size (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ status = _cairo_xlib_screen_get (dpy, scr, &screen);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap));
+
+ return _cairo_xlib_surface_create_internal (screen, bitmap,
+ NULL, NULL,
+ width, height, 1);
+}
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+/**
+ * cairo_xlib_surface_create_with_xrender_format:
+ * @dpy: an X Display
+ * @drawable: an X Drawable, (a Pixmap or a Window)
+ * @screen: the X Screen associated with @drawable
+ * @format: the picture format to use for drawing to @drawable. The depth
+ * of @format must match the depth of the drawable.
+ * @width: the current width of @drawable.
+ * @height: the current height of @drawable.
+ *
+ * Creates an Xlib surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided picture format.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xlib_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create_with_xrender_format (Display *dpy,
+ Drawable drawable,
+ Screen *scr,
+ XRenderPictFormat *format,
+ int width,
+ int height)
+{
+ cairo_xlib_screen_t *screen;
+ cairo_status_t status;
+
+ if (! valid_size (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ status = _cairo_xlib_screen_get (dpy, scr, &screen);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable));
+
+ return _cairo_xlib_surface_create_internal (screen, drawable,
+ _visual_for_xrender_format (scr, format),
+ format, width, height, 0);
+}
+
+/**
+ * cairo_xlib_surface_get_xrender_format:
+ * @surface: an xlib surface
+ *
+ * Gets the X Render picture format that @surface uses for rendering with the
+ * X Render extension. If the surface was created by
+ * cairo_xlib_surface_create_with_xrender_format() originally, the return
+ * value is the format passed to that constructor.
+ *
+ * Return value: the XRenderPictFormat* associated with @surface,
+ * or %NULL if the surface is not an xlib surface
+ * or if the X Render extension is not available.
+ *
+ * Since: 1.6
+ **/
+XRenderPictFormat *
+cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface)
+{
+ cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
+
+ /* Throw an error for a non-xlib surface */
+ if (! _cairo_surface_is_xlib (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return xlib_surface->xrender_format;
+}
+#endif
+
+/**
+ * cairo_xlib_surface_set_size:
+ * @surface: a #cairo_surface_t for the XLib backend
+ * @width: the new width of the surface
+ * @height: the new height of the surface
+ *
+ * Informs cairo of the new size of the X Drawable underlying the
+ * surface. For a surface created for a Window (rather than a Pixmap),
+ * this function must be called each time the size of the window
+ * changes. (For a subwindow, you are normally resizing the window
+ * yourself, but for a toplevel window, it is necessary to listen for
+ * ConfigureNotify events.)
+ *
+ * A Pixmap can never change size, so it is never necessary to call
+ * this function on a surface created for a Pixmap.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (surface->width == width && surface->height == height)
+ return;
+
+ if (! valid_size (width, height)) {
+ _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ status = _cairo_surface_flush (abstract_surface, 0);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (abstract_surface, status);
+ return;
+ }
+
+ _cairo_xlib_surface_discard_shm (surface);
+
+ surface->width = width;
+ surface->height = height;
+}
+
+/**
+ * cairo_xlib_surface_set_drawable:
+ * @surface: a #cairo_surface_t for the XLib backend
+ * @drawable: the new drawable for the surface
+ * @width: the width of the new drawable
+ * @height: the height of the new drawable
+ *
+ * Informs cairo of a new X Drawable underlying the
+ * surface. The drawable must match the display, screen
+ * and format of the existing drawable or the application
+ * will get X protocol errors and will probably terminate.
+ * No checks are done by this function to ensure this
+ * compatibility.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface,
+ Drawable drawable,
+ int width,
+ int height)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (! valid_size (width, height)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ /* XXX: and what about this case? */
+ if (surface->owns_pixmap)
+ return;
+
+ status = _cairo_surface_flush (abstract_surface, 0);
+ if (unlikely (status)) {
+ _cairo_surface_set_error (abstract_surface, status);
+ return;
+ }
+
+ if (surface->drawable != drawable) {
+ cairo_xlib_display_t *display;
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return;
+
+ X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable));
+
+ if (surface->picture != None) {
+ XRenderFreePicture (display->display, surface->picture);
+ surface->picture = None;
+ }
+
+ cairo_device_release (&display->base);
+
+ surface->drawable = drawable;
+ }
+
+ if (surface->width != width || surface->height != height) {
+ _cairo_xlib_surface_discard_shm (surface);
+
+ surface->width = width;
+ surface->height = height;
+ }
+}
+
+/**
+ * cairo_xlib_surface_get_display:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the X Display for the underlying X Drawable.
+ *
+ * Return value: the display.
+ *
+ * Since: 1.2
+ **/
+Display *
+cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface)
+{
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return ((cairo_xlib_display_t *) abstract_surface->device)->display;
+}
+
+/**
+ * cairo_xlib_surface_get_drawable:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the underlying X Drawable used for the surface.
+ *
+ * Return value: the drawable.
+ *
+ * Since: 1.2
+ **/
+Drawable
+cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->drawable;
+}
+
+/**
+ * cairo_xlib_surface_get_screen:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the X Screen for the underlying X Drawable.
+ *
+ * Return value: the screen.
+ *
+ * Since: 1.2
+ **/
+Screen *
+cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return surface->screen->screen;
+}
+
+/**
+ * cairo_xlib_surface_get_visual:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Gets the X Visual associated with @surface, suitable for use with the
+ * underlying X Drawable. If @surface was created by
+ * cairo_xlib_surface_create(), the return value is the Visual passed to that
+ * constructor.
+ *
+ * Return value: the Visual or %NULL if there is no appropriate Visual for
+ * @surface.
+ *
+ * Since: 1.2
+ **/
+Visual *
+cairo_xlib_surface_get_visual (cairo_surface_t *surface)
+{
+ cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
+
+ if (! _cairo_surface_is_xlib (surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return xlib_surface->visual;
+}
+
+/**
+ * cairo_xlib_surface_get_depth:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the number of bits used to represent each pixel value.
+ *
+ * Return value: the depth of the surface in bits.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->depth;
+}
+
+/**
+ * cairo_xlib_surface_get_width:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the width of the X Drawable underlying the surface in pixels.
+ *
+ * Return value: the width of the surface in pixels.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->width;
+}
+
+/**
+ * cairo_xlib_surface_get_height:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the height of the X Drawable underlying the surface in pixels.
+ *
+ * Return value: the height of the surface in pixels.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+ if (! _cairo_surface_is_xlib (abstract_surface)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->height;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-visual.c b/src/cairo-xlib-visual.c
new file mode 100755
index 000000000..d9aac44ca
--- /dev/null
+++ b/src/cairo-xlib-visual.c
@@ -0,0 +1,191 @@
+/* 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"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#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);
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-xcb-surface.c b/src/cairo-xlib-xcb-surface.c
new file mode 100755
index 000000000..9c0d4b413
--- /dev/null
+++ b/src/cairo-xlib-xcb-surface.c
@@ -0,0 +1,844 @@
+/* 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>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib.h"
+#include "cairo-xcb.h"
+
+#include "cairo-xcb-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <X11/Xlib-xcb.h>
+#include <X11/Xlibint.h> /* For XESetCloseDisplay */
+
+struct cairo_xlib_xcb_display_t {
+ cairo_device_t base;
+
+ Display *dpy;
+ cairo_device_t *xcb_device;
+ XExtCodes *codes;
+
+ cairo_list_t link;
+};
+typedef struct cairo_xlib_xcb_display_t cairo_xlib_xcb_display_t;
+
+/* List of all #cairo_xlib_xcb_display_t alive,
+ * protected by _cairo_xlib_display_mutex */
+static cairo_list_t displays;
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create (void *dpy,
+ void *scr,
+ void *visual,
+ void *format,
+ cairo_surface_t *xcb);
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_xlib_xcb_surface_t *other = abstract_other;
+ cairo_surface_t *xcb;
+
+ xcb = other->xcb->base.backend->create_similar (other->xcb, content, width, height);
+ if (unlikely (xcb == NULL || xcb->status))
+ return xcb;
+
+ return _cairo_xlib_xcb_surface_create (other->display, other->screen, NULL, NULL, xcb);
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_finish (void *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ cairo_surface_finish (&surface->xcb->base);
+ status = surface->xcb->base.status;
+ cairo_surface_destroy (&surface->xcb->base);
+ surface->xcb = NULL;
+
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create_similar_image (void *abstract_other,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_other;
+ return cairo_surface_create_similar_image (&surface->xcb->base, format, width, height);
+}
+
+static cairo_image_surface_t *
+_cairo_xlib_xcb_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_map_to_image (&surface->xcb->base, extents);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_unmap (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_unmap_image (&surface->xcb->base, image);
+}
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_get_source (&surface->xcb->base, extents);
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_acquire_source_image (&surface->xcb->base,
+ image_out, image_extra);
+}
+
+static void
+_cairo_xlib_xcb_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image_out,
+ void *image_extra)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ _cairo_surface_release_source_image (&surface->xcb->base, image_out, image_extra);
+}
+
+static cairo_bool_t
+_cairo_xlib_xcb_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_get_extents (&surface->xcb->base, extents);
+}
+
+static void
+_cairo_xlib_xcb_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ cairo_surface_get_font_options (&surface->xcb->base, options);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_paint (&surface->xcb->base, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_mask (&surface->xcb->base, op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_stroke (&surface->xcb->base,
+ op, source, path, style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_fill (&surface->xcb->base,
+ op, source, path,
+ fill_rule, tolerance,
+ antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ return _cairo_surface_show_text_glyphs (&surface->xcb->base, op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font, clip);
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ /* We have to call cairo_surface_flush() to make sure snapshots are detached */
+ return _cairo_surface_flush (&surface->xcb->base, flags);
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_mark_dirty (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+{
+ cairo_xlib_xcb_surface_t *surface = abstract_surface;
+ cairo_surface_mark_dirty_rectangle (&surface->xcb->base, x, y, width, height);
+ return cairo_surface_status (&surface->xcb->base);
+}
+
+static const cairo_surface_backend_t _cairo_xlib_xcb_surface_backend = {
+ CAIRO_SURFACE_TYPE_XLIB,
+ _cairo_xlib_xcb_surface_finish,
+
+ _cairo_default_context_create, /* XXX */
+
+ _cairo_xlib_xcb_surface_create_similar,
+ _cairo_xlib_xcb_surface_create_similar_image,
+ _cairo_xlib_xcb_surface_map_to_image,
+ _cairo_xlib_xcb_surface_unmap,
+
+ _cairo_xlib_xcb_surface_source,
+ _cairo_xlib_xcb_surface_acquire_source_image,
+ _cairo_xlib_xcb_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_xlib_xcb_surface_get_extents,
+ _cairo_xlib_xcb_surface_get_font_options,
+
+ _cairo_xlib_xcb_surface_flush,
+ _cairo_xlib_xcb_surface_mark_dirty,
+
+ _cairo_xlib_xcb_surface_paint,
+ _cairo_xlib_xcb_surface_mask,
+ _cairo_xlib_xcb_surface_stroke,
+ _cairo_xlib_xcb_surface_fill,
+ NULL, /* fill_stroke */
+ _cairo_xlib_xcb_surface_glyphs,
+};
+
+static void
+_cairo_xlib_xcb_display_finish (void *abstract_display)
+{
+ cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) abstract_display;
+
+ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+ cairo_list_del (&display->link);
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+ cairo_device_destroy (display->xcb_device);
+ display->xcb_device = NULL;
+
+ XESetCloseDisplay (display->dpy, display->codes->extension, NULL);
+ /* Drop the reference from _cairo_xlib_xcb_device_create */
+ cairo_device_destroy (&display->base);
+}
+
+static int
+_cairo_xlib_xcb_close_display(Display *dpy, XExtCodes *codes)
+{
+ cairo_xlib_xcb_display_t *display;
+
+ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+ cairo_list_foreach_entry (display,
+ cairo_xlib_xcb_display_t,
+ &displays,
+ link)
+ {
+ if (display->dpy == dpy)
+ {
+ /* _cairo_xlib_xcb_display_finish will lock the mutex again
+ * -> deadlock (This mutex isn't recursive) */
+ cairo_device_reference (&display->base);
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+ /* Make sure the xcb and xlib-xcb devices are finished */
+ cairo_device_finish (display->xcb_device);
+ cairo_device_finish (&display->base);
+
+ cairo_device_destroy (&display->base);
+ return 0;
+ }
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+ return 0;
+}
+
+static const cairo_device_backend_t _cairo_xlib_xcb_device_backend = {
+ CAIRO_DEVICE_TYPE_XLIB,
+
+ NULL,
+ NULL,
+
+ NULL, /* flush */
+ _cairo_xlib_xcb_display_finish,
+ free, /* destroy */
+};
+
+static cairo_device_t *
+_cairo_xlib_xcb_device_create (Display *dpy, cairo_device_t *xcb_device)
+{
+ cairo_xlib_xcb_display_t *display = NULL;
+ cairo_device_t *device;
+
+ if (xcb_device == NULL)
+ return NULL;
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+ if (displays.next == NULL) {
+ cairo_list_init (&displays);
+ }
+
+ cairo_list_foreach_entry (display,
+ cairo_xlib_xcb_display_t,
+ &displays,
+ link)
+ {
+ if (display->dpy == dpy) {
+ /* Maintain MRU order. */
+ if (displays.next != &display->link)
+ cairo_list_move (&display->link, &displays);
+
+ /* Grab a reference for our caller */
+ device = cairo_device_reference (&display->base);
+ assert (display->xcb_device == xcb_device);
+ goto unlock;
+ }
+ }
+
+ display = malloc (sizeof (cairo_xlib_xcb_display_t));
+ if (unlikely (display == NULL)) {
+ device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ goto unlock;
+ }
+
+ display->codes = XAddExtension (dpy);
+ if (unlikely (display->codes == NULL)) {
+ device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ free (display);
+ goto unlock;
+ }
+
+ _cairo_device_init (&display->base, &_cairo_xlib_xcb_device_backend);
+
+ XESetCloseDisplay (dpy, display->codes->extension, _cairo_xlib_xcb_close_display);
+ /* Add a reference for _cairo_xlib_xcb_display_finish. This basically means
+ * that the device's reference count never drops to zero
+ * as long as our Display* is alive. We need this because there is no
+ * "XDelExtension" to undo XAddExtension and having lots of registered
+ * extensions slows down libX11. */
+ cairo_device_reference (&display->base);
+
+ display->dpy = dpy;
+ display->xcb_device = cairo_device_reference(xcb_device);
+
+ cairo_list_add (&display->link, &displays);
+ device = &display->base;
+
+unlock:
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+ return device;
+}
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create (void *dpy,
+ void *scr,
+ void *visual,
+ void *format,
+ cairo_surface_t *xcb)
+{
+ cairo_xlib_xcb_surface_t *surface;
+
+ if (unlikely (xcb->status))
+ return xcb;
+
+ surface = malloc (sizeof (*surface));
+ if (unlikely (surface == NULL)) {
+ cairo_surface_destroy (xcb);
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_xlib_xcb_surface_backend,
+ _cairo_xlib_xcb_device_create (dpy, xcb->device),
+ xcb->content);
+
+ /* _cairo_surface_init() got another reference to the device, drop ours */
+ cairo_device_destroy (surface->base.device);
+
+ surface->display = dpy;
+ surface->screen = scr;
+ surface->visual = visual;
+ surface->format = format;
+ surface->xcb = (cairo_xcb_surface_t *) xcb;
+
+ return &surface->base;
+}
+
+static Screen *
+_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual)
+{
+ int s, d, v;
+
+ for (s = 0; s < ScreenCount (dpy); s++) {
+ Screen *screen;
+
+ screen = ScreenOfDisplay (dpy, s);
+ if (visual == DefaultVisualOfScreen (screen))
+ return screen;
+
+ for (d = 0; d < screen->ndepths; d++) {
+ Depth *depth;
+
+ depth = &screen->depths[d];
+ for (v = 0; v < depth->nvisuals; v++)
+ if (visual == &depth->visuals[v])
+ return screen;
+ }
+ }
+
+ return NULL;
+}
+
+cairo_surface_t *
+cairo_xlib_surface_create (Display *dpy,
+ Drawable drawable,
+ Visual *visual,
+ int width,
+ int height)
+{
+ Screen *scr;
+ xcb_visualtype_t xcb_visual;
+
+ scr = _cairo_xlib_screen_from_visual (dpy, visual);
+ if (scr == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+ xcb_visual.visual_id = visual->visualid;
+#if defined(__cplusplus) || defined(c_plusplus)
+ xcb_visual._class = visual->c_class;
+#else
+ xcb_visual._class = visual->class;
+#endif
+ xcb_visual.bits_per_rgb_value = visual->bits_per_rgb;
+ xcb_visual.colormap_entries = visual->map_entries;
+ xcb_visual.red_mask = visual->red_mask;
+ xcb_visual.green_mask = visual->green_mask;
+ xcb_visual.blue_mask = visual->blue_mask;
+
+ return _cairo_xlib_xcb_surface_create (dpy, scr, visual, NULL,
+ cairo_xcb_surface_create (XGetXCBConnection (dpy),
+ drawable,
+ &xcb_visual,
+ width, height));
+}
+
+cairo_surface_t *
+cairo_xlib_surface_create_for_bitmap (Display *dpy,
+ Pixmap bitmap,
+ Screen *scr,
+ int width,
+ int height)
+{
+ return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, NULL,
+ cairo_xcb_surface_create_for_bitmap (XGetXCBConnection (dpy),
+ (xcb_screen_t *) scr,
+ bitmap,
+ width, height));
+}
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+static xcb_screen_t *
+_cairo_xcb_screen_from_root (xcb_connection_t *connection,
+ xcb_window_t id)
+{
+ xcb_screen_iterator_t s;
+
+ s = xcb_setup_roots_iterator (xcb_get_setup (connection));
+ for (; s.rem; xcb_screen_next (&s)) {
+ if (s.data->root == id)
+ return s.data;
+ }
+
+ return NULL;
+}
+cairo_surface_t *
+cairo_xlib_surface_create_with_xrender_format (Display *dpy,
+ Drawable drawable,
+ Screen *scr,
+ XRenderPictFormat *format,
+ int width,
+ int height)
+{
+ xcb_render_pictforminfo_t xcb_format;
+ xcb_connection_t *connection;
+ xcb_screen_t *screen;
+
+ xcb_format.id = format->id;
+ xcb_format.type = format->type;
+ xcb_format.depth = format->depth;
+ xcb_format.direct.red_shift = format->direct.red;
+ xcb_format.direct.red_mask = format->direct.redMask;
+ xcb_format.direct.green_shift = format->direct.green;
+ xcb_format.direct.green_mask = format->direct.greenMask;
+ xcb_format.direct.blue_shift = format->direct.blue;
+ xcb_format.direct.blue_mask = format->direct.blueMask;
+ xcb_format.direct.alpha_shift = format->direct.alpha;
+ xcb_format.direct.alpha_mask = format->direct.alphaMask;
+ xcb_format.colormap = format->colormap;
+
+ connection = XGetXCBConnection (dpy);
+ screen = _cairo_xcb_screen_from_root (connection, (xcb_window_t) scr->root);
+
+ return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, format,
+ cairo_xcb_surface_create_with_xrender_format (connection, screen,
+ drawable,
+ &xcb_format,
+ width, height));
+}
+
+XRenderPictFormat *
+cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface)
+{
+ cairo_xlib_xcb_surface_t *xlib_surface = (cairo_xlib_xcb_surface_t *) surface;
+
+ /* Throw an error for a non-xlib surface */
+ if (surface->type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return xlib_surface->format;
+}
+#endif
+
+void
+cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ status = _cairo_surface_set_error (abstract_surface,
+ CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return;
+ }
+
+ cairo_xcb_surface_set_size (&surface->xcb->base, width, height);
+ if (unlikely (surface->xcb->base.status)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (surface->xcb->base.status));
+ }
+}
+
+void
+cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface,
+ Drawable drawable,
+ int width,
+ int height)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *)abstract_surface;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ status = _cairo_surface_set_error (abstract_surface,
+ CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return;
+ }
+
+ cairo_xcb_surface_set_drawable (&surface->xcb->base, drawable, width, height);
+ if (unlikely (surface->xcb->base.status)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (surface->xcb->base.status));
+ }
+}
+
+Display *
+cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return surface->display;
+}
+
+Drawable
+cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+ return 0;
+ }
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+ /* This can happen when e.g. create_similar falls back to an image surface
+ * because we don't have the RENDER extension. */
+ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->xcb->drawable;
+}
+
+Screen *
+cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return surface->screen;
+}
+
+Visual *
+cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return NULL;
+ }
+
+ return surface->visual;
+}
+
+int
+cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+ return 0;
+ }
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+ /* This can happen when e.g. create_similar falls back to an image surface
+ * because we don't have the RENDER extension. */
+ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->xcb->depth;
+}
+
+int
+cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+ return 0;
+ }
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+ /* This can happen when e.g. create_similar falls back to an image surface
+ * because we don't have the RENDER extension. */
+ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->xcb->width;
+}
+
+int
+cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface)
+{
+ cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+ if (unlikely (abstract_surface->finished)) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+ return 0;
+ }
+ if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+ /* This can happen when e.g. create_similar falls back to an image surface
+ * because we don't have the RENDER extension. */
+ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->xcb->height;
+}
+
+void
+cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device,
+ int major, int minor)
+{
+ cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
+
+ if (device == NULL || device->status)
+ return;
+
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB)
+ return;
+
+ cairo_xcb_device_debug_cap_xrender_version (display->xcb_device,
+ major, minor);
+}
+
+void
+cairo_xlib_device_debug_set_precision (cairo_device_t *device,
+ int precision)
+{
+ cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
+
+ if (device == NULL || device->status)
+ return;
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return;
+ }
+
+ cairo_xcb_device_debug_set_precision (display->xcb_device, precision);
+}
+
+int
+cairo_xlib_device_debug_get_precision (cairo_device_t *device)
+{
+ cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
+
+ if (device == NULL || device->status)
+ return -1;
+ if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+ cairo_status_t status;
+
+ status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ (void) status;
+ return -1;
+ }
+
+ return cairo_xcb_device_debug_get_precision (display->xcb_device);
+}
+
+#endif /* CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-xrender-private.h b/src/cairo-xlib-xrender-private.h
new file mode 100755
index 000000000..bf3199c4c
--- /dev/null
+++ b/src/cairo-xlib-xrender-private.h
@@ -0,0 +1,1169 @@
+/* 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
+
+#if !HAVE_XRENDERCREATELINEARGRADIENT
+#define XRenderCreateLinearGradient _int_consume
+
+typedef struct _XLinearGradient {
+ XPointFixed p1;
+ XPointFixed p2;
+} XLinearGradient;
+#endif
+
+#if !HAVE_XRENDERCREATERADIALGRADIENT
+#define XRenderCreateRadialGradient _int_consume
+
+typedef struct _XCircle {
+ XFixed x;
+ XFixed y;
+ XFixed radius;
+} XCircle;
+typedef struct _XRadialGradient {
+ XCircle inner;
+ XCircle outer;
+} XRadialGradient;
+#endif
+
+#if !HAVE_XRENDERCREATECONICALGRADIENT
+#define XRenderCreateConicalGradient _int_consume
+
+typedef struct _XConicalGradient {
+ XPointFixed center;
+ XFixed angle; /* in degrees */
+} XConicalGradient;
+#endif
+
+
+#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/src/cairo-xlib-xrender.h b/src/cairo-xlib-xrender.h
new file mode 100755
index 000000000..b34b057de
--- /dev/null
+++ b/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/src/cairo-xlib.h b/src/cairo-xlib.h
new file mode 100755
index 000000000..ecf8d6c86
--- /dev/null
+++ b/src/cairo-xlib.h
@@ -0,0 +1,118 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <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);
+
+/* debug interface */
+
+cairo_public void
+cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device,
+ int major_version,
+ int minor_version);
+
+/*
+ * @precision: -1 implies automatically choose based on antialiasing mode,
+ * any other value overrides and sets the corresponding PolyMode.
+ */
+cairo_public void
+cairo_xlib_device_debug_set_precision (cairo_device_t *device,
+ int precision);
+
+cairo_public int
+cairo_xlib_device_debug_get_precision (cairo_device_t *device);
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_XLIB_SURFACE */
+# error Cairo was not compiled with support for the xlib backend
+#endif /* CAIRO_HAS_XLIB_SURFACE */
+
+#endif /* CAIRO_XLIB_H */
diff --git a/src/cairo-xml-surface.c b/src/cairo-xml-surface.c
new file mode 100755
index 000000000..777d47089
--- /dev/null
+++ b/src/cairo-xml-surface.c
@@ -0,0 +1,1143 @@
+/* -*- 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-default-context-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-recording-surface-inline.h"
+
+#define static cairo_warn static
+
+typedef struct _cairo_xml_surface cairo_xml_surface_t;
+
+typedef struct _cairo_xml {
+ cairo_device_t base;
+
+ cairo_output_stream_t *stream;
+ int indent;
+} cairo_xml_t;
+
+struct _cairo_xml_surface {
+ cairo_surface_t base;
+
+ double width, height;
+};
+
+slim_hidden_proto (cairo_xml_for_recording_surface);
+
+static const cairo_surface_backend_t _cairo_xml_surface_backend;
+
+static const char *
+_operator_to_string (cairo_operator_t op)
+{
+ static const char *names[] = {
+ "CLEAR", /* CAIRO_OPERATOR_CLEAR */
+
+ "SOURCE", /* CAIRO_OPERATOR_SOURCE */
+ "OVER", /* CAIRO_OPERATOR_OVER */
+ "IN", /* CAIRO_OPERATOR_IN */
+ "OUT", /* CAIRO_OPERATOR_OUT */
+ "ATOP", /* CAIRO_OPERATOR_ATOP */
+
+ "DEST", /* CAIRO_OPERATOR_DEST */
+ "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
+ "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
+ "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
+ "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
+
+ "XOR", /* CAIRO_OPERATOR_XOR */
+ "ADD", /* CAIRO_OPERATOR_ADD */
+ "SATURATE", /* CAIRO_OPERATOR_SATURATE */
+
+ "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
+ "SCREEN", /* CAIRO_OPERATOR_SCREEN */
+ "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
+ "DARKEN", /* CAIRO_OPERATOR_DARKEN */
+ "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
+ "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
+ "BURN", /* CAIRO_OPERATOR_COLOR_BURN */
+ "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
+ "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
+ "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
+ "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
+ "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
+ "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
+ "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
+ "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
+ };
+ assert (op < ARRAY_LENGTH (names));
+ return names[op];
+}
+
+static const char *
+_extend_to_string (cairo_extend_t extend)
+{
+ static const char *names[] = {
+ "EXTEND_NONE", /* CAIRO_EXTEND_NONE */
+ "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */
+ "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */
+ "EXTEND_PAD" /* CAIRO_EXTEND_PAD */
+ };
+ assert (extend < ARRAY_LENGTH (names));
+ return names[extend];
+}
+
+static const char *
+_filter_to_string (cairo_filter_t filter)
+{
+ static const char *names[] = {
+ "FILTER_FAST", /* CAIRO_FILTER_FAST */
+ "FILTER_GOOD", /* CAIRO_FILTER_GOOD */
+ "FILTER_BEST", /* CAIRO_FILTER_BEST */
+ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
+ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
+ "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
+ };
+ assert (filter < ARRAY_LENGTH (names));
+ return names[filter];
+}
+
+static const char *
+_fill_rule_to_string (cairo_fill_rule_t rule)
+{
+ static const char *names[] = {
+ "WINDING", /* CAIRO_FILL_RULE_WINDING */
+ "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
+ };
+ assert (rule < ARRAY_LENGTH (names));
+ return names[rule];
+}
+
+static const char *
+_antialias_to_string (cairo_antialias_t antialias)
+{
+ static const char *names[] = {
+ "DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
+ "NONE", /* CAIRO_ANTIALIAS_NONE */
+ "GRAY", /* CAIRO_ANTIALIAS_GRAY */
+ "SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */
+ "FAST", /* CAIRO_ANTIALIAS_FAST */
+ "GOOD", /* CAIRO_ANTIALIAS_GOOD */
+ "BEST", /* CAIRO_ANTIALIAS_BEST */
+ };
+ assert (antialias < ARRAY_LENGTH (names));
+ return names[antialias];
+}
+
+static const char *
+_line_cap_to_string (cairo_line_cap_t line_cap)
+{
+ static const char *names[] = {
+ "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
+ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
+ "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
+ };
+ assert (line_cap < ARRAY_LENGTH (names));
+ return names[line_cap];
+}
+
+static const char *
+_line_join_to_string (cairo_line_join_t line_join)
+{
+ static const char *names[] = {
+ "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
+ "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
+ "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
+ };
+ assert (line_join < ARRAY_LENGTH (names));
+ return names[line_join];
+}
+
+static const char *
+_content_to_string (cairo_content_t content)
+{
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA: return "ALPHA";
+ case CAIRO_CONTENT_COLOR: return "COLOR";
+ default:
+ case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
+ }
+}
+
+static const char *
+_format_to_string (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32: return "ARGB32";
+ case CAIRO_FORMAT_RGB30: return "RGB30";
+ case CAIRO_FORMAT_RGB24: return "RGB24";
+ case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
+ case CAIRO_FORMAT_A8: return "A8";
+ case CAIRO_FORMAT_A1: return "A1";
+ case CAIRO_FORMAT_INVALID: return "INVALID";
+ }
+ ASSERT_NOT_REACHED;
+ return "INVALID";
+}
+
+static cairo_status_t
+_device_flush (void *abstract_device)
+{
+ cairo_xml_t *xml = abstract_device;
+ cairo_status_t status;
+
+ status = _cairo_output_stream_flush (xml->stream);
+
+ return status;
+}
+
+static void
+_device_destroy (void *abstract_device)
+{
+ cairo_xml_t *xml = abstract_device;
+ cairo_status_t status;
+
+ status = _cairo_output_stream_destroy (xml->stream);
+
+ free (xml);
+}
+
+static const cairo_device_backend_t _cairo_xml_device_backend = {
+ CAIRO_DEVICE_TYPE_XML,
+
+ NULL, NULL, /* lock, unlock */
+
+ _device_flush,
+ NULL, /* finish */
+ _device_destroy
+};
+
+static cairo_device_t *
+_cairo_xml_create_internal (cairo_output_stream_t *stream)
+{
+ cairo_xml_t *xml;
+
+ xml = malloc (sizeof (cairo_xml_t));
+ if (unlikely (xml == NULL))
+ return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ memset (xml, 0, sizeof (cairo_xml_t));
+
+ _cairo_device_init (&xml->base, &_cairo_xml_device_backend);
+
+ xml->indent = 0;
+ xml->stream = stream;
+
+ return &xml->base;
+}
+
+static void
+_cairo_xml_indent (cairo_xml_t *xml, int indent)
+{
+ xml->indent += indent;
+ assert (xml->indent >= 0);
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...)
+{
+ va_list ap;
+ char indent[80];
+ int len;
+
+ len = MIN (xml->indent, ARRAY_LENGTH (indent));
+ memset (indent, ' ', len);
+ _cairo_output_stream_write (xml->stream, indent, len);
+
+ va_start (ap, fmt);
+ _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+ va_end (ap);
+
+ _cairo_output_stream_write (xml->stream, "\n", 1);
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...)
+{
+ char indent[80];
+ int len;
+
+ len = MIN (xml->indent, ARRAY_LENGTH (indent));
+ memset (indent, ' ', len);
+ _cairo_output_stream_write (xml->stream, indent, len);
+
+ if (fmt != NULL) {
+ va_list ap;
+
+ va_start (ap, fmt);
+ _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+ va_end (ap);
+ }
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+ va_end (ap);
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ va_start (ap, fmt);
+ _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+ va_end (ap);
+ }
+
+ _cairo_output_stream_write (xml->stream, "\n", 1);
+}
+
+static cairo_surface_t *
+_cairo_xml_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_rectangle_t extents;
+
+ extents.x = extents.y = 0;
+ extents.width = width;
+ extents.height = height;
+
+ return cairo_recording_surface_create (content, &extents);
+}
+
+static cairo_bool_t
+_cairo_xml_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+
+ if (surface->width < 0 || surface->height < 0)
+ return FALSE;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_xml_move_to (void *closure,
+ const cairo_point_t *p1)
+{
+ _cairo_xml_printf_continue (closure, " %f %f m",
+ _cairo_fixed_to_double (p1->x),
+ _cairo_fixed_to_double (p1->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_line_to (void *closure,
+ const cairo_point_t *p1)
+{
+ _cairo_xml_printf_continue (closure, " %f %f l",
+ _cairo_fixed_to_double (p1->x),
+ _cairo_fixed_to_double (p1->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_curve_to (void *closure,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ const cairo_point_t *p3)
+{
+ _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c",
+ _cairo_fixed_to_double (p1->x),
+ _cairo_fixed_to_double (p1->y),
+ _cairo_fixed_to_double (p2->x),
+ _cairo_fixed_to_double (p2->y),
+ _cairo_fixed_to_double (p3->x),
+ _cairo_fixed_to_double (p3->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_close_path (void *closure)
+{
+ _cairo_xml_printf_continue (closure, " h");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xml_emit_path (cairo_xml_t *xml,
+ const cairo_path_fixed_t *path)
+{
+ cairo_status_t status;
+
+ _cairo_xml_printf_start (xml, "<path>");
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_xml_move_to,
+ _cairo_xml_line_to,
+ _cairo_xml_curve_to,
+ _cairo_xml_close_path,
+ xml);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ _cairo_xml_printf_end (xml, "</path>");
+}
+
+static void
+_cairo_xml_emit_string (cairo_xml_t *xml,
+ const char *node,
+ const char *data)
+{
+ _cairo_xml_printf (xml, "<%s>%s</%s>", node, data, node);
+}
+
+static void
+_cairo_xml_emit_double (cairo_xml_t *xml,
+ const char *node,
+ double data)
+{
+ _cairo_xml_printf (xml, "<%s>%f</%s>", node, data, node);
+}
+
+static cairo_xml_t *
+to_xml (cairo_xml_surface_t *surface)
+{
+ return (cairo_xml_t *) surface->base.device;
+}
+
+static cairo_status_t
+_cairo_xml_surface_emit_clip_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,
+ const 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'>",
+ linear->pd1.x, linear->pd1.y,
+ linear->pd2.x, linear->pd2.y);
+ _cairo_xml_indent (xml, 2);
+ _cairo_xml_emit_gradient (xml, &linear->base);
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</linear>");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_radial (cairo_xml_t *xml,
+ const cairo_radial_pattern_t *radial)
+{
+ _cairo_xml_printf (xml,
+ "<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>",
+ radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius,
+ radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius);
+ _cairo_xml_indent (xml, 2);
+ _cairo_xml_emit_gradient (xml, &radial->base);
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</radial>");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_write_func (void *closure, const unsigned char *data, unsigned len)
+{
+ _cairo_output_stream_write (closure, data, len);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_image (cairo_xml_t *xml,
+ cairo_image_surface_t *image)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ _cairo_xml_printf_start (xml,
+ "<image width='%d' height='%d' format='%s'>",
+ image->width, image->height,
+ _format_to_string (image->format));
+
+ stream = _cairo_base64_stream_create (xml->stream);
+ status = cairo_surface_write_to_png_stream (&image->base,
+ _write_func, stream);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_output_stream_destroy (stream);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_printf_end (xml, "</image>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_surface (cairo_xml_t *xml,
+ const cairo_surface_pattern_t *pattern)
+{
+ cairo_surface_t *source = pattern->surface;
+ cairo_status_t status;
+
+ if (_cairo_surface_is_recording (source)) {
+ status = cairo_xml_for_recording_surface (&xml->base, source);
+ } else {
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ status = _cairo_surface_acquire_source_image (source,
+ &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_image (xml, image);
+
+ _cairo_surface_release_source_image (source, image, image_extra);
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xml_emit_pattern (cairo_xml_t *xml,
+ const char *source_or_mask,
+ const cairo_pattern_t *pattern)
+{
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask);
+ _cairo_xml_indent (xml, 2);
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ break;
+ }
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
+ _cairo_xml_emit_matrix (xml, &pattern->matrix);
+ _cairo_xml_printf (xml,
+ "<extend>%s</extend>",
+ _extend_to_string (pattern->extend));
+ _cairo_xml_printf (xml,
+ "<filter>%s</filter>",
+ _filter_to_string (pattern->filter));
+ }
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</%s-pattern>", source_or_mask);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<paint>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</paint>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<mask>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "mask", mask);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</mask>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<stroke>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+ _cairo_xml_emit_double (xml, "line-width", style->line_width);
+ _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit);
+ _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap));
+ _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ if (style->num_dashes) {
+ unsigned int i;
+
+ _cairo_xml_printf_start (xml, "<dash offset='%f'>",
+ style->dash_offset);
+ for (i = 0; i < style->num_dashes; i++)
+ _cairo_xml_printf_continue (xml, "%f ", style->dash[i]);
+
+ _cairo_xml_printf_end (xml, "</dash>");
+ }
+
+ _cairo_xml_emit_path (xml, path);
+ _cairo_xml_emit_double (xml, "tolerance", tolerance);
+ _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
+
+ _cairo_xml_emit_matrix (xml, ctm);
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</stroke>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t*path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<fill>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_emit_path (xml, path);
+ _cairo_xml_emit_double (xml, "tolerance", tolerance);
+ _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
+ _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule));
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</fill>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#if CAIRO_HAS_FT_FONT
+#include "cairo-ft-private.h"
+static cairo_status_t
+_cairo_xml_emit_type42_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font)
+{
+ const cairo_scaled_font_backend_t *backend;
+ cairo_output_stream_t *base64_stream;
+ cairo_output_stream_t *zlib_stream;
+ cairo_status_t status, status2;
+ unsigned long size;
+ uint32_t len;
+ uint8_t *buf;
+
+ backend = scaled_font->backend;
+ if (backend->load_truetype_table == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
+ if (unlikely (status))
+ return status;
+
+ buf = malloc (size);
+ if (unlikely (buf == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
+ if (unlikely (status)) {
+ free (buf);
+ return status;
+ }
+
+ _cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>",
+ _cairo_ft_scaled_font_get_load_flags (scaled_font));
+
+
+ base64_stream = _cairo_base64_stream_create (xml->stream);
+ len = size;
+ _cairo_output_stream_write (base64_stream, &len, sizeof (len));
+
+ zlib_stream = _cairo_deflate_stream_create (base64_stream);
+
+ _cairo_output_stream_write (zlib_stream, buf, size);
+ free (buf);
+
+ status2 = _cairo_output_stream_destroy (zlib_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ status2 = _cairo_output_stream_destroy (base64_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ _cairo_xml_printf_end (xml, "</font>");
+
+ return status;
+}
+#else
+static cairo_status_t
+_cairo_xml_emit_type42_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+#endif
+
+static cairo_status_t
+_cairo_xml_emit_type3_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ _cairo_xml_printf_start (xml, "<font type='3'>");
+ _cairo_xml_printf_end (xml, "</font>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_scaled_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ cairo_int_status_t status;
+
+ _cairo_xml_printf (xml, "<scaled-font>");
+ _cairo_xml_indent (xml, 2);
+
+ status = _cairo_xml_emit_type42_font (xml, scaled_font);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_xml_emit_type3_font (xml, scaled_font,
+ glyphs, num_glyphs);
+ }
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "<scaled-font>");
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+ int i;
+
+ _cairo_xml_printf (xml, "<glyphs>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < num_glyphs; i++) {
+ _cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>",
+ glyphs[i].index,
+ glyphs[i].x,
+ glyphs[i].y);
+ }
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</glyphs>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t
+_cairo_xml_surface_backend = {
+ CAIRO_SURFACE_TYPE_XML,
+ NULL,
+
+ _cairo_default_context_create,
+
+ _cairo_xml_surface_create_similar,
+ NULL, /* create_similar_image */
+ NULL, /* map_to_image */
+ NULL, /* unmap_image */
+
+ _cairo_surface_default_source,
+ NULL, /* acquire source image */
+ NULL, /* release source image */
+ NULL, /* snapshot */
+
+ NULL, /* copy page */
+ NULL, /* show page */
+
+ _cairo_xml_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_xml_surface_paint,
+ _cairo_xml_surface_mask,
+ _cairo_xml_surface_stroke,
+ _cairo_xml_surface_fill,
+ NULL, /* fill_stroke */
+ _cairo_xml_surface_glyphs,
+};
+
+static cairo_surface_t *
+_cairo_xml_surface_create_internal (cairo_device_t *device,
+ cairo_content_t content,
+ double width,
+ double height)
+{
+ cairo_xml_surface_t *surface;
+
+ surface = 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/src/cairo-xml.h b/src/cairo-xml.h
new file mode 100755
index 000000000..9ae76e90a
--- /dev/null
+++ b/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/src/cairo.c b/src/cairo.c
new file mode 100755
index 000000000..3a6607b1b
--- /dev/null
+++ b/src/cairo.c
@@ -0,0 +1,4127 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-private.h"
+
+#include "cairo-backend-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <assert.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 losing the current state.
+ * Use cairo_restore() to restore to the saved state.
+ **/
+
+/**
+ * SECTION:cairo-text
+ * @Title: text
+ * @Short_Description: Rendering text and glyphs
+ * @See_Also: #cairo_font_face_t, #cairo_scaled_font_t, cairo_text_path(),
+ * cairo_glyph_path()
+ *
+ * The functions with <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 DEFINE_NIL_CONTEXT(status) \
+ { \
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \
+ status, /* status */ \
+ { 0, 0, 0, NULL }, /* user_data */ \
+ NULL \
+ }
+
+static const cairo_t _cairo_nil[] = {
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_MEMORY),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_RESTORE),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_POP_GROUP),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_CURRENT_POINT),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MATRIX),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STATUS),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_NULL_POINTER),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRING),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_PATH_DATA),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_READ_ERROR),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_WRITE_ERROR),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_FINISHED),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_TYPE_MISMATCH),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_PATTERN_TYPE_MISMATCH),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CONTENT),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_FORMAT),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_VISUAL),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_FILE_NOT_FOUND),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DASH),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DSC_COMMENT),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_INDEX),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_TEMP_FILE_ERROR),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRIDE),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_FONT_TYPE_MISMATCH),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_IMMUTABLE),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_ERROR),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_NEGATIVE_COUNT),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CLUSTERS),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SLANT),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_WEIGHT),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SIZE),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_TYPE_MISMATCH),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_ERROR),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MESH_CONSTRUCTION),
+ DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED)
+};
+COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1);
+
+/**
+ * _cairo_set_error:
+ * @cr: a cairo context
+ * @status: a status value indicating an error
+ *
+ * Atomically sets cr->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS.
+ *
+ * All assignments of an error status to cr->status should happen
+ * through _cairo_set_error(). Note that due to the nature of the atomic
+ * operation, it is not safe to call this function on the nil objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ **/
+static void
+_cairo_set_error (cairo_t *cr, cairo_status_t status)
+{
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&cr->status, _cairo_error (status));
+}
+
+cairo_t *
+_cairo_create_in_error (cairo_status_t status)
+{
+ cairo_t *cr;
+
+ assert (status != CAIRO_STATUS_SUCCESS);
+
+ cr = (cairo_t *) &_cairo_nil[status - CAIRO_STATUS_NO_MEMORY];
+ assert (status == cr->status);
+
+ return cr;
+}
+
+/**
+ * cairo_create:
+ * @target: target surface for the context
+ *
+ * Creates a new #cairo_t with all graphics state parameters set to
+ * default values and with @target as a target surface. The target
+ * surface should be constructed with a backend-specific function such
+ * as cairo_image_surface_create() (or any other
+ * <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>
+ * variant).
+ *
+ * This function references @target, so you can immediately
+ * call cairo_surface_destroy() on it if you don't need to
+ * maintain a separate reference to it.
+ *
+ * Return value: a newly allocated #cairo_t with a reference
+ * count of 1. The initial reference count should be released
+ * with cairo_destroy() when you are done using the #cairo_t.
+ * This function never returns %NULL. If memory cannot be
+ * allocated, a special #cairo_t object will be returned on
+ * which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. If
+ * you attempt to target a surface which does not support
+ * writing (such as #cairo_mime_surface_t) then a
+ * %CAIRO_STATUS_WRITE_ERROR will be raised. You can use this
+ * object normally, but no drawing will be done.
+ *
+ * Since: 1.0
+ **/
+cairo_t *
+cairo_create (cairo_surface_t *target)
+{
+ if (unlikely (target == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+ if (unlikely (target->status))
+ return _cairo_create_in_error (target->status);
+
+ if (target->backend->create_context == NULL)
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+ return target->backend->create_context (target);
+
+}
+slim_hidden_def (cairo_create);
+
+void
+_cairo_init (cairo_t *cr,
+ const cairo_backend_t *backend)
+{
+ CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1);
+ cr->status = CAIRO_STATUS_SUCCESS;
+ _cairo_user_data_array_init (&cr->user_data);
+
+ cr->backend = backend;
+}
+
+/**
+ * cairo_reference:
+ * @cr: a #cairo_t
+ *
+ * Increases the reference count on @cr by one. This prevents
+ * @cr from being destroyed until a matching call to cairo_destroy()
+ * is made.
+ *
+ * The number of references to a #cairo_t can be get using
+ * cairo_get_reference_count().
+ *
+ * Return value: the referenced #cairo_t.
+ *
+ * Since: 1.0
+ **/
+cairo_t *
+cairo_reference (cairo_t *cr)
+{
+ if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+ return cr;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count));
+
+ _cairo_reference_count_inc (&cr->ref_count);
+
+ return cr;
+}
+
+void
+_cairo_fini (cairo_t *cr)
+{
+ _cairo_user_data_array_fini (&cr->user_data);
+}
+
+/**
+ * cairo_destroy:
+ * @cr: a #cairo_t
+ *
+ * Decreases the reference count on @cr by one. If the result
+ * is zero, then @cr and all associated resources are freed.
+ * See cairo_reference().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_destroy (cairo_t *cr)
+{
+ if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+ return;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&cr->ref_count))
+ return;
+
+ cr->backend->destroy (cr);
+}
+slim_hidden_def (cairo_destroy);
+
+/**
+ * cairo_get_user_data:
+ * @cr: a #cairo_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @cr using the specified
+ * key. If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.4
+ **/
+void *
+cairo_get_user_data (cairo_t *cr,
+ const cairo_user_data_key_t *key)
+{
+ return _cairo_user_data_array_get_data (&cr->user_data, key);
+}
+
+/**
+ * cairo_set_user_data:
+ * @cr: a #cairo_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the #cairo_t
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * #cairo_t is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @cr. To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_set_user_data (cairo_t *cr,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy)
+{
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+ return cr->status;
+
+ return _cairo_user_data_array_set_data (&cr->user_data,
+ key, user_data, destroy);
+}
+
+/**
+ * cairo_get_reference_count:
+ * @cr: a #cairo_t
+ *
+ * Returns the current reference count of @cr.
+ *
+ * Return value: the current reference count of @cr. If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_get_reference_count (cairo_t *cr)
+{
+ if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+ return 0;
+
+ return CAIRO_REFERENCE_COUNT_GET_VALUE (&cr->ref_count);
+}
+
+/**
+ * cairo_save:
+ * @cr: a #cairo_t
+ *
+ * Makes a copy of the current state of @cr and saves it
+ * on an internal stack of saved states for @cr. When
+ * cairo_restore() is called, @cr will be restored to
+ * the saved state. Multiple calls to cairo_save() and
+ * cairo_restore() can be nested; each call to cairo_restore()
+ * restores the state from the matching paired cairo_save().
+ *
+ * It isn't necessary to clear all saved states before
+ * a #cairo_t is freed. If the reference count of a #cairo_t
+ * drops to zero in response to a call to cairo_destroy(),
+ * any saved states will be freed along with the #cairo_t.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_save (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->save (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_save);
+
+/**
+ * cairo_restore:
+ * @cr: a #cairo_t
+ *
+ * Restores @cr to the state saved by a preceding call to
+ * cairo_save() and removes that state from the stack of
+ * saved states.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_restore (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->restore (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_restore);
+
+/**
+ * cairo_push_group:
+ * @cr: a cairo context
+ *
+ * Temporarily redirects drawing to an intermediate surface known as a
+ * group. The redirection lasts until the group is completed by a call
+ * to cairo_pop_group() or cairo_pop_group_to_source(). These calls
+ * provide the result of any drawing to the group as a pattern,
+ * (either as an explicit object, or set as the source pattern).
+ *
+ * This group functionality can be convenient for performing
+ * intermediate compositing. One common use of a group is to render
+ * objects as opaque within the group, (so that they occlude each
+ * other), and then blend the result with translucence onto the
+ * destination.
+ *
+ * Groups can be nested arbitrarily deep by making balanced calls to
+ * cairo_push_group()/cairo_pop_group(). Each call pushes/pops the new
+ * target group onto/from a stack.
+ *
+ * The cairo_push_group() function calls cairo_save() so that any
+ * changes to the graphics state will not be visible outside the
+ * group, (the pop_group functions call cairo_restore()).
+ *
+ * By default the intermediate group will have a content type of
+ * %CAIRO_CONTENT_COLOR_ALPHA. Other content types can be chosen for
+ * the group by using cairo_push_group_with_content() instead.
+ *
+ * As an example, here is how one might fill and stroke a path with
+ * translucence, but without any portion of the fill being visible
+ * under the stroke:
+ *
+ * <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_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->push_group (cr, content);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_push_group_with_content);
+
+/**
+ * cairo_pop_group:
+ * @cr: a cairo context
+ *
+ * Terminates the redirection begun by a call to cairo_push_group() or
+ * cairo_push_group_with_content() and returns a new pattern
+ * containing the results of all drawing operations performed to the
+ * group.
+ *
+ * The cairo_pop_group() function calls cairo_restore(), (balancing a
+ * call to cairo_save() by the push_group function), so that any
+ * changes to the graphics state will not be visible outside the
+ * group.
+ *
+ * Return value: a newly created (surface) pattern containing the
+ * results of all drawing operations performed to the group. The
+ * caller owns the returned object and should call
+ * cairo_pattern_destroy() when finished with it.
+ *
+ * Since: 1.2
+ **/
+cairo_pattern_t *
+cairo_pop_group (cairo_t *cr)
+{
+ cairo_pattern_t *group_pattern;
+
+ if (unlikely (cr->status))
+ return _cairo_pattern_create_in_error (cr->status);
+
+ group_pattern = cr->backend->pop_group (cr);
+ if (unlikely (group_pattern->status))
+ _cairo_set_error (cr, group_pattern->status);
+
+ return group_pattern;
+}
+slim_hidden_def(cairo_pop_group);
+
+/**
+ * cairo_pop_group_to_source:
+ * @cr: a cairo context
+ *
+ * Terminates the redirection begun by a call to cairo_push_group() or
+ * cairo_push_group_with_content() and installs the resulting pattern
+ * as the source pattern in the given cairo context.
+ *
+ * The behavior of this function is equivalent to the sequence of
+ * operations:
+ *
+ * <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.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_operator (cairo_t *cr, cairo_operator_t op)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_operator (cr, op);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_operator);
+
+
+#if 0
+/**
+ * cairo_set_opacity:
+ * @cr: a #cairo_t
+ * @opacity: the level of opacity to use when compositing
+ *
+ * Sets the compositing opacity to be used for all drawing
+ * operations. The effect is to fade out the operations
+ * using the alpha value.
+ *
+ * The default opacity is 1.
+ *
+ * Since: TBD
+ **/
+void
+cairo_set_opacity (cairo_t *cr, double opacity)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_opacity (cr, opacity);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+#endif
+
+/**
+ * cairo_set_source_rgb:
+ * @cr: a cairo context
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ *
+ * Sets the source pattern within @cr to an opaque color. This opaque
+ * color will then be used for any subsequent drawing operation until
+ * a new source pattern is set.
+ *
+ * The color components are floating point numbers in the range 0 to
+ * 1. If the values passed in are outside that range, they will be
+ * clamped.
+ *
+ * The default source pattern is opaque black, (that is, it is
+ * equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, 0.0)).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_source_rgba (cr, red, green, blue, 1.);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_source_rgb);
+
+/**
+ * cairo_set_source_rgba:
+ * @cr: a cairo context
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ * @alpha: alpha component of color
+ *
+ * Sets the source pattern within @cr to a translucent color. This
+ * color will then be used for any subsequent drawing operation until
+ * a new source pattern is set.
+ *
+ * The color and alpha components are floating point numbers in the
+ * range 0 to 1. If the values passed in are outside that range, they
+ * will be clamped.
+ *
+ * The default source pattern is opaque black, (that is, it is
+ * equivalent to cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0)).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_source_rgba (cairo_t *cr,
+ double red, double green, double blue,
+ double alpha)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_source_rgba (cr, red, green, blue, alpha);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_source_surface:
+ * @cr: a cairo context
+ * @surface: a surface to be used to set the source pattern
+ * @x: User-space X coordinate for surface origin
+ * @y: User-space Y coordinate for surface origin
+ *
+ * This is a convenience function for creating a pattern from @surface
+ * and setting it as the source in @cr with cairo_set_source().
+ *
+ * The @x and @y parameters give the user-space coordinate at which
+ * the surface origin should appear. (The surface origin is its
+ * upper-left corner before any transformation has been applied.) The
+ * @x and @y parameters are negated and then set as translation values
+ * in the pattern matrix.
+ *
+ * Other than the initial translation pattern matrix, as described
+ * above, all other pattern attributes, (such as its extend mode), are
+ * set to the default values as in cairo_pattern_create_for_surface().
+ * The resulting pattern can be queried with cairo_get_source() so
+ * that these attributes can be modified if desired, (eg. to create a
+ * repeating pattern with cairo_pattern_set_extend()).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_source_surface (cairo_t *cr,
+ cairo_surface_t *surface,
+ double x,
+ double y)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (unlikely (surface == NULL)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ status = cr->backend->set_source_surface (cr, surface, x, y);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_source_surface);
+
+/**
+ * cairo_set_source:
+ * @cr: a cairo context
+ * @source: a #cairo_pattern_t to be used as the source for
+ * subsequent drawing operations.
+ *
+ * Sets the source pattern within @cr to @source. This pattern
+ * will then be used for any subsequent drawing operation until a new
+ * source pattern is set.
+ *
+ * Note: The pattern's transformation matrix will be locked to the
+ * user space in effect at the time of cairo_set_source(). This means
+ * that further modifications of the current transformation matrix
+ * will not affect the source pattern. See cairo_pattern_set_matrix().
+ *
+ * The default source pattern is a solid pattern that is opaque black,
+ * (that is, it is equivalent to cairo_set_source_rgb(cr, 0.0, 0.0,
+ * 0.0)).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_source (cairo_t *cr, cairo_pattern_t *source)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (unlikely (source == NULL)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ if (unlikely (source->status)) {
+ _cairo_set_error (cr, source->status);
+ return;
+ }
+
+ status = cr->backend->set_source (cr, source);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_source);
+
+/**
+ * cairo_get_source:
+ * @cr: a cairo context
+ *
+ * Gets the current source pattern for @cr.
+ *
+ * Return value: the current source pattern. This object is owned by
+ * cairo. To keep a reference to it, you must call
+ * cairo_pattern_reference().
+ *
+ * Since: 1.0
+ **/
+cairo_pattern_t *
+cairo_get_source (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_pattern_create_in_error (cr->status);
+
+ return cr->backend->get_source (cr);
+}
+
+/**
+ * cairo_set_tolerance:
+ * @cr: a #cairo_t
+ * @tolerance: the tolerance, in device units (typically pixels)
+ *
+ * Sets the tolerance used when converting paths into trapezoids.
+ * Curved segments of the path will be subdivided until the maximum
+ * deviation between the original path and the polygonal approximation
+ * is less than @tolerance. The default value is 0.1. A larger
+ * value will give better performance, a smaller value, better
+ * appearance. (Reducing the value from the default value of 0.1
+ * is unlikely to improve appearance significantly.) The accuracy of paths
+ * within Cairo is limited by the precision of its internal arithmetic, and
+ * the prescribed @tolerance is restricted to the smallest
+ * representable internal value.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_tolerance (cairo_t *cr, double tolerance)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_tolerance (cr, tolerance);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_tolerance);
+
+/**
+ * cairo_set_antialias:
+ * @cr: a #cairo_t
+ * @antialias: the new antialiasing mode
+ *
+ * Set the antialiasing mode of the rasterizer used for drawing shapes.
+ * This value is a hint, and a particular backend may or may not support
+ * a particular value. At the current time, no backend supports
+ * %CAIRO_ANTIALIAS_SUBPIXEL when drawing shapes.
+ *
+ * Note that this option does not affect text rendering, instead see
+ * cairo_font_options_set_antialias().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_antialias (cr, antialias);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_fill_rule:
+ * @cr: a #cairo_t
+ * @fill_rule: a fill rule, specified as a #cairo_fill_rule_t
+ *
+ * Set the current fill rule within the cairo context. The fill rule
+ * is used to determine which regions are inside or outside a complex
+ * (potentially self-intersecting) path. The current fill rule affects
+ * both cairo_fill() and cairo_clip(). See #cairo_fill_rule_t for details
+ * on the semantics of each available fill rule.
+ *
+ * The default fill rule is %CAIRO_FILL_RULE_WINDING.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_fill_rule (cr, fill_rule);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_line_width:
+ * @cr: a #cairo_t
+ * @width: a line width
+ *
+ * Sets the current line width within the cairo context. The line
+ * width value specifies the diameter of a pen that is circular in
+ * user space, (though device-space pen may be an ellipse in general
+ * due to scaling/shear/rotation of the CTM).
+ *
+ * Note: When the description above refers to user space and CTM it
+ * refers to the user space and CTM in effect at the time of the
+ * stroking operation, not the user space and CTM in effect at the
+ * time of the call to cairo_set_line_width(). The simplest usage
+ * makes both of these spaces identical. That is, if there is no
+ * change to the CTM between a call to cairo_set_line_width() and the
+ * stroking operation, then one can just pass user-space values to
+ * cairo_set_line_width() and ignore this note.
+ *
+ * As with the other stroke parameters, the current line width is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default line width value is 2.0.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_line_width (cairo_t *cr, double width)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (width < 0.)
+ width = 0.;
+
+ status = cr->backend->set_line_width (cr, width);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_line_width);
+
+/**
+ * cairo_set_line_cap:
+ * @cr: a cairo context
+ * @line_cap: a line cap style
+ *
+ * Sets the current line cap style within the cairo context. See
+ * #cairo_line_cap_t for details about how the available line cap
+ * styles are drawn.
+ *
+ * As with the other stroke parameters, the current line cap style is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default line cap style is %CAIRO_LINE_CAP_BUTT.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_line_cap (cr, line_cap);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_line_cap);
+
+/**
+ * cairo_set_line_join:
+ * @cr: a cairo context
+ * @line_join: a line join style
+ *
+ * Sets the current line join style within the cairo context. See
+ * #cairo_line_join_t for details about how the available line join
+ * styles are drawn.
+ *
+ * As with the other stroke parameters, the current line join style is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default line join style is %CAIRO_LINE_JOIN_MITER.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_line_join (cr, line_join);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_line_join);
+
+/**
+ * cairo_set_dash:
+ * @cr: a cairo context
+ * @dashes: an array specifying alternate lengths of on and off stroke portions
+ * @num_dashes: the length of the dashes array
+ * @offset: an offset into the dash pattern at which the stroke should start
+ *
+ * Sets the dash pattern to be used by cairo_stroke(). A dash pattern
+ * is specified by @dashes, an array of positive values. Each value
+ * provides the length of alternate "on" and "off" portions of the
+ * stroke. The @offset specifies an offset into the pattern at which
+ * the stroke begins.
+ *
+ * Each "on" segment will have caps applied as if the segment were a
+ * separate sub-path. In particular, it is valid to use an "on" length
+ * of 0.0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order
+ * to distributed dots or squares along a path.
+ *
+ * Note: The length values are in user-space units as evaluated at the
+ * time of stroking. This is not necessarily the same as the user
+ * space at the time of cairo_set_dash().
+ *
+ * If @num_dashes is 0 dashing is disabled.
+ *
+ * If @num_dashes is 1 a symmetric pattern is assumed with alternating
+ * on and off portions of the size specified by the single value in
+ * @dashes.
+ *
+ * If any value in @dashes is negative, or if all values are 0, then
+ * @cr will be put into an error state with a status of
+ * %CAIRO_STATUS_INVALID_DASH.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_dash (cairo_t *cr,
+ const double *dashes,
+ int num_dashes,
+ double offset)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_dash (cr, dashes, num_dashes, offset);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_dash_count:
+ * @cr: a #cairo_t
+ *
+ * This function returns the length of the dash array in @cr (0 if dashing
+ * is not currently in effect).
+ *
+ * See also cairo_set_dash() and cairo_get_dash().
+ *
+ * Return value: the length of the dash array, or 0 if no dash array set.
+ *
+ * Since: 1.4
+ **/
+int
+cairo_get_dash_count (cairo_t *cr)
+{
+ int num_dashes;
+
+ if (unlikely (cr->status))
+ return 0;
+
+ cr->backend->get_dash (cr, NULL, &num_dashes, NULL);
+
+ return num_dashes;
+}
+
+/**
+ * cairo_get_dash:
+ * @cr: a #cairo_t
+ * @dashes: return value for the dash array, or %NULL
+ * @offset: return value for the current dash offset, or %NULL
+ *
+ * Gets the current dash array. If not %NULL, @dashes should be big
+ * enough to hold at least the number of values returned by
+ * cairo_get_dash_count().
+ *
+ * Since: 1.4
+ **/
+void
+cairo_get_dash (cairo_t *cr,
+ double *dashes,
+ double *offset)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cr->backend->get_dash (cr, dashes, NULL, offset);
+}
+
+/**
+ * cairo_set_miter_limit:
+ * @cr: a cairo context
+ * @limit: miter limit to set
+ *
+ * Sets the current miter limit within the cairo context.
+ *
+ * If the current line join style is set to %CAIRO_LINE_JOIN_MITER
+ * (see cairo_set_line_join()), the miter limit is used to determine
+ * whether the lines should be joined with a bevel instead of a miter.
+ * Cairo divides the length of the miter by the line width.
+ * If the result is greater than the miter limit, the style is
+ * converted to a bevel.
+ *
+ * As with the other stroke parameters, the current line miter limit is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default miter limit value is 10.0, which will convert joins
+ * with interior angles less than 11 degrees to bevels instead of
+ * miters. For reference, a miter limit of 2.0 makes the miter cutoff
+ * at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90
+ * degrees.
+ *
+ * A miter limit for a desired angle can be computed as: miter limit =
+ * 1/sin(angle/2)
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_miter_limit (cairo_t *cr, double limit)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_miter_limit (cr, limit);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_translate:
+ * @cr: a cairo context
+ * @tx: amount to translate in the X direction
+ * @ty: amount to translate in the Y direction
+ *
+ * Modifies the current transformation matrix (CTM) by translating the
+ * user-space origin by (@tx, @ty). This offset is interpreted as a
+ * user-space coordinate according to the CTM in place before the new
+ * call to cairo_translate(). In other words, the translation of the
+ * user-space origin takes place after any existing transformation.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_translate (cairo_t *cr, double tx, double ty)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->translate (cr, tx, ty);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_translate);
+
+/**
+ * cairo_scale:
+ * @cr: a cairo context
+ * @sx: scale factor for the X dimension
+ * @sy: scale factor for the Y dimension
+ *
+ * Modifies the current transformation matrix (CTM) by scaling the X
+ * and Y user-space axes by @sx and @sy respectively. The scaling of
+ * the axes takes place after any existing transformation of user
+ * space.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_scale (cairo_t *cr, double sx, double sy)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->scale (cr, sx, sy);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_scale);
+
+/**
+ * cairo_rotate:
+ * @cr: a cairo context
+ * @angle: angle (in radians) by which the user-space axes will be
+ * rotated
+ *
+ * Modifies the current transformation matrix (CTM) by rotating the
+ * user-space axes by @angle radians. The rotation of the axes takes
+ * places after any existing transformation of user space. The
+ * rotation direction for positive angles is from the positive X axis
+ * toward the positive Y axis.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_rotate (cairo_t *cr, double angle)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->rotate (cr, angle);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_transform:
+ * @cr: a cairo context
+ * @matrix: a transformation to be applied to the user-space axes
+ *
+ * Modifies the current transformation matrix (CTM) by applying
+ * @matrix as an additional transformation. The new transformation of
+ * user space takes place after any existing transformation.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_transform (cairo_t *cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->transform (cr, matrix);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_transform);
+
+/**
+ * cairo_set_matrix:
+ * @cr: a cairo context
+ * @matrix: a transformation matrix from user space to device space
+ *
+ * Modifies the current transformation matrix (CTM) by setting it
+ * equal to @matrix.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_matrix (cairo_t *cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_matrix (cr, matrix);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_matrix);
+
+/**
+ * cairo_identity_matrix:
+ * @cr: a cairo context
+ *
+ * Resets the current transformation matrix (CTM) by setting it equal
+ * to the identity matrix. That is, the user-space and device-space
+ * axes will be aligned and one user-space unit will transform to one
+ * device-space unit.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_identity_matrix (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_identity_matrix (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_user_to_device:
+ * @cr: a cairo context
+ * @x: X value of coordinate (in/out parameter)
+ * @y: Y value of coordinate (in/out parameter)
+ *
+ * Transform a coordinate from user space to device space by
+ * multiplying the given point by the current transformation matrix
+ * (CTM).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_user_to_device (cairo_t *cr, double *x, double *y)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cr->backend->user_to_device (cr, x, y);
+}
+slim_hidden_def (cairo_user_to_device);
+
+/**
+ * cairo_user_to_device_distance:
+ * @cr: a cairo context
+ * @dx: X component of a distance vector (in/out parameter)
+ * @dy: Y component of a distance vector (in/out parameter)
+ *
+ * Transform a distance vector from user space to device space. This
+ * function is similar to cairo_user_to_device() except that the
+ * translation components of the CTM will be ignored when transforming
+ * (@dx,@dy).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cr->backend->user_to_device_distance (cr, dx, dy);
+}
+slim_hidden_def (cairo_user_to_device_distance);
+
+/**
+ * cairo_device_to_user:
+ * @cr: a cairo
+ * @x: X value of coordinate (in/out parameter)
+ * @y: Y value of coordinate (in/out parameter)
+ *
+ * Transform a coordinate from device space to user space by
+ * multiplying the given point by the inverse of the current
+ * transformation matrix (CTM).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_device_to_user (cairo_t *cr, double *x, double *y)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cr->backend->device_to_user (cr, x, y);
+}
+slim_hidden_def (cairo_device_to_user);
+
+/**
+ * cairo_device_to_user_distance:
+ * @cr: a cairo context
+ * @dx: X component of a distance vector (in/out parameter)
+ * @dy: Y component of a distance vector (in/out parameter)
+ *
+ * Transform a distance vector from device space to user space. This
+ * function is similar to cairo_device_to_user() except that the
+ * translation components of the inverse CTM will be ignored when
+ * transforming (@dx,@dy).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cr->backend->device_to_user_distance (cr, dx, dy);
+}
+
+/**
+ * cairo_new_path:
+ * @cr: a cairo context
+ *
+ * Clears the current path. After this call there will be no path and
+ * no current point.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_new_path (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->new_path (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_new_path);
+
+/**
+ * cairo_new_sub_path:
+ * @cr: a cairo context
+ *
+ * Begin a new sub-path. Note that the existing path is not
+ * affected. After this call there will be no current point.
+ *
+ * In many cases, this call is not needed since new sub-paths are
+ * frequently started with cairo_move_to().
+ *
+ * A call to cairo_new_sub_path() is particularly useful when
+ * beginning a new sub-path with one of the cairo_arc() calls. This
+ * makes things easier as it is no longer necessary to manually
+ * compute the arc's initial coordinates for a call to
+ * cairo_move_to().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_new_sub_path (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->new_sub_path (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_move_to:
+ * @cr: a cairo context
+ * @x: the X coordinate of the new position
+ * @y: the Y coordinate of the new position
+ *
+ * Begin a new sub-path. After this call the current point will be (@x,
+ * @y).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_move_to (cairo_t *cr, double x, double y)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->move_to (cr, x, y);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_move_to);
+
+
+/**
+ * cairo_line_to:
+ * @cr: a cairo context
+ * @x: the X coordinate of the end of the new line
+ * @y: the Y coordinate of the end of the new line
+ *
+ * Adds a line to the path from the current point to position (@x, @y)
+ * in user-space coordinates. After this call the current point
+ * will be (@x, @y).
+ *
+ * If there is no current point before the call to cairo_line_to()
+ * this function will behave as cairo_move_to(@cr, @x, @y).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_line_to (cairo_t *cr, double x, double y)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->line_to (cr, x, y);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_line_to);
+
+/**
+ * cairo_curve_to:
+ * @cr: a cairo context
+ * @x1: the X coordinate of the first control point
+ * @y1: the Y coordinate of the first control point
+ * @x2: the X coordinate of the second control point
+ * @y2: the Y coordinate of the second control point
+ * @x3: the X coordinate of the end of the curve
+ * @y3: the Y coordinate of the end of the curve
+ *
+ * Adds a cubic Bézier spline to the path from the current point to
+ * position (@x3, @y3) in user-space coordinates, using (@x1, @y1) and
+ * (@x2, @y2) as the control points. After this call the current point
+ * will be (@x3, @y3).
+ *
+ * If there is no current point before the call to cairo_curve_to()
+ * this function will behave as if preceded by a call to
+ * cairo_move_to(@cr, @x1, @y1).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_curve_to (cairo_t *cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->curve_to (cr,
+ x1, y1,
+ x2, y2,
+ x3, y3);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_curve_to);
+
+/**
+ * cairo_arc:
+ * @cr: a cairo context
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ *
+ * Adds a circular arc of the given @radius to the current path. The
+ * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in
+ * the direction of increasing angles to end at @angle2. If @angle2 is
+ * less than @angle1 it will be progressively increased by
+ * <literal>2*M_PI</literal> 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
+ * <literal>M_PI/2.0</literal> 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>
+ *
+ * Since: 1.0
+ **/
+void
+cairo_arc (cairo_t *cr,
+ double xc, double yc,
+ double radius,
+ double angle1, double angle2)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (angle2 < angle1) {
+ /* increase angle2 by multiples of full circle until it
+ * satisfies angle2 >= angle1 */
+ angle2 = fmod (angle2 - angle1, 2 * M_PI);
+ if (angle2 < 0)
+ angle2 += 2 * M_PI;
+ angle2 += angle1;
+ }
+
+ status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, TRUE);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_arc_negative:
+ * @cr: a cairo context
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ *
+ * Adds a circular arc of the given @radius to the current path. The
+ * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in
+ * the direction of decreasing angles to end at @angle2. If @angle2 is
+ * greater than @angle1 it will be progressively decreased by
+ * <literal>2*M_PI</literal> until it is less than @angle1.
+ *
+ * See cairo_arc() for more details. This function differs only in the
+ * direction of the arc between the two angles.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_arc_negative (cairo_t *cr,
+ double xc, double yc,
+ double radius,
+ double angle1, double angle2)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (angle2 > angle1) {
+ /* decrease angle2 by multiples of full circle until it
+ * satisfies angle2 <= angle1 */
+ angle2 = fmod (angle2 - angle1, 2 * M_PI);
+ if (angle2 > 0)
+ angle2 -= 2 * M_PI;
+ angle2 += angle1;
+ }
+
+ status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, FALSE);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/* XXX: NYI
+void
+cairo_arc_to (cairo_t *cr,
+ double x1, double y1,
+ double x2, double y2,
+ double radius)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->arc_to (cr, x1, y1, x2, y2, radius);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+void
+cairo_rel_arc_to (cairo_t *cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double radius)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->rel_arc_to (cr, dx1, dy1, dx2, dy2, radius);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+*/
+
+/**
+ * cairo_rel_move_to:
+ * @cr: a cairo context
+ * @dx: the X offset
+ * @dy: the Y offset
+ *
+ * Begin a new sub-path. After this call the current point will offset
+ * by (@x, @y).
+ *
+ * Given a current point of (x, y), cairo_rel_move_to(@cr, @dx, @dy)
+ * is logically equivalent to cairo_move_to(@cr, x + @dx, y + @dy).
+ *
+ * It is an error to call this function with no current point. Doing
+ * so will cause @cr to shutdown with a status of
+ * %CAIRO_STATUS_NO_CURRENT_POINT.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_rel_move_to (cairo_t *cr, double dx, double dy)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->rel_move_to (cr, dx, dy);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_rel_line_to:
+ * @cr: a cairo context
+ * @dx: the X offset to the end of the new line
+ * @dy: the Y offset to the end of the new line
+ *
+ * Relative-coordinate version of cairo_line_to(). Adds a line to the
+ * path from the current point to a point that is offset from the
+ * current point by (@dx, @dy) in user space. After this call the
+ * current point will be offset by (@dx, @dy).
+ *
+ * Given a current point of (x, y), cairo_rel_line_to(@cr, @dx, @dy)
+ * is logically equivalent to cairo_line_to(@cr, x + @dx, y + @dy).
+ *
+ * It is an error to call this function with no current point. Doing
+ * so will cause @cr to shutdown with a status of
+ * %CAIRO_STATUS_NO_CURRENT_POINT.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_rel_line_to (cairo_t *cr, double dx, double dy)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->rel_line_to (cr, dx, dy);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_rel_line_to);
+
+/**
+ * cairo_rel_curve_to:
+ * @cr: a cairo context
+ * @dx1: the X offset to the first control point
+ * @dy1: the Y offset to the first control point
+ * @dx2: the X offset to the second control point
+ * @dy2: the Y offset to the second control point
+ * @dx3: the X offset to the end of the curve
+ * @dy3: the Y offset to the end of the curve
+ *
+ * Relative-coordinate version of cairo_curve_to(). All offsets are
+ * relative to the current point. Adds a cubic Bézier spline to the
+ * path from the current point to a point offset from the current
+ * point by (@dx3, @dy3), using points offset by (@dx1, @dy1) and
+ * (@dx2, @dy2) as the control points. After this call the current
+ * point will be offset by (@dx3, @dy3).
+ *
+ * Given a current point of (x, y), cairo_rel_curve_to(@cr, @dx1,
+ * @dy1, @dx2, @dy2, @dx3, @dy3) is logically equivalent to
+ * cairo_curve_to(@cr, x+@dx1, y+@dy1, x+@dx2, y+@dy2, x+@dx3, y+@dy3).
+ *
+ * It is an error to call this function with no current point. Doing
+ * so will cause @cr to shutdown with a status of
+ * %CAIRO_STATUS_NO_CURRENT_POINT.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_rel_curve_to (cairo_t *cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->rel_curve_to (cr,
+ dx1, dy1,
+ dx2, dy2,
+ dx3, dy3);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_rectangle:
+ * @cr: a cairo context
+ * @x: the X coordinate of the top left corner of the rectangle
+ * @y: the Y coordinate to the top left corner of the rectangle
+ * @width: the width of the rectangle
+ * @height: the height of the rectangle
+ *
+ * Adds a closed sub-path rectangle of the given size to the current
+ * path at position (@x, @y) in user-space coordinates.
+ *
+ * This function is logically equivalent to:
+ * <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>
+ *
+ * Since: 1.0
+ **/
+void
+cairo_rectangle (cairo_t *cr,
+ double x, double y,
+ double width, double height)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->rectangle (cr, x, y, width, height);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+#if 0
+/* XXX: NYI */
+void
+cairo_stroke_to_path (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ /* The code in _cairo_recording_surface_get_path has a poorman's stroke_to_path */
+
+ status = _cairo_gstate_stroke_path (cr->gstate);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+#endif
+
+/**
+ * cairo_close_path:
+ * @cr: a cairo context
+ *
+ * Adds a line segment to the path from the current point to the
+ * beginning of the current sub-path, (the most recent point passed to
+ * cairo_move_to()), and closes this sub-path. After this call the
+ * current point will be at the joined endpoint of the sub-path.
+ *
+ * The behavior of cairo_close_path() is distinct from simply calling
+ * cairo_line_to() with the equivalent coordinate in the case of
+ * stroking. When a closed sub-path is stroked, there are no caps on
+ * the ends of the sub-path. Instead, there is a line join connecting
+ * the final and initial segments of the sub-path.
+ *
+ * If there is no current point before the call to cairo_close_path(),
+ * this function will have no effect.
+ *
+ * Note: As of cairo version 1.2.4 any call to cairo_close_path() will
+ * place an explicit MOVE_TO element into the path immediately after
+ * the CLOSE_PATH element, (which can be seen in cairo_copy_path() for
+ * example). This can simplify path processing in some cases as it may
+ * not be necessary to save the "last move_to point" during processing
+ * as the MOVE_TO immediately after the CLOSE_PATH will provide that
+ * point.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_close_path (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->close_path (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_close_path);
+
+/**
+ * cairo_path_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user-space coordinates covering the
+ * points on the current path. If the current path is empty, returns
+ * an empty rectangle ((0,0), (0,0)). Stroke parameters, fill rule,
+ * surface dimensions and clipping are not taken into account.
+ *
+ * Contrast with cairo_fill_extents() and cairo_stroke_extents() which
+ * return the extents of only the area that would be "inked" by
+ * the corresponding drawing operations.
+ *
+ * The result of cairo_path_extents() is defined as equivalent to the
+ * limit of cairo_stroke_extents() with %CAIRO_LINE_CAP_ROUND as the
+ * line width approaches 0.0, (but never reaching the empty-rectangle
+ * returned by cairo_stroke_extents() for a line width of 0.0).
+ *
+ * Specifically, this means that zero-area sub-paths such as
+ * cairo_move_to();cairo_line_to() segments, (even degenerate cases
+ * where the coordinates to both calls are identical), will be
+ * considered as contributing to the extents. However, a lone
+ * cairo_move_to() will not contribute to the results of
+ * cairo_path_extents().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_path_extents (cairo_t *cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ if (unlikely (cr->status)) {
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+
+ return;
+ }
+
+ cr->backend->path_extents (cr, x1, y1, x2, y2);
+}
+
+/**
+ * cairo_paint:
+ * @cr: a cairo context
+ *
+ * A drawing operator that paints the current source everywhere within
+ * the current clip region.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_paint (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->paint (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_paint);
+
+/**
+ * cairo_paint_with_alpha:
+ * @cr: a cairo context
+ * @alpha: alpha value, between 0 (transparent) and 1 (opaque)
+ *
+ * A drawing operator that paints the current source everywhere within
+ * the current clip region using a mask of constant alpha value
+ * @alpha. The effect is similar to cairo_paint(), but the drawing
+ * is faded out using the alpha value.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_paint_with_alpha (cairo_t *cr,
+ double alpha)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->paint_with_alpha (cr, alpha);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_mask:
+ * @cr: a cairo context
+ * @pattern: a #cairo_pattern_t
+ *
+ * A drawing operator that paints the current source
+ * using the alpha channel of @pattern as a mask. (Opaque
+ * areas of @pattern are painted with the source, transparent
+ * areas are not painted.)
+ *
+ * Since: 1.0
+ **/
+void
+cairo_mask (cairo_t *cr,
+ cairo_pattern_t *pattern)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (unlikely (pattern == NULL)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ if (unlikely (pattern->status)) {
+ _cairo_set_error (cr, pattern->status);
+ return;
+ }
+
+ status = cr->backend->mask (cr, pattern);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_mask);
+
+/**
+ * cairo_mask_surface:
+ * @cr: a cairo context
+ * @surface: a #cairo_surface_t
+ * @surface_x: X coordinate at which to place the origin of @surface
+ * @surface_y: Y coordinate at which to place the origin of @surface
+ *
+ * A drawing operator that paints the current source
+ * using the alpha channel of @surface as a mask. (Opaque
+ * areas of @surface are painted with the source, transparent
+ * areas are not painted.)
+ *
+ * Since: 1.0
+ **/
+void
+cairo_mask_surface (cairo_t *cr,
+ cairo_surface_t *surface,
+ double surface_x,
+ double surface_y)
+{
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+
+ if (unlikely (cr->status))
+ return;
+
+ pattern = cairo_pattern_create_for_surface (surface);
+
+ cairo_matrix_init_translate (&matrix, - surface_x, - surface_y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ cairo_mask (cr, pattern);
+
+ cairo_pattern_destroy (pattern);
+}
+
+/**
+ * cairo_stroke:
+ * @cr: a cairo context
+ *
+ * A drawing operator that strokes the current path according to the
+ * current line width, line join, line cap, and dash settings. After
+ * cairo_stroke(), the current path will be cleared from the cairo
+ * context. See cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ *
+ * Note: Degenerate segments and sub-paths are treated specially and
+ * provide a useful result. These can result in two different
+ * situations:
+ *
+ * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap
+ * style is %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE then these
+ * segments will be drawn as circular dots or squares respectively. In
+ * the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the squares
+ * is determined by the direction of the underlying path.
+ *
+ * 2. A sub-path created by cairo_move_to() followed by either a
+ * cairo_close_path() or one or more calls to cairo_line_to() to the
+ * same coordinate as the cairo_move_to(). If the cap style is
+ * %CAIRO_LINE_CAP_ROUND then these sub-paths will be drawn as circular
+ * dots. Note that in the case of %CAIRO_LINE_CAP_SQUARE a degenerate
+ * sub-path will not be drawn at all, (since the correct orientation
+ * is indeterminate).
+ *
+ * In no case will a cap style of %CAIRO_LINE_CAP_BUTT cause anything
+ * to be drawn in the case of either degenerate segments or sub-paths.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_stroke (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->stroke (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_stroke);
+
+/**
+ * cairo_stroke_preserve:
+ * @cr: a cairo context
+ *
+ * A drawing operator that strokes the current path according to the
+ * current line width, line join, line cap, and dash settings. Unlike
+ * cairo_stroke(), cairo_stroke_preserve() preserves the path within the
+ * cairo context.
+ *
+ * See cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_stroke_preserve (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->stroke_preserve (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_stroke_preserve);
+
+/**
+ * cairo_fill:
+ * @cr: a cairo context
+ *
+ * A drawing operator that fills the current path according to the
+ * current fill rule, (each sub-path is implicitly closed before being
+ * filled). After cairo_fill(), the current path will be cleared from
+ * the cairo context. See cairo_set_fill_rule() and
+ * cairo_fill_preserve().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_fill (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->fill (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_fill_preserve:
+ * @cr: a cairo context
+ *
+ * A drawing operator that fills the current path according to the
+ * current fill rule, (each sub-path is implicitly closed before being
+ * filled). Unlike cairo_fill(), cairo_fill_preserve() preserves the
+ * path within the cairo context.
+ *
+ * See cairo_set_fill_rule() and cairo_fill().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_fill_preserve (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->fill_preserve (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_fill_preserve);
+
+/**
+ * cairo_copy_page:
+ * @cr: a cairo context
+ *
+ * Emits the current page for backends that support multiple pages, but
+ * doesn't clear it, so, the contents of the current page will be retained
+ * for the next page too. Use cairo_show_page() if you want to get an
+ * empty page after the emission.
+ *
+ * This is a convenience function that simply calls
+ * cairo_surface_copy_page() on @cr's target.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_copy_page (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->copy_page (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_page:
+ * @cr: a cairo context
+ *
+ * Emits and clears the current page for backends that support multiple
+ * pages. Use cairo_copy_page() if you don't want to clear the page.
+ *
+ * This is a convenience function that simply calls
+ * cairo_surface_show_page() on @cr's target.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_show_page (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->show_page (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_in_stroke:
+ * @cr: a cairo context
+ * @x: X coordinate of the point to test
+ * @y: Y coordinate of the point to test
+ *
+ * Tests whether the given point is inside the area that would be
+ * affected by a cairo_stroke() operation given the current path and
+ * stroking parameters. Surface dimensions and clipping are not taken
+ * into account.
+ *
+ * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ *
+ * Return value: A non-zero value if the point is inside, or zero if
+ * outside.
+ *
+ * Since: 1.0
+ **/
+cairo_bool_t
+cairo_in_stroke (cairo_t *cr, double x, double y)
+{
+ cairo_status_t status;
+ cairo_bool_t inside = FALSE;
+
+ if (unlikely (cr->status))
+ return FALSE;
+
+ status = cr->backend->in_stroke (cr, x, y, &inside);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+
+ return inside;
+}
+
+/**
+ * cairo_in_fill:
+ * @cr: a cairo context
+ * @x: X coordinate of the point to test
+ * @y: Y coordinate of the point to test
+ *
+ * Tests whether the given point is inside the area that would be
+ * affected by a cairo_fill() operation given the current path and
+ * filling parameters. Surface dimensions and clipping are not taken
+ * into account.
+ *
+ * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve().
+ *
+ * Return value: A non-zero value if the point is inside, or zero if
+ * outside.
+ *
+ * Since: 1.0
+ **/
+cairo_bool_t
+cairo_in_fill (cairo_t *cr, double x, double y)
+{
+ cairo_status_t status;
+ cairo_bool_t inside = FALSE;
+
+ if (unlikely (cr->status))
+ return FALSE;
+
+ status = cr->backend->in_fill (cr, x, y, &inside);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+
+ return inside;
+}
+
+/**
+ * cairo_stroke_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area that
+ * would be affected, (the "inked" area), by a cairo_stroke()
+ * operation given the current path and stroke parameters.
+ * If the current path is empty, returns an empty rectangle ((0,0), (0,0)).
+ * Surface dimensions and clipping are not taken into account.
+ *
+ * Note that if the line width is set to exactly zero, then
+ * cairo_stroke_extents() will return an empty rectangle. Contrast with
+ * cairo_path_extents() which can be used to compute the non-empty
+ * bounds as the line width approaches zero.
+ *
+ * Note that cairo_stroke_extents() must necessarily do more work to
+ * compute the precise inked areas in light of the stroke parameters,
+ * so cairo_path_extents() may be more desirable for sake of
+ * performance if non-inked path extents are desired.
+ *
+ * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_stroke_extents (cairo_t *cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status)) {
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+
+ return;
+ }
+
+ status = cr->backend->stroke_extents (cr, x1, y1, x2, y2);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_fill_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area that
+ * would be affected, (the "inked" area), by a cairo_fill() operation
+ * given the current path and fill parameters. If the current path is
+ * empty, returns an empty rectangle ((0,0), (0,0)). Surface
+ * dimensions and clipping are not taken into account.
+ *
+ * Contrast with cairo_path_extents(), which is similar, but returns
+ * non-zero extents for some paths with no inked area, (such as a
+ * simple line segment).
+ *
+ * Note that cairo_fill_extents() must necessarily do more work to
+ * compute the precise inked areas in light of the fill rule, so
+ * cairo_path_extents() may be more desirable for sake of performance
+ * if the non-inked path extents are desired.
+ *
+ * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_fill_extents (cairo_t *cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status)) {
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+
+ return;
+ }
+
+ status = cr->backend->fill_extents (cr, x1, y1, x2, y2);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_clip:
+ * @cr: a cairo context
+ *
+ * Establishes a new clip region by intersecting the current clip
+ * region with the current path as it would be filled by cairo_fill()
+ * and according to the current fill rule (see cairo_set_fill_rule()).
+ *
+ * After cairo_clip(), the current path will be cleared from the cairo
+ * context.
+ *
+ * The current clip region affects all drawing operations by
+ * effectively masking out any changes to the surface that are outside
+ * the current clip region.
+ *
+ * Calling cairo_clip() can only make the clip region smaller, never
+ * larger. But the current clip is part of the graphics state, so a
+ * temporary restriction of the clip region can be achieved by
+ * calling cairo_clip() within a cairo_save()/cairo_restore()
+ * pair. The only other means of increasing the size of the clip
+ * region is cairo_reset_clip().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_clip (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->clip (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_clip_preserve:
+ * @cr: a cairo context
+ *
+ * Establishes a new clip region by intersecting the current clip
+ * region with the current path as it would be filled by cairo_fill()
+ * and according to the current fill rule (see cairo_set_fill_rule()).
+ *
+ * Unlike cairo_clip(), cairo_clip_preserve() preserves the path within
+ * the cairo context.
+ *
+ * The current clip region affects all drawing operations by
+ * effectively masking out any changes to the surface that are outside
+ * the current clip region.
+ *
+ * Calling cairo_clip_preserve() can only make the clip region smaller, never
+ * larger. But the current clip is part of the graphics state, so a
+ * temporary restriction of the clip region can be achieved by
+ * calling cairo_clip_preserve() within a cairo_save()/cairo_restore()
+ * pair. The only other means of increasing the size of the clip
+ * region is cairo_reset_clip().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_clip_preserve (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->clip_preserve (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_clip_preserve);
+
+/**
+ * cairo_reset_clip:
+ * @cr: a cairo context
+ *
+ * Reset the current clip region to its original, unrestricted
+ * state. That is, set the clip region to an infinitely large shape
+ * containing the target surface. Equivalently, if infinity is too
+ * hard to grasp, one can imagine the clip region being reset to the
+ * exact bounds of the target surface.
+ *
+ * Note that code meant to be reusable should not call
+ * cairo_reset_clip() as it will cause results unexpected by
+ * higher-level code which calls cairo_clip(). Consider using
+ * cairo_save() and cairo_restore() around cairo_clip() as a more
+ * robust means of temporarily restricting the clip region.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_reset_clip (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->reset_clip (cr);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_clip_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area inside the
+ * current clip.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_clip_extents (cairo_t *cr,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_status_t status;
+
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->clip_extents (cr, x1, y1, x2, y2);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_in_clip:
+ * @cr: a cairo context
+ * @x: X coordinate of the point to test
+ * @y: Y coordinate of the point to test
+ *
+ * Tests whether the given point is inside the area that would be
+ * visible through the current clip, i.e. the area that would be filled by
+ * a cairo_paint() operation.
+ *
+ * See cairo_clip(), and cairo_clip_preserve().
+ *
+ * Return value: A non-zero value if the point is inside, or zero if
+ * outside.
+ *
+ * Since: 1.10
+ **/
+cairo_bool_t
+cairo_in_clip (cairo_t *cr, double x, double y)
+{
+ cairo_status_t status;
+ cairo_bool_t inside = FALSE;
+
+ if (unlikely (cr->status))
+ return FALSE;
+
+ status = cr->backend->in_clip (cr, x, y, &inside);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+
+ return inside;
+}
+
+/**
+ * cairo_copy_clip_rectangle_list:
+ * @cr: a cairo context
+ *
+ * Gets the current clip region as a list of rectangles in user coordinates.
+ * Never returns %NULL.
+ *
+ * The status in the list may be %CAIRO_STATUS_CLIP_NOT_REPRESENTABLE to
+ * indicate that the clip region cannot be represented as a list of
+ * user-space rectangles. The status may have other values to indicate
+ * other errors.
+ *
+ * Returns: the current clip region as a list of rectangles in user coordinates,
+ * which should be destroyed using cairo_rectangle_list_destroy().
+ *
+ * Since: 1.4
+ **/
+cairo_rectangle_list_t *
+cairo_copy_clip_rectangle_list (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_rectangle_list_create_in_error (cr->status);
+
+ return cr->backend->clip_copy_rectangle_list (cr);
+}
+
+/**
+ * cairo_select_font_face:
+ * @cr: a #cairo_t
+ * @family: a font family name, encoded in UTF-8
+ * @slant: the slant for the font
+ * @weight: the weight for the font
+ *
+ * Note: The cairo_select_font_face() function call is part of what
+ * the cairo designers call the "toy" text API. It is convenient for
+ * short demos and simple programs, but it is not expected to be
+ * adequate for serious text-using applications.
+ *
+ * Selects a family and style of font from a simplified description as
+ * a family name, slant and weight. Cairo provides no operation to
+ * list available family names on the system (this is a "toy",
+ * remember), but the standard CSS2 generic family names, ("serif",
+ * "sans-serif", "cursive", "fantasy", "monospace"), are likely to
+ * work as expected.
+ *
+ * If @family starts with the string "@cairo:", or if no native font
+ * backends are compiled in, cairo will use an internal font family.
+ * The internal font family recognizes many modifiers in the @family
+ * string, most notably, it recognizes the string "monospace". That is,
+ * the family name "@cairo:monospace" will use the monospace version of
+ * the internal font family.
+ *
+ * For "real" font selection, see the font-backend-specific
+ * font_face_create functions for the font backend you are using. (For
+ * example, if you are using the freetype-based cairo-ft font backend,
+ * see cairo_ft_font_face_create_for_ft_face() or
+ * cairo_ft_font_face_create_for_pattern().) The resulting font face
+ * could then be used with cairo_scaled_font_create() and
+ * cairo_set_scaled_font().
+ *
+ * Similarly, when using the "real" font support, you can call
+ * directly into the underlying font system, (such as fontconfig or
+ * freetype), for operations such as listing available fonts, etc.
+ *
+ * It is expected that most applications will need to use a more
+ * comprehensive font handling and text layout library, (for example,
+ * pango), in conjunction with cairo.
+ *
+ * If text is drawn without a call to cairo_select_font_face(), (nor
+ * cairo_set_font_face() nor cairo_set_scaled_font()), the default
+ * family is platform-specific, but is essentially "sans-serif".
+ * Default slant is %CAIRO_FONT_SLANT_NORMAL, and default weight is
+ * %CAIRO_FONT_WEIGHT_NORMAL.
+ *
+ * This function is equivalent to a call to cairo_toy_font_face_create()
+ * followed by cairo_set_font_face().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_select_font_face (cairo_t *cr,
+ const char *family,
+ cairo_font_slant_t slant,
+ cairo_font_weight_t weight)
+{
+ cairo_font_face_t *font_face;
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ font_face = cairo_toy_font_face_create (family, slant, weight);
+ if (unlikely (font_face->status)) {
+ _cairo_set_error (cr, font_face->status);
+ return;
+ }
+
+ status = cr->backend->set_font_face (cr, font_face);
+ cairo_font_face_destroy (font_face);
+
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_font_extents:
+ * @cr: a #cairo_t
+ * @extents: a #cairo_font_extents_t object into which the results
+ * will be stored.
+ *
+ * Gets the font extents for the currently selected font.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_font_extents (cairo_t *cr,
+ cairo_font_extents_t *extents)
+{
+ cairo_status_t status;
+
+ extents->ascent = 0.0;
+ extents->descent = 0.0;
+ extents->height = 0.0;
+ extents->max_x_advance = 0.0;
+ extents->max_y_advance = 0.0;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->font_extents (cr, extents);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_font_face:
+ * @cr: a #cairo_t
+ * @font_face: a #cairo_font_face_t, or %NULL to restore to the default font
+ *
+ * Replaces the current #cairo_font_face_t object in the #cairo_t with
+ * @font_face. The replaced font face in the #cairo_t will be
+ * destroyed if there are no other references to it.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_font_face (cairo_t *cr,
+ cairo_font_face_t *font_face)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_font_face (cr, font_face);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_font_face:
+ * @cr: a #cairo_t
+ *
+ * Gets the current font face for a #cairo_t.
+ *
+ * Return value: the current font face. This object is owned by
+ * cairo. To keep a reference to it, you must call
+ * cairo_font_face_reference().
+ *
+ * This function never returns %NULL. If memory cannot be allocated, a
+ * special "nil" #cairo_font_face_t object will be returned on which
+ * cairo_font_face_status() returns %CAIRO_STATUS_NO_MEMORY. Using
+ * this nil object will cause its error state to propagate to other
+ * objects it is passed to, (for example, calling
+ * cairo_set_font_face() with a nil font will trigger an error that
+ * will shutdown the #cairo_t object).
+ *
+ * Since: 1.0
+ **/
+cairo_font_face_t *
+cairo_get_font_face (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return (cairo_font_face_t*) &_cairo_font_face_nil;
+
+ return cr->backend->get_font_face (cr);
+}
+
+/**
+ * cairo_set_font_size:
+ * @cr: a #cairo_t
+ * @size: the new font size, in user space units
+ *
+ * Sets the current font matrix to a scale by a factor of @size, replacing
+ * any font matrix previously set with cairo_set_font_size() or
+ * cairo_set_font_matrix(). This results in a font size of @size user space
+ * units. (More precisely, this matrix will result in the font's
+ * em-square being a @size by @size square in user space.)
+ *
+ * If text is drawn without a call to cairo_set_font_size(), (nor
+ * cairo_set_font_matrix() nor cairo_set_scaled_font()), the default
+ * font size is 10.0.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_font_size (cairo_t *cr, double size)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_font_size (cr, size);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_font_size);
+
+/**
+ * cairo_set_font_matrix:
+ * @cr: a #cairo_t
+ * @matrix: a #cairo_matrix_t describing a transform to be applied to
+ * the current font.
+ *
+ * Sets the current font matrix to @matrix. The font matrix gives a
+ * transformation from the design space of the font (in this space,
+ * the em-square is 1 unit by 1 unit) to user space. Normally, a
+ * simple scale is used (see cairo_set_font_size()), but a more
+ * complex font matrix can be used to shear the font
+ * or stretch it unequally along the two axes
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_font_matrix (cairo_t *cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_font_matrix (cr, matrix);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_font_matrix);
+
+/**
+ * cairo_get_font_matrix:
+ * @cr: a #cairo_t
+ * @matrix: return value for the matrix
+ *
+ * Stores the current font matrix into @matrix. See
+ * cairo_set_font_matrix().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix)
+{
+ if (unlikely (cr->status)) {
+ cairo_matrix_init_identity (matrix);
+ return;
+ }
+
+ cr->backend->get_font_matrix (cr, matrix);
+}
+
+/**
+ * cairo_set_font_options:
+ * @cr: a #cairo_t
+ * @options: font options to use
+ *
+ * Sets a set of custom font rendering options for the #cairo_t.
+ * Rendering options are derived by merging these options with the
+ * options derived from underlying surface; if the value in @options
+ * has a default value (like %CAIRO_ANTIALIAS_DEFAULT), then the value
+ * from the surface is used.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_set_font_options (cairo_t *cr,
+ const cairo_font_options_t *options)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cairo_font_options_status ((cairo_font_options_t *) options);
+ if (unlikely (status)) {
+ _cairo_set_error (cr, status);
+ return;
+ }
+
+ status = cr->backend->set_font_options (cr, options);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_font_options);
+
+/**
+ * cairo_get_font_options:
+ * @cr: a #cairo_t
+ * @options: a #cairo_font_options_t object into which to store
+ * the retrieved options. All existing values are overwritten
+ *
+ * Retrieves font rendering options set via #cairo_set_font_options.
+ * Note that the returned options do not include any options derived
+ * from the underlying surface; they are literally the options
+ * passed to cairo_set_font_options().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_get_font_options (cairo_t *cr,
+ cairo_font_options_t *options)
+{
+ /* check that we aren't trying to overwrite the nil object */
+ if (cairo_font_options_status (options))
+ return;
+
+ if (unlikely (cr->status)) {
+ _cairo_font_options_init_default (options);
+ return;
+ }
+
+ cr->backend->get_font_options (cr, options);
+}
+
+/**
+ * cairo_set_scaled_font:
+ * @cr: a #cairo_t
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Replaces the current font face, font matrix, and font options in
+ * the #cairo_t with those of the #cairo_scaled_font_t. Except for
+ * some translation, the current CTM of the #cairo_t should be the
+ * same as that of the #cairo_scaled_font_t, which can be accessed
+ * using cairo_scaled_font_get_ctm().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_set_scaled_font (cairo_t *cr,
+ const cairo_scaled_font_t *scaled_font)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if ((scaled_font == NULL)) {
+ _cairo_set_error (cr, _cairo_error (CAIRO_STATUS_NULL_POINTER));
+ return;
+ }
+
+ status = scaled_font->status;
+ if (unlikely (status)) {
+ _cairo_set_error (cr, status);
+ return;
+ }
+
+ status = cr->backend->set_scaled_font (cr, (cairo_scaled_font_t *) scaled_font);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_scaled_font:
+ * @cr: a #cairo_t
+ *
+ * Gets the current scaled font for a #cairo_t.
+ *
+ * Return value: the current scaled font. This object is owned by
+ * cairo. To keep a reference to it, you must call
+ * cairo_scaled_font_reference().
+ *
+ * This function never returns %NULL. If memory cannot be allocated, a
+ * special "nil" #cairo_scaled_font_t object will be returned on which
+ * cairo_scaled_font_status() returns %CAIRO_STATUS_NO_MEMORY. Using
+ * this nil object will cause its error state to propagate to other
+ * objects it is passed to, (for example, calling
+ * cairo_set_scaled_font() with a nil font will trigger an error that
+ * will shutdown the #cairo_t object).
+ *
+ * Since: 1.4
+ **/
+cairo_scaled_font_t *
+cairo_get_scaled_font (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_scaled_font_create_in_error (cr->status);
+
+ return cr->backend->get_scaled_font (cr);
+}
+slim_hidden_def (cairo_get_scaled_font);
+
+/**
+ * cairo_text_extents:
+ * @cr: a #cairo_t
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
+ * @extents: a #cairo_text_extents_t object into which the results
+ * will be stored
+ *
+ * Gets the extents for a string of text. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the text,
+ * (as it would be drawn by cairo_show_text()). Additionally, the
+ * x_advance and y_advance values indicate the amount by which the
+ * current point would be advanced by cairo_show_text().
+ *
+ * Note that whitespace characters do not directly contribute to the
+ * size of the rectangle (extents.width and extents.height). They do
+ * contribute indirectly by changing the position of non-whitespace
+ * characters. In particular, trailing whitespace characters are
+ * likely to not affect the size of the rectangle, though they will
+ * affect the x_advance and y_advance values.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_text_extents (cairo_t *cr,
+ const char *utf8,
+ cairo_text_extents_t *extents)
+{
+ cairo_status_t status;
+ cairo_scaled_font_t *scaled_font;
+ cairo_glyph_t *glyphs = NULL;
+ int num_glyphs = 0;
+ double x, y;
+
+ extents->x_bearing = 0.0;
+ extents->y_bearing = 0.0;
+ extents->width = 0.0;
+ extents->height = 0.0;
+ extents->x_advance = 0.0;
+ extents->y_advance = 0.0;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (utf8 == NULL)
+ return;
+
+ scaled_font = cairo_get_scaled_font (cr);
+ if (unlikely (scaled_font->status)) {
+ _cairo_set_error (cr, scaled_font->status);
+ return;
+ }
+
+ cairo_get_current_point (cr, &x, &y);
+ status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ x, y,
+ utf8, -1,
+ &glyphs, &num_glyphs,
+ NULL, NULL, NULL);
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = cr->backend->glyph_extents (cr,
+ glyphs, num_glyphs,
+ extents);
+ }
+ cairo_glyph_free (glyphs);
+
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_glyph_extents:
+ * @cr: a #cairo_t
+ * @glyphs: an array of #cairo_glyph_t objects
+ * @num_glyphs: the number of elements in @glyphs
+ * @extents: a #cairo_text_extents_t object into which the results
+ * will be stored
+ *
+ * Gets the extents for an array of glyphs. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the
+ * glyphs, (as they would be drawn by cairo_show_glyphs()).
+ * Additionally, the x_advance and y_advance values indicate the
+ * amount by which the current point would be advanced by
+ * cairo_show_glyphs().
+ *
+ * Note that whitespace glyphs do not contribute to the size of the
+ * rectangle (extents.width and extents.height).
+ *
+ * Since: 1.0
+ **/
+void
+cairo_glyph_extents (cairo_t *cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents)
+{
+ cairo_status_t status;
+
+ extents->x_bearing = 0.0;
+ extents->y_bearing = 0.0;
+ extents->width = 0.0;
+ extents->height = 0.0;
+ extents->x_advance = 0.0;
+ extents->y_advance = 0.0;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (num_glyphs == 0)
+ return;
+
+ if (unlikely (num_glyphs < 0)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
+ return;
+ }
+
+ if (unlikely (glyphs == NULL)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ status = cr->backend->glyph_extents (cr, glyphs, num_glyphs, extents);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_text:
+ * @cr: a cairo context
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
+ *
+ * A drawing operator that generates the shape from a string of UTF-8
+ * characters, rendered according to the current font_face, font_size
+ * (font_matrix), and font_options.
+ *
+ * This function first computes a set of glyphs for the string of
+ * text. The first glyph is placed so that its origin is at the
+ * current point. The origin of each subsequent glyph is offset from
+ * that of the previous glyph by the advance values of the previous
+ * glyph.
+ *
+ * After this call the current point is moved to the origin of where
+ * the next glyph would be placed in this same progression. That is,
+ * the current point will be at the origin of the final glyph offset
+ * by its advance values. This allows for easy display of a single
+ * logical string with multiple calls to cairo_show_text().
+ *
+ * Note: The cairo_show_text() function call is part of what the cairo
+ * designers call the "toy" text API. It is convenient for short demos
+ * and simple programs, but it is not expected to be adequate for
+ * serious text-using applications. See cairo_show_glyphs() for the
+ * "real" text display API in cairo.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_show_text (cairo_t *cr, const char *utf8)
+{
+ cairo_text_extents_t extents;
+ cairo_status_t status;
+ cairo_glyph_t *glyphs, *last_glyph;
+ cairo_text_cluster_t *clusters;
+ int utf8_len, num_glyphs, num_clusters;
+ cairo_text_cluster_flags_t cluster_flags;
+ double x, y;
+ cairo_bool_t has_show_text_glyphs;
+ cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+ cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
+ cairo_scaled_font_t *scaled_font;
+ cairo_glyph_text_info_t info, *i;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (utf8 == NULL)
+ return;
+
+ scaled_font = cairo_get_scaled_font (cr);
+ if (unlikely (scaled_font->status)) {
+ _cairo_set_error (cr, scaled_font->status);
+ return;
+ }
+
+ utf8_len = strlen (utf8);
+
+ has_show_text_glyphs =
+ cairo_surface_has_show_text_glyphs (cairo_get_target (cr));
+
+ glyphs = stack_glyphs;
+ num_glyphs = ARRAY_LENGTH (stack_glyphs);
+
+ if (has_show_text_glyphs) {
+ clusters = stack_clusters;
+ num_clusters = ARRAY_LENGTH (stack_clusters);
+ } else {
+ clusters = NULL;
+ num_clusters = 0;
+ }
+
+ cairo_get_current_point (cr, &x, &y);
+ status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ x, y,
+ utf8, utf8_len,
+ &glyphs, &num_glyphs,
+ has_show_text_glyphs ? &clusters : NULL, &num_clusters,
+ &cluster_flags);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (num_glyphs == 0)
+ return;
+
+ i = NULL;
+ if (has_show_text_glyphs) {
+ info.utf8 = utf8;
+ info.utf8_len = utf8_len;
+ info.clusters = clusters;
+ info.num_clusters = num_clusters;
+ info.cluster_flags = cluster_flags;
+ i = &info;
+ }
+
+ status = cr->backend->glyphs (cr, glyphs, num_glyphs, i);
+ if (unlikely (status))
+ goto BAIL;
+
+ last_glyph = &glyphs[num_glyphs - 1];
+ status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents);
+ if (unlikely (status))
+ goto BAIL;
+
+ x = last_glyph->x + extents.x_advance;
+ y = last_glyph->y + extents.y_advance;
+ cr->backend->move_to (cr, x, y);
+
+ BAIL:
+ if (glyphs != stack_glyphs)
+ cairo_glyph_free (glyphs);
+ if (clusters != stack_clusters)
+ cairo_text_cluster_free (clusters);
+
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_glyphs:
+ * @cr: a cairo context
+ * @glyphs: array of glyphs to show
+ * @num_glyphs: number of glyphs to show
+ *
+ * A drawing operator that generates the shape from an array of glyphs,
+ * rendered according to the current font face, font size
+ * (font matrix), and font options.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (num_glyphs == 0)
+ return;
+
+ if (num_glyphs < 0) {
+ _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
+ return;
+ }
+
+ if (glyphs == NULL) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_text_glyphs:
+ * @cr: a cairo context
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
+ * @glyphs: array of glyphs to show
+ * @num_glyphs: number of glyphs to show
+ * @clusters: array of cluster mapping information
+ * @num_clusters: number of clusters in the mapping
+ * @cluster_flags: cluster mapping flags
+ *
+ * This operation has rendering effects similar to cairo_show_glyphs()
+ * but, if the target surface supports it, uses the provided text and
+ * cluster mapping to embed the text for the glyphs shown in the output.
+ * If the target does not support the extended attributes, this function
+ * acts like the basic cairo_show_glyphs() as if it had been passed
+ * @glyphs and @num_glyphs.
+ *
+ * The mapping between @utf8 and @glyphs is provided by an array of
+ * <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;
+ }
+
+ if (num_glyphs == 0 && utf8_len == 0)
+ return;
+
+ if (utf8) {
+ /* Make sure clusters cover the entire glyphs and utf8 arrays,
+ * and that cluster boundaries are UTF-8 boundaries. */
+ status = _cairo_validate_text_clusters (utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters, cluster_flags);
+ if (status == CAIRO_STATUS_INVALID_CLUSTERS) {
+ /* Either got invalid UTF-8 text, or cluster mapping is bad.
+ * Differentiate those. */
+
+ cairo_status_t status2;
+
+ status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL);
+ if (status2)
+ status = status2;
+ } else {
+ cairo_glyph_text_info_t info;
+
+ info.utf8 = utf8;
+ info.utf8_len = utf8_len;
+ info.clusters = clusters;
+ info.num_clusters = num_clusters;
+ info.cluster_flags = cluster_flags;
+
+ status = cr->backend->glyphs (cr, glyphs, num_glyphs, &info);
+ }
+ } else {
+ status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL);
+ }
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_text_path:
+ * @cr: a cairo context
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
+ *
+ * Adds closed paths for text to the current path. The generated
+ * path if filled, achieves an effect similar to that of
+ * cairo_show_text().
+ *
+ * Text conversion and positioning is done similar to cairo_show_text().
+ *
+ * Like cairo_show_text(), After this call the current point is
+ * moved to the origin of where the next glyph would be placed in
+ * this same progression. That is, the current point will be at
+ * the origin of the final glyph offset by its advance values.
+ * This allows for chaining multiple calls to to cairo_text_path()
+ * without having to set current point in between.
+ *
+ * Note: The cairo_text_path() function call is part of what the cairo
+ * designers call the "toy" text API. It is convenient for short demos
+ * and simple programs, but it is not expected to be adequate for
+ * serious text-using applications. See cairo_glyph_path() for the
+ * "real" text path API in cairo.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_text_path (cairo_t *cr, const char *utf8)
+{
+ cairo_status_t status;
+ cairo_text_extents_t extents;
+ cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+ cairo_glyph_t *glyphs, *last_glyph;
+ cairo_scaled_font_t *scaled_font;
+ int num_glyphs;
+ double x, y;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (utf8 == NULL)
+ return;
+
+
+ glyphs = stack_glyphs;
+ num_glyphs = ARRAY_LENGTH (stack_glyphs);
+
+ scaled_font = cairo_get_scaled_font (cr);
+ if (unlikely (scaled_font->status)) {
+ _cairo_set_error (cr, scaled_font->status);
+ return;
+ }
+
+ cairo_get_current_point (cr, &x, &y);
+ status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ x, y,
+ utf8, -1,
+ &glyphs, &num_glyphs,
+ NULL, NULL, NULL);
+
+ if (num_glyphs == 0)
+ return;
+
+ status = cr->backend->glyph_path (cr, glyphs, num_glyphs);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ last_glyph = &glyphs[num_glyphs - 1];
+ status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ x = last_glyph->x + extents.x_advance;
+ y = last_glyph->y + extents.y_advance;
+ cr->backend->move_to (cr, x, y);
+
+ BAIL:
+ if (glyphs != stack_glyphs)
+ cairo_glyph_free (glyphs);
+
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_glyph_path:
+ * @cr: a cairo context
+ * @glyphs: array of glyphs to show
+ * @num_glyphs: number of glyphs to show
+ *
+ * Adds closed paths for the glyphs to the current path. The generated
+ * path if filled, achieves an effect similar to that of
+ * cairo_show_glyphs().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (num_glyphs == 0)
+ return;
+
+ if (unlikely (num_glyphs < 0)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
+ return;
+ }
+
+ if (unlikely (glyphs == NULL)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ status = cr->backend->glyph_path (cr, glyphs, num_glyphs);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_operator:
+ * @cr: a cairo context
+ *
+ * Gets the current compositing operator for a cairo context.
+ *
+ * Return value: the current compositing operator.
+ *
+ * Since: 1.0
+ **/
+cairo_operator_t
+cairo_get_operator (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_OPERATOR_DEFAULT;
+
+ return cr->backend->get_operator (cr);
+}
+
+#if 0
+/**
+ * cairo_get_opacity:
+ * @cr: a cairo context
+ *
+ * Gets the current compositing opacity for a cairo context.
+ *
+ * Return value: the current compositing opacity.
+ *
+ * Since: TBD
+ **/
+double
+cairo_get_opacity (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return 1.;
+
+ return cr->backend->get_opacity (cr);
+}
+#endif
+
+/**
+ * cairo_get_tolerance:
+ * @cr: a cairo context
+ *
+ * Gets the current tolerance value, as set by cairo_set_tolerance().
+ *
+ * Return value: the current tolerance value.
+ *
+ * Since: 1.0
+ **/
+double
+cairo_get_tolerance (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_TOLERANCE_DEFAULT;
+
+ return cr->backend->get_tolerance (cr);
+}
+slim_hidden_def (cairo_get_tolerance);
+
+/**
+ * cairo_get_antialias:
+ * @cr: a cairo context
+ *
+ * Gets the current shape antialiasing mode, as set by
+ * cairo_set_antialias().
+ *
+ * Return value: the current shape antialiasing mode.
+ *
+ * Since: 1.0
+ **/
+cairo_antialias_t
+cairo_get_antialias (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_ANTIALIAS_DEFAULT;
+
+ return cr->backend->get_antialias (cr);
+}
+
+/**
+ * cairo_has_current_point:
+ * @cr: a cairo context
+ *
+ * Returns whether a current point is defined on the current path.
+ * See cairo_get_current_point() for details on the current point.
+ *
+ * Return value: whether a current point is defined.
+ *
+ * Since: 1.6
+ **/
+cairo_bool_t
+cairo_has_current_point (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return FALSE;
+
+ return cr->backend->has_current_point (cr);
+}
+
+/**
+ * cairo_get_current_point:
+ * @cr: a cairo context
+ * @x: return value for X coordinate of the current point
+ * @y: return value for Y coordinate of the current point
+ *
+ * Gets the current point of the current path, which is
+ * conceptually the final point reached by the path so far.
+ *
+ * The current point is returned in the user-space coordinate
+ * system. If there is no defined current point or if @cr is in an
+ * error status, @x and @y will both be set to 0.0. It is possible to
+ * check this in advance with cairo_has_current_point().
+ *
+ * Most path construction functions alter the current point. See the
+ * following for details on how they affect the current point:
+ * cairo_new_path(), cairo_new_sub_path(),
+ * cairo_append_path(), cairo_close_path(),
+ * cairo_move_to(), cairo_line_to(), cairo_curve_to(),
+ * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(),
+ * cairo_arc(), cairo_arc_negative(), cairo_rectangle(),
+ * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path().
+ *
+ * Some functions use and alter the current point but do not
+ * otherwise change current path:
+ * cairo_show_text().
+ *
+ * Some functions unset the current path and as a result, current point:
+ * cairo_fill(), cairo_stroke().
+ *
+ * Since: 1.0
+ **/
+void
+cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret)
+{
+ double x, y;
+
+ x = y = 0;
+ if (cr->status == CAIRO_STATUS_SUCCESS &&
+ cr->backend->has_current_point (cr))
+ {
+ cr->backend->get_current_point (cr, &x, &y);
+ }
+
+ if (x_ret)
+ *x_ret = x;
+ if (y_ret)
+ *y_ret = y;
+}
+slim_hidden_def(cairo_get_current_point);
+
+/**
+ * cairo_get_fill_rule:
+ * @cr: a cairo context
+ *
+ * Gets the current fill rule, as set by cairo_set_fill_rule().
+ *
+ * Return value: the current fill rule.
+ *
+ * Since: 1.0
+ **/
+cairo_fill_rule_t
+cairo_get_fill_rule (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_FILL_RULE_DEFAULT;
+
+ return cr->backend->get_fill_rule (cr);
+}
+
+/**
+ * cairo_get_line_width:
+ * @cr: a cairo context
+ *
+ * This function returns the current line width value exactly as set by
+ * cairo_set_line_width(). Note that the value is unchanged even if
+ * the CTM has changed between the calls to cairo_set_line_width() and
+ * cairo_get_line_width().
+ *
+ * Return value: the current line width.
+ *
+ * Since: 1.0
+ **/
+double
+cairo_get_line_width (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_LINE_WIDTH_DEFAULT;
+
+ return cr->backend->get_line_width (cr);
+}
+slim_hidden_def (cairo_get_line_width);
+
+/**
+ * cairo_get_line_cap:
+ * @cr: a cairo context
+ *
+ * Gets the current line cap style, as set by cairo_set_line_cap().
+ *
+ * Return value: the current line cap style.
+ *
+ * Since: 1.0
+ **/
+cairo_line_cap_t
+cairo_get_line_cap (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_LINE_CAP_DEFAULT;
+
+ return cr->backend->get_line_cap (cr);
+}
+
+/**
+ * cairo_get_line_join:
+ * @cr: a cairo context
+ *
+ * Gets the current line join style, as set by cairo_set_line_join().
+ *
+ * Return value: the current line join style.
+ *
+ * Since: 1.0
+ **/
+cairo_line_join_t
+cairo_get_line_join (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_LINE_JOIN_DEFAULT;
+
+ return cr->backend->get_line_join (cr);
+}
+
+/**
+ * cairo_get_miter_limit:
+ * @cr: a cairo context
+ *
+ * Gets the current miter limit, as set by cairo_set_miter_limit().
+ *
+ * Return value: the current miter limit.
+ *
+ * Since: 1.0
+ **/
+double
+cairo_get_miter_limit (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_MITER_LIMIT_DEFAULT;
+
+ return cr->backend->get_miter_limit (cr);
+}
+
+/**
+ * cairo_get_matrix:
+ * @cr: a cairo context
+ * @matrix: return value for the matrix
+ *
+ * Stores the current transformation matrix (CTM) into @matrix.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix)
+{
+ if (unlikely (cr->status)) {
+ cairo_matrix_init_identity (matrix);
+ return;
+ }
+
+ cr->backend->get_matrix (cr, matrix);
+}
+slim_hidden_def (cairo_get_matrix);
+
+/**
+ * cairo_get_target:
+ * @cr: a cairo context
+ *
+ * Gets the target surface for the cairo context as passed to
+ * cairo_create().
+ *
+ * This function will always return a valid pointer, but the result
+ * can be a "nil" surface if @cr is already in an error state,
+ * (ie. cairo_status() <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.0
+ **/
+cairo_surface_t *
+cairo_get_target (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_surface_create_in_error (cr->status);
+
+ return cr->backend->get_original_target (cr);
+}
+slim_hidden_def (cairo_get_target);
+
+/**
+ * cairo_get_group_target:
+ * @cr: a cairo context
+ *
+ * Gets the current destination surface for the context. This is either
+ * the original target surface as passed to cairo_create() or the target
+ * surface for the current group as started by the most recent call to
+ * cairo_push_group() or cairo_push_group_with_content().
+ *
+ * This function will always return a valid pointer, but the result
+ * can be a "nil" surface if @cr is already in an error state,
+ * (ie. cairo_status() <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 cr->backend->get_current_target (cr);
+}
+
+/**
+ * cairo_copy_path:
+ * @cr: a cairo context
+ *
+ * Creates a copy of the current path and returns it to the user as a
+ * #cairo_path_t. See #cairo_path_data_t for hints on how to iterate
+ * over the returned data structure.
+ *
+ * This function will always return a valid pointer, but the result
+ * will have no data (<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.
+ *
+ * Since: 1.0
+ **/
+cairo_path_t *
+cairo_copy_path (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_path_create_in_error (cr->status);
+
+ return cr->backend->copy_path (cr);
+}
+
+/**
+ * cairo_copy_path_flat:
+ * @cr: a cairo context
+ *
+ * Gets a flattened copy of the current path and returns it to the
+ * user as a #cairo_path_t. See #cairo_path_data_t for hints on
+ * how to iterate over the returned data structure.
+ *
+ * This function is like cairo_copy_path() except that any curves
+ * in the path will be approximated with piecewise-linear
+ * approximations, (accurate to within the current tolerance
+ * value). That is, the result is guaranteed to not have any elements
+ * of type %CAIRO_PATH_CURVE_TO which will instead be replaced by a
+ * series of %CAIRO_PATH_LINE_TO elements.
+ *
+ * This function will always return a valid pointer, but the result
+ * will have no data (<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.
+ *
+ * Since: 1.0
+ **/
+cairo_path_t *
+cairo_copy_path_flat (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_path_create_in_error (cr->status);
+
+ return cr->backend->copy_path_flat (cr);
+}
+
+/**
+ * cairo_append_path:
+ * @cr: a cairo context
+ * @path: path to be appended
+ *
+ * Append the @path onto the current path. The @path may be either the
+ * return value from one of cairo_copy_path() or
+ * cairo_copy_path_flat() or it may be constructed manually. See
+ * #cairo_path_t for details on how the path data structure should be
+ * initialized, and note that <literal>path->status</literal> must be
+ * initialized to %CAIRO_STATUS_SUCCESS.
+ *
+ * Since: 1.0
+ **/
+void
+cairo_append_path (cairo_t *cr,
+ const cairo_path_t *path)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (unlikely (path == NULL)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ if (unlikely (path->status)) {
+ if (path->status > CAIRO_STATUS_SUCCESS &&
+ path->status <= CAIRO_STATUS_LAST_STATUS)
+ _cairo_set_error (cr, path->status);
+ else
+ _cairo_set_error (cr, CAIRO_STATUS_INVALID_STATUS);
+ return;
+ }
+
+ if (path->num_data == 0)
+ return;
+
+ if (unlikely (path->data == NULL)) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ status = cr->backend->append_path (cr, path);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_status:
+ * @cr: a cairo context
+ *
+ * Checks whether an error has previously occurred for this context.
+ *
+ * Returns: the current status of this context, see #cairo_status_t
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_status (cairo_t *cr)
+{
+ return cr->status;
+}
+slim_hidden_def (cairo_status);
+
+void
+cairo_set_shadow (cairo_t *cr, cairo_shadow_type_t shadow)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_shadow (cr, shadow);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_shadow);
+
+void
+cairo_set_shadow_offset (cairo_t *cr, double x_offset, double y_offset)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_shadow_offset (cr, x_offset, y_offset);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_shadow_offset);
+
+void
+cairo_set_shadow_rgb (cairo_t *cr, double red, double green, double blue)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_shadow_rgba (cr, red, green, blue, 1.0);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_shadow_rgb);
+
+void
+cairo_set_shadow_rgba (cairo_t *cr, double red, double green,
+ double blue, double alpha)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_shadow_rgba (cr, red, green, blue, alpha);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_shadow_rgba);
+
+void
+cairo_set_shadow_blur (cairo_t *cr, double x_blur, double y_blur)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = cr->backend->set_shadow_blur (cr, x_blur, y_blur);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_shadow_blur);
+
+void
+cairo_set_draw_shadow_only (cairo_t *cr, cairo_bool_t draw_shadow_only)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cr->backend->set_draw_shadow_only (cr, draw_shadow_only);
+}
+slim_hidden_def (cairo_set_draw_shadow_only);
+
+void
+cairo_shadow_enable_cache (cairo_t *cr, cairo_bool_t enable)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cr->backend->shadow_enable_cache (cr, enable);
+}
+slim_hidden_def (cairo_shadow_enable_cache);
+
+void
+cairo_set_path_is_inset_shadow_with_spread (cairo_t *cr,
+ cairo_bool_t is_spread_path)
+{
+ if (unlikely (cr->status))
+ return;
+ cr->backend->set_path_is_inset_shadow_with_spread (cr, is_spread_path);}
+slim_hidden_def (cairo_set_path_is_inset_shadow_with_spread);
diff --git a/src/cairo.h b/src/cairo.h
new file mode 100755
index 000000000..2b03e77ec
--- /dev/null
+++ b/src/cairo.h
@@ -0,0 +1,3184 @@
+/* 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>
+ *
+ * Since: 1.0
+ **/
+typedef int cairo_bool_t;
+
+/**
+ * cairo_t:
+ *
+ * A #cairo_t contains the current state of the rendering device,
+ * including coordinates of yet to be drawn shapes.
+ *
+ * Cairo contexts, as #cairo_t objects are named, are central to
+ * cairo and all drawing with cairo is always done to a #cairo_t
+ * object.
+ *
+ * Memory management of #cairo_t is done with
+ * cairo_reference() and cairo_destroy().
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo cairo_t;
+
+/**
+ * cairo_surface_t:
+ *
+ * A #cairo_surface_t represents an image, either as the destination
+ * of a drawing operation or as source when drawing onto another
+ * surface. To draw to a #cairo_surface_t, create a cairo context
+ * with the surface as the target, using cairo_create().
+ *
+ * There are different subtypes of #cairo_surface_t for
+ * different drawing backends; for example, cairo_image_surface_create()
+ * creates a bitmap image in memory.
+ * The type of a surface can be queried with cairo_surface_get_type().
+ *
+ * The initial contents of a surface after creation depend upon the manner
+ * of its creation. If cairo creates the surface and backing storage for
+ * the user, it will be initially cleared; for example,
+ * cairo_image_surface_create() and cairo_surface_create_similar().
+ * Alternatively, if the user passes in a reference to some backing storage
+ * and asks cairo to wrap that in a #cairo_surface_t, then the contents are
+ * not modified; for example, cairo_image_surface_create_for_data() and
+ * cairo_xlib_surface_create().
+ *
+ * Memory management of #cairo_surface_t is done with
+ * cairo_surface_reference() and cairo_surface_destroy().
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo_surface cairo_surface_t;
+
+/**
+ * cairo_device_t:
+ *
+ * A #cairo_device_t represents the driver interface for drawing
+ * operations to a #cairo_surface_t. There are different subtypes of
+ * #cairo_device_t for different drawing backends; for example,
+ * cairo_egl_device_create() creates a device that wraps an EGL display and
+ * context.
+ *
+ * The type of a device can be queried with cairo_device_get_type().
+ *
+ * Memory management of #cairo_device_t is done with
+ * cairo_device_reference() and cairo_device_destroy().
+ *
+ * Since: 1.10
+ **/
+typedef struct _cairo_device cairo_device_t;
+
+/**
+ * cairo_matrix_t:
+ * @xx: xx component of the affine transformation
+ * @yx: yx component of the affine transformation
+ * @xy: xy component of the affine transformation
+ * @yy: yy component of the affine transformation
+ * @x0: X translation component of the affine transformation
+ * @y0: Y translation component of the affine transformation
+ *
+ * A #cairo_matrix_t holds an affine transformation, such as a scale,
+ * rotation, shear, or a combination of those. The transformation of
+ * a point (x, y) is given by:
+ * <programlisting>
+ * x_new = xx * x + xy * y + x0;
+ * y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo_matrix {
+ double xx; double yx;
+ double xy; double yy;
+ double x0; double y0;
+} cairo_matrix_t;
+
+/**
+ * cairo_pattern_t:
+ *
+ * A #cairo_pattern_t represents a source when drawing onto a
+ * surface. There are different subtypes of #cairo_pattern_t,
+ * for different types of sources; for example,
+ * cairo_pattern_create_rgb() creates a pattern for a solid
+ * opaque color.
+ *
+ * Other than various
+ * <function>cairo_pattern_create_<emphasis>type</emphasis>()</function>
+ * functions, some of the pattern types can be implicitly created using various
+ * <function>cairo_set_source_<emphasis>type</emphasis>()</function> functions;
+ * for example cairo_set_source_rgb().
+ *
+ * The type of a pattern can be queried with cairo_pattern_get_type().
+ *
+ * Memory management of #cairo_pattern_t is done with
+ * cairo_pattern_reference() and cairo_pattern_destroy().
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo_pattern cairo_pattern_t;
+
+/**
+ * cairo_destroy_func_t:
+ * @data: The data element being destroyed.
+ *
+ * #cairo_destroy_func_t the type of function which is called when a
+ * data element is destroyed. It is passed the pointer to the data
+ * element and should free any memory and resources allocated for it.
+ *
+ * Since: 1.0
+ **/
+typedef void (*cairo_destroy_func_t) (void *data);
+
+/**
+ * cairo_user_data_key_t:
+ * @unused: not used; ignore.
+ *
+ * #cairo_user_data_key_t is used for attaching user data to cairo
+ * data structures. The actual contents of the struct is never used,
+ * and there is no need to initialize the object; only the unique
+ * address of a #cairo_data_key_t object is used. Typically, you
+ * would just use the address of a static #cairo_data_key_t object.
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo_user_data_key {
+ int unused;
+} cairo_user_data_key_t;
+
+/**
+ * cairo_status_t:
+ * @CAIRO_STATUS_SUCCESS: no error has occurred (Since 1.0)
+ * @CAIRO_STATUS_NO_MEMORY: out of memory (Since 1.0)
+ * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() (Since 1.0)
+ * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() (Since 1.0)
+ * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined (Since 1.0)
+ * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) (Since 1.0)
+ * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t (Since 1.0)
+ * @CAIRO_STATUS_NULL_POINTER: %NULL pointer (Since 1.0)
+ * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 (Since 1.0)
+ * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid (Since 1.0)
+ * @CAIRO_STATUS_READ_ERROR: error while reading from input stream (Since 1.0)
+ * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream (Since 1.0)
+ * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished (Since 1.0)
+ * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation (Since 1.0)
+ * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation (Since 1.0)
+ * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t (Since 1.0)
+ * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t (Since 1.0)
+ * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual* (Since 1.0)
+ * @CAIRO_STATUS_FILE_NOT_FOUND: file not found (Since 1.0)
+ * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting (Since 1.0)
+ * @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2)
+ * @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter (Since 1.4)
+ * @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4)
+ * @CAIRO_STATUS_TEMP_FILE_ERROR: error creating or writing to a temporary file (Since 1.6)
+ * @CAIRO_STATUS_INVALID_STRIDE: invalid value for stride (Since 1.6)
+ * @CAIRO_STATUS_FONT_TYPE_MISMATCH: the font type is not appropriate for the operation (Since 1.8)
+ * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8)
+ * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8)
+ * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8)
+ * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8)
+ * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8)
+ * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8)
+ * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for the size of the input (surface, pattern, etc.) (Since 1.10)
+ * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10)
+ * @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10)
+ * @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10)
+ * @CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: a mesh pattern
+ * construction operation was used outside of a
+ * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch()
+ * pair (Since 1.12)
+ * @CAIRO_STATUS_DEVICE_FINISHED: target device has been finished (Since 1.12)
+ * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of
+ * status values defined in this enumeration. When using this value, note
+ * that the version of cairo at run-time may have additional status values
+ * defined than the value of this symbol at compile-time. (Since 1.10)
+ *
+ * #cairo_status_t is used to indicate errors that can occur when
+ * using Cairo. In some cases it is returned directly by functions.
+ * but when using #cairo_t, the last error, if any, is stored in
+ * the context and can be retrieved with cairo_status().
+ *
+ * New entries may be added in future versions. Use cairo_status_to_string()
+ * to get a human-readable representation of an error message.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_status {
+ CAIRO_STATUS_SUCCESS = 0,
+
+ CAIRO_STATUS_NO_MEMORY,
+ CAIRO_STATUS_INVALID_RESTORE,
+ CAIRO_STATUS_INVALID_POP_GROUP,
+ CAIRO_STATUS_NO_CURRENT_POINT,
+ CAIRO_STATUS_INVALID_MATRIX,
+ CAIRO_STATUS_INVALID_STATUS,
+ CAIRO_STATUS_NULL_POINTER,
+ CAIRO_STATUS_INVALID_STRING,
+ CAIRO_STATUS_INVALID_PATH_DATA,
+ CAIRO_STATUS_READ_ERROR,
+ CAIRO_STATUS_WRITE_ERROR,
+ CAIRO_STATUS_SURFACE_FINISHED,
+ CAIRO_STATUS_SURFACE_TYPE_MISMATCH,
+ CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
+ CAIRO_STATUS_INVALID_CONTENT,
+ CAIRO_STATUS_INVALID_FORMAT,
+ CAIRO_STATUS_INVALID_VISUAL,
+ CAIRO_STATUS_FILE_NOT_FOUND,
+ CAIRO_STATUS_INVALID_DASH,
+ CAIRO_STATUS_INVALID_DSC_COMMENT,
+ CAIRO_STATUS_INVALID_INDEX,
+ CAIRO_STATUS_CLIP_NOT_REPRESENTABLE,
+ CAIRO_STATUS_TEMP_FILE_ERROR,
+ CAIRO_STATUS_INVALID_STRIDE,
+ CAIRO_STATUS_FONT_TYPE_MISMATCH,
+ CAIRO_STATUS_USER_FONT_IMMUTABLE,
+ CAIRO_STATUS_USER_FONT_ERROR,
+ CAIRO_STATUS_NEGATIVE_COUNT,
+ CAIRO_STATUS_INVALID_CLUSTERS,
+ CAIRO_STATUS_INVALID_SLANT,
+ CAIRO_STATUS_INVALID_WEIGHT,
+ CAIRO_STATUS_INVALID_SIZE,
+ CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED,
+ CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
+ CAIRO_STATUS_DEVICE_ERROR,
+ CAIRO_STATUS_INVALID_MESH_CONSTRUCTION,
+ CAIRO_STATUS_DEVICE_FINISHED,
+
+ CAIRO_STATUS_LAST_STATUS
+} cairo_status_t;
+
+/**
+ * cairo_content_t:
+ * @CAIRO_CONTENT_COLOR: The surface will hold color content only. (Since 1.0)
+ * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. (Since 1.0)
+ * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. (Since 1.0)
+ *
+ * #cairo_content_t is used to describe the content that a surface will
+ * contain, whether color information, alpha information (translucence
+ * vs. opacity), or both.
+ *
+ * Note: The large values here are designed to keep #cairo_content_t
+ * values distinct from #cairo_format_t values so that the
+ * implementation can detect the error if users confuse the two types.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_content {
+ CAIRO_CONTENT_COLOR = 0x1000,
+ CAIRO_CONTENT_ALPHA = 0x2000,
+ CAIRO_CONTENT_COLOR_ALPHA = 0x3000
+} cairo_content_t;
+
+/**
+ * cairo_format_t:
+ * @CAIRO_FORMAT_INVALID: no such format exists or is supported.
+ * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with
+ * alpha in the upper 8 bits, then red, then green, then blue.
+ * The 32-bit quantities are stored native-endian. Pre-multiplied
+ * alpha is used. (That is, 50% transparent red is 0x80800000,
+ * not 0x80ff0000.) (Since 1.0)
+ * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with
+ * the upper 8 bits unused. Red, Green, and Blue are stored
+ * in the remaining 24 bits in that order. (Since 1.0)
+ * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding
+ * an alpha value. (Since 1.0)
+ * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding
+ * an alpha value. Pixels are packed together into 32-bit
+ * quantities. The ordering of the bits matches the
+ * 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. (Since 1.0)
+ * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity
+ * with red in the upper 5 bits, then green in the middle
+ * 6 bits, and blue in the lower 5 bits. (Since 1.2)
+ * @CAIRO_FORMAT_RGB30: like RGB24 but with 10bpc. (Since 1.12)
+ *
+ * #cairo_format_t is used to identify the memory format of
+ * image data.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_format {
+ CAIRO_FORMAT_INVALID = -1,
+ CAIRO_FORMAT_ARGB32 = 0,
+ CAIRO_FORMAT_RGB24 = 1,
+ CAIRO_FORMAT_A8 = 2,
+ CAIRO_FORMAT_A1 = 3,
+ CAIRO_FORMAT_RGB16_565 = 4,
+ CAIRO_FORMAT_RGB30 = 5
+} cairo_format_t;
+
+
+/**
+ * cairo_write_func_t:
+ * @closure: the output closure
+ * @data: the buffer containing the data to write
+ * @length: the amount of data to write
+ *
+ * #cairo_write_func_t is the type of function which is called when a
+ * backend needs to write data to an output stream. It is passed the
+ * closure which was specified by the user at the time the write
+ * function was registered, the data to write and the length of the
+ * data in bytes. The write function should return
+ * %CAIRO_STATUS_SUCCESS if all the data was successfully written,
+ * %CAIRO_STATUS_WRITE_ERROR otherwise.
+ *
+ * Returns: the status code of the write operation
+ *
+ * Since: 1.0
+ **/
+typedef cairo_status_t (*cairo_write_func_t) (void *closure,
+ const unsigned char *data,
+ unsigned int length);
+
+/**
+ * cairo_read_func_t:
+ * @closure: the input closure
+ * @data: the buffer into which to read the data
+ * @length: the amount of data to read
+ *
+ * #cairo_read_func_t is the type of function which is called when a
+ * backend needs to read data from an input stream. It is passed the
+ * closure which was specified by the user at the time the read
+ * function was registered, the buffer to read the data into and the
+ * length of the data in bytes. The read function should return
+ * %CAIRO_STATUS_SUCCESS if all the data was successfully read,
+ * %CAIRO_STATUS_READ_ERROR otherwise.
+ *
+ * Returns: the status code of the read operation
+ *
+ * Since: 1.0
+ **/
+typedef cairo_status_t (*cairo_read_func_t) (void *closure,
+ unsigned char *data,
+ unsigned int length);
+
+/**
+ * cairo_rectangle_int_t:
+ * @x: X coordinate of the left side of the rectangle
+ * @y: Y coordinate of the the top side of the rectangle
+ * @width: width of the rectangle
+ * @height: height of the rectangle
+ *
+ * A data structure for holding a rectangle with integer coordinates.
+ *
+ * Since: 1.10
+ **/
+
+typedef struct _cairo_rectangle_int {
+ int x, y;
+ int width, height;
+} cairo_rectangle_int_t;
+
+
+/* Functions for manipulating state objects */
+cairo_public cairo_t *
+cairo_create (cairo_surface_t *target);
+
+cairo_public cairo_t *
+cairo_reference (cairo_t *cr);
+
+cairo_public void
+cairo_destroy (cairo_t *cr);
+
+cairo_public unsigned int
+cairo_get_reference_count (cairo_t *cr);
+
+cairo_public void *
+cairo_get_user_data (cairo_t *cr,
+ const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_set_user_data (cairo_t *cr,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy);
+
+cairo_public void
+cairo_save (cairo_t *cr);
+
+cairo_public void
+cairo_restore (cairo_t *cr);
+
+cairo_public void
+cairo_push_group (cairo_t *cr);
+
+cairo_public void
+cairo_push_group_with_content (cairo_t *cr, cairo_content_t content);
+
+cairo_public cairo_pattern_t *
+cairo_pop_group (cairo_t *cr);
+
+cairo_public void
+cairo_pop_group_to_source (cairo_t *cr);
+
+/* Modify state */
+
+/**
+ * cairo_operator_t:
+ * @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded) (Since 1.0)
+ * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded) (Since 1.0)
+ * @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer
+ * (bounded) (Since 1.0)
+ * @CAIRO_OPERATOR_IN: draw source where there was destination content
+ * (unbounded) (Since 1.0)
+ * @CAIRO_OPERATOR_OUT: draw source where there was no destination
+ * content (unbounded) (Since 1.0)
+ * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and
+ * only there (Since 1.0)
+ * @CAIRO_OPERATOR_DEST: ignore the source (Since 1.0)
+ * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source (Since 1.0)
+ * @CAIRO_OPERATOR_DEST_IN: leave destination only where there was
+ * source content (unbounded) (Since 1.0)
+ * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no
+ * source content (Since 1.0)
+ * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content
+ * and only there (unbounded) (Since 1.0)
+ * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only
+ * one of them (Since 1.0)
+ * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated (Since 1.0)
+ * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are
+ * disjoint geometries (Since 1.0)
+ * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied.
+ * This causes the result to be at least as dark as the darker inputs. (Since 1.10)
+ * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and
+ * multiplied. This causes the result to be at least as light as the lighter
+ * inputs. (Since 1.10)
+ * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the
+ * lightness of the destination color. (Since 1.10)
+ * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it
+ * is darker, otherwise keeps the source. (Since 1.10)
+ * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it
+ * is lighter, otherwise keeps the source. (Since 1.10)
+ * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect
+ * the source color. (Since 1.10)
+ * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect
+ * the source color. (Since 1.10)
+ * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependent on source
+ * color. (Since 1.10)
+ * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependent on source
+ * color. (Since 1.10)
+ * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and
+ * destination color. (Since 1.10)
+ * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but
+ * with lower contrast. (Since 1.10)
+ * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source
+ * and the saturation and luminosity of the target. (Since 1.10)
+ * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation
+ * of the source and the hue and luminosity of the target. Painting with
+ * this mode onto a gray area produces no change. (Since 1.10)
+ * @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation
+ * of the source and the luminosity of the target. This preserves the gray
+ * levels of the target and is useful for coloring monochrome images or
+ * tinting color images. (Since 1.10)
+ * @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of
+ * the source and the hue and saturation of the target. This produces an
+ * inverse effect to @CAIRO_OPERATOR_HSL_COLOR. (Since 1.10)
+ *
+ * #cairo_operator_t is used to set the compositing operator for all cairo
+ * drawing operations.
+ *
+ * The default operator is %CAIRO_OPERATOR_OVER.
+ *
+ * The operators marked as <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>.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_operator {
+ CAIRO_OPERATOR_CLEAR,
+
+ CAIRO_OPERATOR_SOURCE,
+ CAIRO_OPERATOR_OVER,
+ CAIRO_OPERATOR_IN,
+ CAIRO_OPERATOR_OUT,
+ CAIRO_OPERATOR_ATOP,
+
+ CAIRO_OPERATOR_DEST,
+ CAIRO_OPERATOR_DEST_OVER,
+ CAIRO_OPERATOR_DEST_IN,
+ CAIRO_OPERATOR_DEST_OUT,
+ CAIRO_OPERATOR_DEST_ATOP,
+
+ CAIRO_OPERATOR_XOR,
+ CAIRO_OPERATOR_ADD,
+ CAIRO_OPERATOR_SATURATE,
+
+ CAIRO_OPERATOR_MULTIPLY,
+ CAIRO_OPERATOR_SCREEN,
+ CAIRO_OPERATOR_OVERLAY,
+ CAIRO_OPERATOR_DARKEN,
+ CAIRO_OPERATOR_LIGHTEN,
+ CAIRO_OPERATOR_COLOR_DODGE,
+ CAIRO_OPERATOR_COLOR_BURN,
+ CAIRO_OPERATOR_HARD_LIGHT,
+ CAIRO_OPERATOR_SOFT_LIGHT,
+ CAIRO_OPERATOR_DIFFERENCE,
+ CAIRO_OPERATOR_EXCLUSION,
+ CAIRO_OPERATOR_HSL_HUE,
+ CAIRO_OPERATOR_HSL_SATURATION,
+ CAIRO_OPERATOR_HSL_COLOR,
+ CAIRO_OPERATOR_HSL_LUMINOSITY
+} cairo_operator_t;
+
+cairo_public void
+cairo_set_operator (cairo_t *cr, cairo_operator_t op);
+
+cairo_public void
+cairo_set_source (cairo_t *cr, cairo_pattern_t *source);
+
+cairo_public void
+cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue);
+
+cairo_public void
+cairo_set_source_rgba (cairo_t *cr,
+ double red, double green, double blue,
+ double alpha);
+
+cairo_public void
+cairo_set_source_surface (cairo_t *cr,
+ cairo_surface_t *surface,
+ double x,
+ double y);
+
+cairo_public void
+cairo_set_tolerance (cairo_t *cr, double tolerance);
+
+/**
+ * cairo_antialias_t:
+ * @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for
+ * the subsystem and target device, since 1.0
+ * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask, since 1.0
+ * @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using
+ * shades of gray for black text on a white background, for example), since 1.0
+ * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking
+ * advantage of the order of subpixel elements on devices
+ * such as LCD panels, since 1.0
+ * @CAIRO_ANTIALIAS_FAST: Hint that the backend should perform some
+ * antialiasing but prefer speed over quality, since 1.12
+ * @CAIRO_ANTIALIAS_GOOD: The backend should balance quality against
+ * performance, since 1.12
+ * @CAIRO_ANTIALIAS_BEST: Hint that the backend should render at the highest
+ * quality, sacrificing speed if necessary, since 1.12
+ *
+ * Specifies the type of antialiasing to do when rendering text or shapes.
+ *
+ * As it is not necessarily clear from the above what advantages a particular
+ * antialias method provides, since 1.12, there is also a set of hints:
+ * @CAIRO_ANTIALIAS_FAST: Allow the backend to degrade raster quality for speed
+ * @CAIRO_ANTIALIAS_GOOD: A balance between speed and quality
+ * @CAIRO_ANTIALIAS_BEST: A high-fidelity, but potentially slow, raster mode
+ *
+ * These make no guarantee on how the backend will perform its rasterisation
+ * (if it even rasterises!), nor that they have any differing effect other
+ * than to enable some form of antialiasing. In the case of glyph rendering,
+ * @CAIRO_ANTIALIAS_FAST and @CAIRO_ANTIALIAS_GOOD will be mapped to
+ * @CAIRO_ANTIALIAS_GRAY, with @CAIRO_ANTALIAS_BEST being equivalent to
+ * @CAIRO_ANTIALIAS_SUBPIXEL.
+ *
+ * The interpretation of @CAIRO_ANTIALIAS_DEFAULT is left entirely up to
+ * the backend, typically this will be similar to @CAIRO_ANTIALIAS_GOOD.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_antialias {
+ CAIRO_ANTIALIAS_DEFAULT,
+
+ /* method */
+ CAIRO_ANTIALIAS_NONE,
+ CAIRO_ANTIALIAS_GRAY,
+ CAIRO_ANTIALIAS_SUBPIXEL,
+
+ /* hints */
+ CAIRO_ANTIALIAS_FAST,
+ CAIRO_ANTIALIAS_GOOD,
+ CAIRO_ANTIALIAS_BEST
+} cairo_antialias_t;
+
+cairo_public void
+cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias);
+
+/**
+ * cairo_fill_rule_t:
+ * @CAIRO_FILL_RULE_WINDING: If the path crosses the ray from
+ * left-to-right, counts +1. If the path crosses the ray
+ * from right to left, counts -1. (Left and right are determined
+ * from the perspective of looking along the ray from the starting
+ * point.) If the total count is non-zero, the point will be filled. (Since 1.0)
+ * @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of
+ * intersections, without regard to the orientation of the contour. If
+ * the total number of intersections is odd, the point will be
+ * filled. (Since 1.0)
+ *
+ * #cairo_fill_rule_t is used to select how paths are filled. For both
+ * fill rules, whether or not a point is included in the fill is
+ * determined by taking a ray from that point to infinity and looking
+ * at intersections with the path. The ray can be in any direction,
+ * as long as it doesn't pass through the end point of a segment
+ * or have a tricky intersection such as intersecting tangent to the path.
+ * (Note that filling is not actually implemented in this way. This
+ * is just a description of the rule that is applied.)
+ *
+ * The default fill rule is %CAIRO_FILL_RULE_WINDING.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_fill_rule {
+ CAIRO_FILL_RULE_WINDING,
+ CAIRO_FILL_RULE_EVEN_ODD
+} cairo_fill_rule_t;
+
+cairo_public void
+cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule);
+
+cairo_public void
+cairo_set_line_width (cairo_t *cr, double width);
+
+/**
+ * cairo_line_cap_t:
+ * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point (Since 1.0)
+ * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point (Since 1.0)
+ * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point (Since 1.0)
+ *
+ * Specifies how to render the endpoints of the path when stroking.
+ *
+ * The default line cap style is %CAIRO_LINE_CAP_BUTT.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_line_cap {
+ CAIRO_LINE_CAP_BUTT,
+ CAIRO_LINE_CAP_ROUND,
+ CAIRO_LINE_CAP_SQUARE
+} cairo_line_cap_t;
+
+cairo_public void
+cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap);
+
+/**
+ * cairo_line_join_t:
+ * @CAIRO_LINE_JOIN_MITER: use a sharp (angled) corner, see
+ * cairo_set_miter_limit() (Since 1.0)
+ * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the
+ * joint point (Since 1.0)
+ * @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half
+ * the line width from the joint point (Since 1.0)
+ *
+ * Specifies how to render the junction of two lines when stroking.
+ *
+ * The default line join style is %CAIRO_LINE_JOIN_MITER.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_line_join {
+ CAIRO_LINE_JOIN_MITER,
+ CAIRO_LINE_JOIN_ROUND,
+ CAIRO_LINE_JOIN_BEVEL
+} cairo_line_join_t;
+
+cairo_public void
+cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join);
+
+cairo_public void
+cairo_set_dash (cairo_t *cr,
+ const double *dashes,
+ int num_dashes,
+ double offset);
+
+cairo_public void
+cairo_set_miter_limit (cairo_t *cr, double limit);
+
+cairo_public void
+cairo_translate (cairo_t *cr, double tx, double ty);
+
+cairo_public void
+cairo_scale (cairo_t *cr, double sx, double sy);
+
+cairo_public void
+cairo_rotate (cairo_t *cr, double angle);
+
+cairo_public void
+cairo_transform (cairo_t *cr,
+ const cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_set_matrix (cairo_t *cr,
+ const cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_identity_matrix (cairo_t *cr);
+
+cairo_public void
+cairo_user_to_device (cairo_t *cr, double *x, double *y);
+
+cairo_public void
+cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy);
+
+cairo_public void
+cairo_device_to_user (cairo_t *cr, double *x, double *y);
+
+cairo_public void
+cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy);
+
+/* Path creation functions */
+cairo_public void
+cairo_new_path (cairo_t *cr);
+
+cairo_public void
+cairo_move_to (cairo_t *cr, double x, double y);
+
+cairo_public void
+cairo_new_sub_path (cairo_t *cr);
+
+cairo_public void
+cairo_line_to (cairo_t *cr, double x, double y);
+
+cairo_public void
+cairo_curve_to (cairo_t *cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3);
+
+cairo_public void
+cairo_arc (cairo_t *cr,
+ double xc, double yc,
+ double radius,
+ double angle1, double angle2);
+
+cairo_public void
+cairo_arc_negative (cairo_t *cr,
+ double xc, double yc,
+ double radius,
+ double angle1, double angle2);
+
+/* XXX: NYI
+cairo_public void
+cairo_arc_to (cairo_t *cr,
+ double x1, double y1,
+ double x2, double y2,
+ double radius);
+*/
+
+cairo_public void
+cairo_rel_move_to (cairo_t *cr, double dx, double dy);
+
+cairo_public void
+cairo_rel_line_to (cairo_t *cr, double dx, double dy);
+
+cairo_public void
+cairo_rel_curve_to (cairo_t *cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3);
+
+cairo_public void
+cairo_rectangle (cairo_t *cr,
+ double x, double y,
+ double width, double height);
+
+/* XXX: NYI
+cairo_public void
+cairo_stroke_to_path (cairo_t *cr);
+*/
+
+cairo_public void
+cairo_close_path (cairo_t *cr);
+
+cairo_public void
+cairo_path_extents (cairo_t *cr,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+/* Painting functions */
+cairo_public void
+cairo_paint (cairo_t *cr);
+
+cairo_public void
+cairo_paint_with_alpha (cairo_t *cr,
+ double alpha);
+
+cairo_public void
+cairo_mask (cairo_t *cr,
+ cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_mask_surface (cairo_t *cr,
+ cairo_surface_t *surface,
+ double surface_x,
+ double surface_y);
+
+cairo_public void
+cairo_stroke (cairo_t *cr);
+
+cairo_public void
+cairo_stroke_preserve (cairo_t *cr);
+
+cairo_public void
+cairo_fill (cairo_t *cr);
+
+cairo_public void
+cairo_fill_preserve (cairo_t *cr);
+
+cairo_public void
+cairo_copy_page (cairo_t *cr);
+
+cairo_public void
+cairo_show_page (cairo_t *cr);
+
+/* Insideness testing */
+cairo_public cairo_bool_t
+cairo_in_stroke (cairo_t *cr, double x, double y);
+
+cairo_public cairo_bool_t
+cairo_in_fill (cairo_t *cr, double x, double y);
+
+cairo_public cairo_bool_t
+cairo_in_clip (cairo_t *cr, double x, double y);
+
+/* Rectangular extents */
+cairo_public void
+cairo_stroke_extents (cairo_t *cr,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+cairo_public void
+cairo_fill_extents (cairo_t *cr,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+/* Clipping */
+cairo_public void
+cairo_reset_clip (cairo_t *cr);
+
+cairo_public void
+cairo_clip (cairo_t *cr);
+
+cairo_public void
+cairo_clip_preserve (cairo_t *cr);
+
+cairo_public void
+cairo_clip_extents (cairo_t *cr,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+/**
+ * cairo_rectangle_t:
+ * @x: X coordinate of the left side of the rectangle
+ * @y: Y coordinate of the the top side of the rectangle
+ * @width: width of the rectangle
+ * @height: height of the rectangle
+ *
+ * A data structure for holding a rectangle.
+ *
+ * Since: 1.4
+ **/
+typedef struct _cairo_rectangle {
+ double x, y, width, height;
+} cairo_rectangle_t;
+
+/**
+ * cairo_rectangle_list_t:
+ * @status: Error status of the rectangle list
+ * @rectangles: Array containing the rectangles
+ * @num_rectangles: Number of rectangles in this list
+ *
+ * A data structure for holding a dynamically allocated
+ * array of rectangles.
+ *
+ * Since: 1.4
+ **/
+typedef struct _cairo_rectangle_list {
+ cairo_status_t status;
+ cairo_rectangle_t *rectangles;
+ int num_rectangles;
+} cairo_rectangle_list_t;
+
+cairo_public cairo_rectangle_list_t *
+cairo_copy_clip_rectangle_list (cairo_t *cr);
+
+cairo_public void
+cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list);
+
+/* 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().
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo_scaled_font cairo_scaled_font_t;
+
+/**
+ * cairo_font_face_t:
+ *
+ * A #cairo_font_face_t specifies all aspects of a font other
+ * than the size or font matrix (a font matrix is used to distort
+ * a font by 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().
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo_font_face cairo_font_face_t;
+
+/**
+ * cairo_glyph_t:
+ * @index: glyph index in the font. The exact interpretation of the
+ * glyph index depends on the font technology being used.
+ * @x: the offset in the X direction between the origin used for
+ * drawing or measuring the string and the origin of this glyph.
+ * @y: the offset in the Y direction between the origin used for
+ * drawing or measuring the string and the origin of this glyph.
+ *
+ * The #cairo_glyph_t structure holds information about a single glyph
+ * when drawing or measuring text. A font is (in simple terms) a
+ * collection of shapes used to draw text. A glyph is one of these
+ * shapes. There can be multiple glyphs for a single character
+ * (alternates to be used in different contexts, for example), or a
+ * glyph can be a <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
+ *
+ * Since: 1.0
+ **/
+typedef struct {
+ unsigned long index;
+ double x;
+ double y;
+} cairo_glyph_t;
+
+cairo_public cairo_glyph_t *
+cairo_glyph_allocate (int num_glyphs);
+
+cairo_public void
+cairo_glyph_free (cairo_glyph_t *glyphs);
+
+/**
+ * cairo_text_cluster_t:
+ * @num_bytes: the number of bytes of UTF-8 text covered by cluster
+ * @num_glyphs: the number of glyphs covered by cluster
+ *
+ * The #cairo_text_cluster_t structure holds information about a single
+ * <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. (Since 1.8)
+ *
+ * Specifies properties of a text cluster mapping.
+ *
+ * Since: 1.8
+ **/
+typedef enum _cairo_text_cluster_flags {
+ CAIRO_TEXT_CLUSTER_FLAG_BACKWARD = 0x00000001
+} cairo_text_cluster_flags_t;
+
+/**
+ * cairo_text_extents_t:
+ * @x_bearing: the horizontal distance from the origin to the
+ * leftmost part of the glyphs as drawn. Positive if the
+ * glyphs lie entirely to the right of the origin.
+ * @y_bearing: the vertical distance from the origin to the
+ * topmost part of the glyphs as drawn. Positive only if the
+ * glyphs lie completely below the origin; will usually be
+ * negative.
+ * @width: width of the glyphs as drawn
+ * @height: height of the glyphs as drawn
+ * @x_advance:distance to advance in the X direction
+ * after drawing these glyphs
+ * @y_advance: distance to advance in the Y direction
+ * after drawing these glyphs. Will typically be zero except
+ * for vertical text layout as found in East-Asian languages.
+ *
+ * The #cairo_text_extents_t structure stores the extents of a single
+ * glyph or a string of glyphs in user-space coordinates. Because text
+ * extents are in user-space coordinates, they are mostly, but not
+ * entirely, independent of the current transformation matrix. If you call
+ * <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.
+ *
+ * Since: 1.0
+ **/
+typedef struct {
+ double x_bearing;
+ double y_bearing;
+ double width;
+ double height;
+ double x_advance;
+ double y_advance;
+} cairo_text_extents_t;
+
+/**
+ * cairo_font_extents_t:
+ * @ascent: the distance that the font extends above the baseline.
+ * Note that this is not always exactly equal to the maximum
+ * of the extents of all the glyphs in the font, but rather
+ * is picked to express the font designer's intent as to
+ * how the font should align with elements above it.
+ * @descent: the distance that the font extends below the baseline.
+ * This value is positive for typical fonts that include
+ * portions below the baseline. Note that this is not always
+ * exactly equal to the maximum of the extents of all the
+ * glyphs in the font, but rather is picked to express the
+ * font designer's intent as to how the font should
+ * align with elements below it.
+ * @height: the recommended vertical distance between baselines when
+ * setting consecutive lines of text with the font. This
+ * is greater than @ascent+@descent by a
+ * quantity known as the <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 origin is advanced for any glyph in the font.
+ * @max_y_advance: the maximum distance in the Y direction that
+ * the origin is advanced for any glyph in the font.
+ * This will be zero for normal fonts used for horizontal
+ * writing. (The scripts of East Asia are sometimes written
+ * vertically.)
+ *
+ * The #cairo_font_extents_t structure stores metric information for
+ * a font. Values are given in the current user-space coordinate
+ * system.
+ *
+ * Because font metrics are in user-space coordinates, they are
+ * mostly, but not entirely, independent of the current transformation
+ * matrix. If you call <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.
+ *
+ * Since: 1.0
+ **/
+typedef struct {
+ double ascent;
+ double descent;
+ double height;
+ double max_x_advance;
+ double max_y_advance;
+} cairo_font_extents_t;
+
+/**
+ * cairo_font_slant_t:
+ * @CAIRO_FONT_SLANT_NORMAL: Upright font style, since 1.0
+ * @CAIRO_FONT_SLANT_ITALIC: Italic font style, since 1.0
+ * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style, since 1.0
+ *
+ * Specifies variants of a font face based on their slant.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_font_slant {
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_SLANT_OBLIQUE
+} cairo_font_slant_t;
+
+/**
+ * cairo_font_weight_t:
+ * @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight, since 1.0
+ * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight, since 1.0
+ *
+ * Specifies variants of a font face based on their weight.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_font_weight {
+ CAIRO_FONT_WEIGHT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD
+} cairo_font_weight_t;
+
+/**
+ * cairo_subpixel_order_t:
+ * @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for
+ * for the target device, since 1.0
+ * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally
+ * with red at the left, since 1.0
+ * @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally
+ * with blue at the left, since 1.0
+ * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically
+ * with red at the top, since 1.0
+ * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically
+ * with blue at the top, since 1.0
+ *
+ * The subpixel order specifies the order of color elements within
+ * each pixel on the display device when rendering with an
+ * antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_subpixel_order {
+ CAIRO_SUBPIXEL_ORDER_DEFAULT,
+ CAIRO_SUBPIXEL_ORDER_RGB,
+ CAIRO_SUBPIXEL_ORDER_BGR,
+ CAIRO_SUBPIXEL_ORDER_VRGB,
+ CAIRO_SUBPIXEL_ORDER_VBGR
+} cairo_subpixel_order_t;
+
+/**
+ * cairo_hint_style_t:
+ * @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for
+ * font backend and target device, since 1.0
+ * @CAIRO_HINT_STYLE_NONE: Do not hint outlines, since 1.0
+ * @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve
+ * contrast while retaining good fidelity to the original
+ * shapes, since 1.0
+ * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength
+ * giving a compromise between fidelity to the original shapes
+ * and contrast, since 1.0
+ * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast, since 1.0
+ *
+ * Specifies the type of hinting to do on font outlines. Hinting
+ * is the process of fitting outlines to the pixel grid in order
+ * to improve the appearance of the result. Since hinting outlines
+ * involves distorting them, it also reduces the faithfulness
+ * to the original outline shapes. Not all of the outline hinting
+ * styles are supported by all font backends.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_hint_style {
+ CAIRO_HINT_STYLE_DEFAULT,
+ CAIRO_HINT_STYLE_NONE,
+ CAIRO_HINT_STYLE_SLIGHT,
+ CAIRO_HINT_STYLE_MEDIUM,
+ CAIRO_HINT_STYLE_FULL
+} cairo_hint_style_t;
+
+/**
+ * cairo_hint_metrics_t:
+ * @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default
+ * manner for the font backend and target device, since 1.0
+ * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics, since 1.0
+ * @CAIRO_HINT_METRICS_ON: Hint font metrics, since 1.0
+ *
+ * Specifies whether to hint font metrics; hinting font metrics
+ * means quantizing them so that they are integer values in
+ * device space. Doing this improves the consistency of
+ * letter and line spacing, however it also means that text
+ * will be laid out differently at different zoom factors.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_hint_metrics {
+ CAIRO_HINT_METRICS_DEFAULT,
+ CAIRO_HINT_METRICS_OFF,
+ CAIRO_HINT_METRICS_ON
+} cairo_hint_metrics_t;
+
+/**
+ * cairo_font_color_t:
+ * @CAIRO_FONT_COLOR_DEFAULT: default color, if the font has color,
+ * use font's color, otherwise, use user specified, since 1.12.14
+ * @CAIRO_FONT_COLOR_USER: always uses user's color, since 1.0
+ *
+ * When rendering text, specifies whether to use user's color set
+ * by cairo_set_source_XXXX() or use glyph's builtin color
+ *
+ * Since: 1.4
+ **/
+ typedef enum _cairo_font_color {
+ CAIRO_FONT_COLOR_DEFAULT,
+ CAIRO_FONT_COLOR_USER
+ } cairo_font_color_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
+ * <function>cairo_font_options_set_<emphasis>feature_name</emphasis>()</function> and
+ * <function>cairo_font_options_get_<emphasis>feature_name</emphasis>()</function>, like
+ * cairo_font_options_set_antialias() and
+ * cairo_font_options_get_antialias().
+ *
+ * New features may be added to a #cairo_font_options_t in the
+ * future. For this reason, cairo_font_options_copy(),
+ * cairo_font_options_equal(), cairo_font_options_merge(), and
+ * cairo_font_options_hash() should be used to copy, check
+ * for equality, merge, or compute a hash value of
+ * #cairo_font_options_t objects.
+ *
+ * Since: 1.0
+ **/
+typedef struct _cairo_font_options cairo_font_options_t;
+
+cairo_public cairo_font_options_t *
+cairo_font_options_create (void);
+
+cairo_public cairo_font_options_t *
+cairo_font_options_copy (const cairo_font_options_t *original);
+
+cairo_public void
+cairo_font_options_destroy (cairo_font_options_t *options);
+
+cairo_public cairo_status_t
+cairo_font_options_status (cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_merge (cairo_font_options_t *options,
+ const cairo_font_options_t *other);
+cairo_public cairo_bool_t
+cairo_font_options_equal (const cairo_font_options_t *options,
+ const cairo_font_options_t *other);
+
+cairo_public unsigned long
+cairo_font_options_hash (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_antialias (cairo_font_options_t *options,
+ cairo_antialias_t antialias);
+cairo_public cairo_antialias_t
+cairo_font_options_get_antialias (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_subpixel_order (cairo_font_options_t *options,
+ cairo_subpixel_order_t subpixel_order);
+cairo_public cairo_subpixel_order_t
+cairo_font_options_get_subpixel_order (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_hint_style (cairo_font_options_t *options,
+ cairo_hint_style_t hint_style);
+cairo_public cairo_hint_style_t
+cairo_font_options_get_hint_style (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_hint_metrics (cairo_font_options_t *options,
+ cairo_hint_metrics_t hint_metrics);
+cairo_public cairo_hint_metrics_t
+cairo_font_options_get_hint_metrics (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_font_color (cairo_font_options_t *options,
+cairo_font_color_t font_color);
+
+cairo_public cairo_font_color_t
+cairo_font_options_get_font_color (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 (Since: 1.2)
+ * @CAIRO_FONT_TYPE_FT: The font is of type FreeType (Since: 1.2)
+ * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 (Since: 1.2)
+ * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6, in 1.2 and
+ * 1.4 it was named CAIRO_FONT_TYPE_ATSUI)
+ * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8)
+ *
+ * #cairo_font_type_t is used to describe the type of a given font
+ * face or scaled font. The font types are also known as "font
+ * backends" within cairo.
+ *
+ * The type of a font face is determined by the function used to
+ * create it, which will generally be of the form
+ * <function>cairo_<emphasis>type</emphasis>_font_face_create(<!-- -->)</function>.
+ * 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
+ * <function>cairo_<emphasis>type</emphasis>_scaled_font(<!-- -->)</function>
+ * 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_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 caller will also free
+ * the original value of @glyphs, so the callback shouldn't do so.
+ * The callback should populate the glyph indices and positions (in font space)
+ * assuming that the text is to be shown at the origin.
+ *
+ * If @clusters is not %NULL, @num_clusters and @cluster_flags are also
+ * non-%NULL, and cluster mapping should be computed. The semantics of how
+ * cluster array allocation works is similar to the glyph array. That is,
+ * if @clusters initially points to a non-%NULL value, that array may be used
+ * as a cluster buffer, and @num_clusters points to the number of cluster
+ * entries available there. If the provided cluster array is too short for
+ * the conversion (or for convenience), a new cluster array may be allocated
+ * using cairo_text_cluster_allocate() and placed in @clusters. In this case,
+ * the original value of @clusters will still be freed by the caller. Upon
+ * return, @num_clusters should contain the number of generated clusters.
+ * If the value @clusters points at has changed after the call, the caller
+ * will free the allocated cluster array using cairo_text_cluster_free().
+ *
+ * The callback is optional. If @num_glyphs is negative upon
+ * the callback returning or if the return value
+ * is %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the unicode_to_glyph callback
+ * is tried. See #cairo_user_scaled_font_unicode_to_glyph_func_t.
+ *
+ * Note: While cairo does not impose any limitation on glyph indices,
+ * some applications may assume that a glyph index fits in a 16-bit
+ * unsigned integer. As such, it is advised that user-fonts keep their
+ * glyphs in the 0 to 65535 range. Furthermore, some applications may
+ * assume that glyph 0 is a special glyph-not-found glyph. User-fonts
+ * are advised to use glyph 0 for such purposes and do not use that
+ * glyph value for other purposes.
+ *
+ * Returns: %CAIRO_STATUS_SUCCESS upon success,
+ * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried,
+ * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
+ *
+ * Since: 1.8
+ **/
+typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t *scaled_font,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs,
+ cairo_text_cluster_t **clusters,
+ int *num_clusters,
+ cairo_text_cluster_flags_t *cluster_flags);
+
+/**
+ * cairo_user_scaled_font_unicode_to_glyph_func_t:
+ * @scaled_font: the scaled-font being created
+ * @unicode: input unicode character code-point
+ * @glyph_index: output glyph index
+ *
+ * #cairo_user_scaled_font_unicode_to_glyph_func_t is the type of function which
+ * is called to convert an input Unicode character to a single glyph.
+ * This is used by the cairo_show_text() operation.
+ *
+ * This callback is used to provide the same functionality as the
+ * text_to_glyphs callback does (see #cairo_user_scaled_font_text_to_glyphs_func_t)
+ * but has much less control on the output,
+ * in exchange for increased ease of use. The inherent assumption to using
+ * this callback is that each character maps to one glyph, and that the
+ * mapping is context independent. It also assumes that glyphs are positioned
+ * according to their advance width. These mean no ligatures, kerning, or
+ * complex scripts can be implemented using this callback.
+ *
+ * The callback is optional, and only used if text_to_glyphs callback is not
+ * set or fails to return glyphs. If this callback is not set or if it returns
+ * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, an identity mapping from Unicode
+ * code-points to glyph indices is assumed.
+ *
+ * Note: While cairo does not impose any limitation on glyph indices,
+ * some applications may assume that a glyph index fits in a 16-bit
+ * unsigned integer. As such, it is advised that user-fonts keep their
+ * glyphs in the 0 to 65535 range. Furthermore, some applications may
+ * assume that glyph 0 is a special glyph-not-found glyph. User-fonts
+ * are advised to use glyph 0 for such purposes and do not use that
+ * glyph value for other purposes.
+ *
+ * Returns: %CAIRO_STATUS_SUCCESS upon success,
+ * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried,
+ * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
+ *
+ * Since: 1.8
+ **/
+typedef cairo_status_t (*cairo_user_scaled_font_unicode_to_glyph_func_t) (cairo_scaled_font_t *scaled_font,
+ unsigned long unicode,
+ unsigned long *glyph_index);
+
+/* User-font method setters */
+
+cairo_public void
+cairo_user_font_face_set_init_func (cairo_font_face_t *font_face,
+ cairo_user_scaled_font_init_func_t init_func);
+
+cairo_public void
+cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face,
+ cairo_user_scaled_font_render_glyph_func_t render_glyph_func);
+
+cairo_public void
+cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face,
+ cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func);
+
+cairo_public void
+cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face,
+ cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func);
+
+/* User-font method getters */
+
+cairo_public cairo_user_scaled_font_init_func_t
+cairo_user_font_face_get_init_func (cairo_font_face_t *font_face);
+
+cairo_public cairo_user_scaled_font_render_glyph_func_t
+cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face);
+
+cairo_public cairo_user_scaled_font_text_to_glyphs_func_t
+cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face);
+
+cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t
+cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face);
+
+
+/* Query functions */
+
+cairo_public cairo_operator_t
+cairo_get_operator (cairo_t *cr);
+
+cairo_public cairo_pattern_t *
+cairo_get_source (cairo_t *cr);
+
+cairo_public double
+cairo_get_tolerance (cairo_t *cr);
+
+cairo_public cairo_antialias_t
+cairo_get_antialias (cairo_t *cr);
+
+cairo_public cairo_bool_t
+cairo_has_current_point (cairo_t *cr);
+
+cairo_public void
+cairo_get_current_point (cairo_t *cr, double *x, double *y);
+
+cairo_public cairo_fill_rule_t
+cairo_get_fill_rule (cairo_t *cr);
+
+cairo_public double
+cairo_get_line_width (cairo_t *cr);
+
+cairo_public cairo_line_cap_t
+cairo_get_line_cap (cairo_t *cr);
+
+cairo_public cairo_line_join_t
+cairo_get_line_join (cairo_t *cr);
+
+cairo_public double
+cairo_get_miter_limit (cairo_t *cr);
+
+cairo_public int
+cairo_get_dash_count (cairo_t *cr);
+
+cairo_public void
+cairo_get_dash (cairo_t *cr, double *dashes, double *offset);
+
+cairo_public void
+cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix);
+
+cairo_public cairo_surface_t *
+cairo_get_target (cairo_t *cr);
+
+cairo_public cairo_surface_t *
+cairo_get_group_target (cairo_t *cr);
+
+/**
+ * cairo_path_data_type_t:
+ * @CAIRO_PATH_MOVE_TO: A move-to operation, since 1.0
+ * @CAIRO_PATH_LINE_TO: A line-to operation, since 1.0
+ * @CAIRO_PATH_CURVE_TO: A curve-to operation, since 1.0
+ * @CAIRO_PATH_CLOSE_PATH: A close-path operation, since 1.0
+ *
+ * #cairo_path_data_t is used to describe the type of one portion
+ * of a path when represented as a #cairo_path_t.
+ * See #cairo_path_data_t for details.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_path_data_type {
+ CAIRO_PATH_MOVE_TO,
+ CAIRO_PATH_LINE_TO,
+ CAIRO_PATH_CURVE_TO,
+ CAIRO_PATH_CLOSE_PATH
+} cairo_path_data_type_t;
+
+/**
+ * cairo_path_data_t:
+ *
+ * #cairo_path_data_t is used to represent the path data inside a
+ * #cairo_path_t.
+ *
+ * The data structure is designed to try to balance the demands of
+ * efficiency and ease-of-use. A path is represented as an array of
+ * #cairo_path_data_t, which is a union of headers and points.
+ *
+ * Each portion of the path is represented by one or more elements in
+ * the array, (one header followed by 0 or more points). The length
+ * value of the header is the number of array elements for the current
+ * portion including the header, (ie. length == 1 + # of points), and
+ * where the number of points for each element type is as follows:
+ *
+ * <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.
+ *
+ * Since: 1.0
+ **/
+typedef union _cairo_path_data_t cairo_path_data_t;
+union _cairo_path_data_t {
+ struct {
+ cairo_path_data_type_t type;
+ int length;
+ } header;
+ struct {
+ double x, y;
+ } point;
+};
+
+/**
+ * cairo_path_t:
+ * @status: the current error status
+ * @data: the elements in the path
+ * @num_data: the number of elements in the data array
+ *
+ * A data structure for holding a path. This data structure serves as
+ * the return value for cairo_copy_path() and
+ * cairo_copy_path_flat() as well the input value for
+ * cairo_append_path().
+ *
+ * See #cairo_path_data_t for hints on how to iterate over the
+ * actual data within the path.
+ *
+ * The num_data member gives the number of elements in the data
+ * array. This number is larger than the number of independent path
+ * portions (defined in #cairo_path_data_type_t), since the data
+ * includes both headers and coordinates for each portion.
+ *
+ * Since: 1.0
+ **/
+typedef struct cairo_path {
+ cairo_status_t status;
+ cairo_path_data_t *data;
+ int num_data;
+} cairo_path_t;
+
+cairo_public cairo_path_t *
+cairo_copy_path (cairo_t *cr);
+
+cairo_public cairo_path_t *
+cairo_copy_path_flat (cairo_t *cr);
+
+cairo_public void
+cairo_append_path (cairo_t *cr,
+ const cairo_path_t *path);
+
+cairo_public void
+cairo_path_destroy (cairo_path_t *path);
+
+/* Error status queries */
+
+cairo_public cairo_status_t
+cairo_status (cairo_t *cr);
+
+cairo_public const char *
+cairo_status_to_string (cairo_status_t status);
+
+/* Backend device manipulation */
+
+cairo_public cairo_device_t *
+cairo_device_reference (cairo_device_t *device);
+
+/**
+ * cairo_device_type_t:
+ * @CAIRO_DEVICE_TYPE_DRM: The device is of type Direct Render Manager, since 1.10
+ * @CAIRO_DEVICE_TYPE_GL: The device is of type OpenGL, since 1.10
+ * @CAIRO_DEVICE_TYPE_SCRIPT: The device is of type script, since 1.10
+ * @CAIRO_DEVICE_TYPE_XCB: The device is of type xcb, since 1.10
+ * @CAIRO_DEVICE_TYPE_XLIB: The device is of type xlib, since 1.10
+ * @CAIRO_DEVICE_TYPE_XML: The device is of type XML, since 1.10
+ * @CAIRO_DEVICE_TYPE_COGL: The device is of type cogl, since 1.12
+ * @CAIRO_DEVICE_TYPE_WIN32: The device is of type win32, since 1.12
+ * @CAIRO_DEVICE_TYPE_INVALID: The device is invalid, since 1.10
+ *
+ * #cairo_device_type_t is used to describe the type of a given
+ * device. The devices types are also known as "backends" within cairo.
+ *
+ * The device type can be queried with cairo_device_get_type()
+ *
+ * The various #cairo_device_t functions can be used with devices of
+ * any type, but some backends also provide type-specific functions
+ * that must only be called with a device of the appropriate
+ * type. These functions have names that begin with
+ * <literal>cairo_<emphasis>type</emphasis>_device</literal> such as
+ * cairo_xcb_device_debug_cap_xrender_version().
+ *
+ * The behavior of calling a type-specific function with a device of
+ * the wrong type is undefined.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.10
+ **/
+typedef enum _cairo_device_type {
+ CAIRO_DEVICE_TYPE_DRM,
+ CAIRO_DEVICE_TYPE_GL,
+ CAIRO_DEVICE_TYPE_SCRIPT,
+ CAIRO_DEVICE_TYPE_XCB,
+ CAIRO_DEVICE_TYPE_XLIB,
+ CAIRO_DEVICE_TYPE_XML,
+ CAIRO_DEVICE_TYPE_COGL,
+ CAIRO_DEVICE_TYPE_WIN32,
+
+ CAIRO_DEVICE_TYPE_INVALID = -1
+} cairo_device_type_t;
+
+cairo_public cairo_device_type_t
+cairo_device_get_type (cairo_device_t *device);
+
+cairo_public cairo_status_t
+cairo_device_status (cairo_device_t *device);
+
+cairo_public cairo_status_t
+cairo_device_acquire (cairo_device_t *device);
+
+cairo_public void
+cairo_device_release (cairo_device_t *device);
+
+cairo_public void
+cairo_device_flush (cairo_device_t *device);
+
+cairo_public void
+cairo_device_finish (cairo_device_t *device);
+
+cairo_public void
+cairo_device_destroy (cairo_device_t *device);
+
+cairo_public unsigned int
+cairo_device_get_reference_count (cairo_device_t *device);
+
+cairo_public void *
+cairo_device_get_user_data (cairo_device_t *device,
+ const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_device_set_user_data (cairo_device_t *device,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy);
+
+
+/* Surface manipulation */
+
+cairo_public cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_public cairo_surface_t *
+cairo_surface_create_similar_image (cairo_surface_t *other,
+ cairo_format_t format,
+ int width,
+ int height);
+
+cairo_public cairo_surface_t *
+cairo_surface_map_to_image (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents);
+
+cairo_public void
+cairo_surface_unmap_image (cairo_surface_t *surface,
+ cairo_surface_t *image);
+
+cairo_public cairo_surface_t *
+cairo_surface_create_for_rectangle (cairo_surface_t *target,
+ double x,
+ double y,
+ double width,
+ double height);
+
+typedef enum {
+ CAIRO_SURFACE_OBSERVER_NORMAL = 0,
+ CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS = 0x1
+} cairo_surface_observer_mode_t;
+
+cairo_public cairo_surface_t *
+cairo_surface_create_observer (cairo_surface_t *target,
+ cairo_surface_observer_mode_t mode);
+
+typedef void (*cairo_surface_observer_callback_t) (cairo_surface_t *observer,
+ cairo_surface_t *target,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
+ cairo_surface_observer_callback_t func,
+ void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_print (cairo_surface_t *surface,
+ cairo_write_func_t write_func,
+ void *closure);
+cairo_public double
+cairo_surface_observer_elapsed (cairo_surface_t *surface);
+
+cairo_public cairo_status_t
+cairo_device_observer_print (cairo_device_t *device,
+ cairo_write_func_t write_func,
+ void *closure);
+
+cairo_public double
+cairo_device_observer_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_paint_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_mask_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_fill_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_stroke_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_glyphs_elapsed (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_surface_reference (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_finish (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_destroy (cairo_surface_t *surface);
+
+cairo_public cairo_device_t *
+cairo_surface_get_device (cairo_surface_t *surface);
+
+cairo_public unsigned int
+cairo_surface_get_reference_count (cairo_surface_t *surface);
+
+cairo_public cairo_status_t
+cairo_surface_status (cairo_surface_t *surface);
+
+/**
+ * cairo_surface_type_t:
+ * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image, since 1.2
+ * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf, since 1.2
+ * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps, since 1.2
+ * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib, since 1.2
+ * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb, since 1.2
+ * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz, since 1.2
+ * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz, since 1.2
+ * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32, since 1.2
+ * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos, since 1.2
+ * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb, since 1.2
+ * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg, since 1.2
+ * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2, since 1.4
+ * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface, since 1.6
+ * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image, since 1.6
+ * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10
+ * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10
+ * @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10
+ * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10
+ * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10
+ * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10
+ * @CAIRO_SURFACE_TYPE_TEE: The surface is of type 'tee' (a multiplexing surface), since 1.10
+ * @CAIRO_SURFACE_TYPE_XML: The surface is of type XML (for debugging), since 1.10
+ * @CAIRO_SURFACE_TYPE_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_COGL: This surface is of type Cogl, since 1.12
+ *
+ * #cairo_surface_type_t is used to describe the type of a given
+ * surface. The surface types are also known as "backends" or "surface
+ * backends" within cairo.
+ *
+ * The type of a surface is determined by the function used to create
+ * it, which will generally be of the form
+ * <function>cairo_<emphasis>type</emphasis>_surface_create(<!-- -->)</function>,
+ * (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
+ * <literal>cairo_<emphasis>type</emphasis>_surface</literal> such as cairo_image_surface_get_width().
+ *
+ * The behavior of calling a type-specific function with a surface of
+ * the wrong type is undefined.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.2
+ **/
+typedef enum _cairo_surface_type {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ CAIRO_SURFACE_TYPE_PDF,
+ CAIRO_SURFACE_TYPE_PS,
+ CAIRO_SURFACE_TYPE_XLIB,
+ CAIRO_SURFACE_TYPE_XCB,
+ CAIRO_SURFACE_TYPE_GLITZ,
+ CAIRO_SURFACE_TYPE_QUARTZ,
+ CAIRO_SURFACE_TYPE_WIN32,
+ CAIRO_SURFACE_TYPE_BEOS,
+ CAIRO_SURFACE_TYPE_DIRECTFB,
+ CAIRO_SURFACE_TYPE_SVG,
+ CAIRO_SURFACE_TYPE_OS2,
+ CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+ CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
+ CAIRO_SURFACE_TYPE_SCRIPT,
+ CAIRO_SURFACE_TYPE_QT,
+ CAIRO_SURFACE_TYPE_RECORDING,
+ CAIRO_SURFACE_TYPE_VG,
+ CAIRO_SURFACE_TYPE_GL,
+ CAIRO_SURFACE_TYPE_DRM,
+ CAIRO_SURFACE_TYPE_TEE,
+ CAIRO_SURFACE_TYPE_XML,
+ CAIRO_SURFACE_TYPE_SKIA,
+ CAIRO_SURFACE_TYPE_SUBSURFACE,
+ CAIRO_SURFACE_TYPE_COGL,
+ CAIRO_SURFACE_TYPE_TG,
+} cairo_surface_type_t;
+
+cairo_public cairo_surface_type_t
+cairo_surface_get_type (cairo_surface_t *surface);
+
+cairo_public cairo_content_t
+cairo_surface_get_content (cairo_surface_t *surface);
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+
+cairo_public cairo_status_t
+cairo_surface_write_to_png (cairo_surface_t *surface,
+ const char *filename);
+
+cairo_public cairo_status_t
+cairo_surface_write_to_png_stream (cairo_surface_t *surface,
+ cairo_write_func_t write_func,
+ void *closure);
+
+#endif
+
+cairo_public void *
+cairo_surface_get_user_data (cairo_surface_t *surface,
+ const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_surface_set_user_data (cairo_surface_t *surface,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy);
+
+#define CAIRO_MIME_TYPE_JPEG "image/jpeg"
+#define CAIRO_MIME_TYPE_PNG "image/png"
+#define CAIRO_MIME_TYPE_JP2 "image/jp2"
+#define CAIRO_MIME_TYPE_URI "text/x-uri"
+#define CAIRO_MIME_TYPE_UNIQUE_ID "application/x-cairo.uuid"
+
+cairo_public void
+cairo_surface_get_mime_data (cairo_surface_t *surface,
+ const char *mime_type,
+ const unsigned char **data,
+ unsigned long *length);
+
+cairo_public cairo_status_t
+cairo_surface_set_mime_data (cairo_surface_t *surface,
+ const char *mime_type,
+ const unsigned char *data,
+ unsigned long length,
+ cairo_destroy_func_t destroy,
+ void *closure);
+
+cairo_public cairo_bool_t
+cairo_surface_supports_mime_type (cairo_surface_t *surface,
+ const char *mime_type);
+
+cairo_public void
+cairo_surface_get_font_options (cairo_surface_t *surface,
+ cairo_font_options_t *options);
+
+cairo_public void
+cairo_surface_flush (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_mark_dirty (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
+ int x,
+ int y,
+ int width,
+ int height);
+
+cairo_public void
+cairo_surface_set_device_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);
+
+/* Image-surface functions */
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create (cairo_format_t format,
+ int width,
+ int height);
+
+cairo_public int
+cairo_format_stride_for_width (cairo_format_t format,
+ int width);
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride);
+
+cairo_public unsigned char *
+cairo_image_surface_get_data (cairo_surface_t *surface);
+
+cairo_public cairo_format_t
+cairo_image_surface_get_format (cairo_surface_t *surface);
+
+cairo_public int
+cairo_image_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_image_surface_get_height (cairo_surface_t *surface);
+
+cairo_public int
+cairo_image_surface_get_stride (cairo_surface_t *surface);
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create_from_png (const char *filename);
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
+ void *closure);
+
+#endif
+
+/* Recording-surface functions */
+
+cairo_public cairo_surface_t *
+cairo_recording_surface_create (cairo_content_t content,
+ const cairo_rectangle_t *extents);
+
+cairo_public void
+cairo_recording_surface_ink_extents (cairo_surface_t *surface,
+ double *x0,
+ double *y0,
+ double *width,
+ double *height);
+
+cairo_public cairo_bool_t
+cairo_recording_surface_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_t *extents);
+
+/* raster-source pattern (callback) functions */
+
+/**
+ * cairo_raster_source_acquire_func_t:
+ * @pattern: the pattern being rendered from
+ * @callback_data: the user data supplied during creation
+ * @target: the rendering target surface
+ * @extents: rectangular region of interest in pixels in sample space
+ *
+ * #cairo_raster_source_acquire_func_t is the type of function which is
+ * called when a pattern is being rendered from. It should create a surface
+ * that provides the pixel data for the region of interest as defined by
+ * extents, though the surface itself does not have to be limited to that
+ * area. For convenience the surface should probably be of image type,
+ * created with cairo_surface_create_similar_image() for the target (which
+ * enables the number of copies to be reduced during transfer to the
+ * device). Another option, might be to return a similar surface to the
+ * target for explicit handling by the application of a set of cached sources
+ * on the device. The region of sample data provided should be defined using
+ * cairo_surface_set_device_offset() to specify the top-left corner of the
+ * sample data (along with width and height of the surface).
+ *
+ * Returns: a #cairo_surface_t
+ *
+ * Since: 1.12
+ **/
+typedef cairo_surface_t *
+(*cairo_raster_source_acquire_func_t) (cairo_pattern_t *pattern,
+ void *callback_data,
+ cairo_surface_t *target,
+ const cairo_rectangle_int_t *extents);
+
+/**
+ * cairo_raster_source_release_func_t:
+ * @pattern: the pattern being rendered from
+ * @callback_data: the user data supplied during creation
+ * @surface: the surface created during acquire
+ *
+ * #cairo_raster_source_release_func_t is the type of function which is
+ * called when the pixel data is no longer being access by the pattern
+ * for the rendering operation. Typically this function will simply
+ * destroy the surface created during acquire.
+ *
+ * Since: 1.12
+ **/
+typedef void
+(*cairo_raster_source_release_func_t) (cairo_pattern_t *pattern,
+ void *callback_data,
+ cairo_surface_t *surface);
+
+/**
+ * cairo_raster_source_snapshot_func_t:
+ * @pattern: the pattern being rendered from
+ * @callback_data: the user data supplied during creation
+ *
+ * #cairo_raster_source_snapshot_func_t is the type of function which is
+ * called when the pixel data needs to be preserved for later use
+ * during printing. This pattern will be accessed again later, and it
+ * is expected to provide the pixel data that was current at the time
+ * of snapshotting.
+ *
+ * Return value: CAIRO_STATUS_SUCCESS on success, or one of the
+ * #cairo_status_t error codes for failure.
+ *
+ * Since: 1.12
+ **/
+typedef cairo_status_t
+(*cairo_raster_source_snapshot_func_t) (cairo_pattern_t *pattern,
+ void *callback_data);
+
+/**
+ * cairo_raster_source_copy_func_t:
+ * @pattern: the #cairo_pattern_t that was copied to
+ * @callback_data: the user data supplied during creation
+ * @other: the #cairo_pattern_t being used as the source for the copy
+ *
+ * #cairo_raster_source_copy_func_t is the type of function which is
+ * called when the pattern gets copied as a normal part of rendering.
+ *
+ * Return value: CAIRO_STATUS_SUCCESS on success, or one of the
+ * #cairo_status_t error codes for failure.
+ *
+ * Since: 1.12
+ **/
+typedef cairo_status_t
+(*cairo_raster_source_copy_func_t) (cairo_pattern_t *pattern,
+ void *callback_data,
+ const cairo_pattern_t *other);
+
+/**
+ * cairo_raster_source_finish_func_t:
+ * @pattern: the pattern being rendered from
+ * @callback_data: the user data supplied during creation
+ *
+ * #cairo_raster_source_finish_func_t is the type of function which is
+ * called when the pattern (or a copy thereof) is no longer required.
+ *
+ * Since: 1.12
+ **/
+typedef void
+(*cairo_raster_source_finish_func_t) (cairo_pattern_t *pattern,
+ void *callback_data);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_raster_source (void *user_data,
+ cairo_content_t content,
+ int width, int height);
+
+cairo_public void
+cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *pattern,
+ void *data);
+
+cairo_public void *
+cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_raster_source_pattern_set_acquire (cairo_pattern_t *pattern,
+ cairo_raster_source_acquire_func_t acquire,
+ cairo_raster_source_release_func_t release);
+
+cairo_public void
+cairo_raster_source_pattern_get_acquire (cairo_pattern_t *pattern,
+ cairo_raster_source_acquire_func_t *acquire,
+ cairo_raster_source_release_func_t *release);
+cairo_public void
+cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *pattern,
+ cairo_raster_source_snapshot_func_t snapshot);
+
+cairo_public cairo_raster_source_snapshot_func_t
+cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_raster_source_pattern_set_copy (cairo_pattern_t *pattern,
+ cairo_raster_source_copy_func_t copy);
+
+cairo_public cairo_raster_source_copy_func_t
+cairo_raster_source_pattern_get_copy (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_raster_source_pattern_set_finish (cairo_pattern_t *pattern,
+ cairo_raster_source_finish_func_t finish);
+
+cairo_public cairo_raster_source_finish_func_t
+cairo_raster_source_pattern_get_finish (cairo_pattern_t *pattern);
+
+/* Pattern creation functions */
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_rgb (double red, double green, double blue);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_rgba (double red, double green, double blue,
+ double alpha);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_linear (double x0, double y0,
+ double x1, double y1);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_radial (double cx0, double cy0, double radius0,
+ double cx1, double cy1, double radius1);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_mesh (void);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_reference (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_pattern_destroy (cairo_pattern_t *pattern);
+
+cairo_public unsigned int
+cairo_pattern_get_reference_count (cairo_pattern_t *pattern);
+
+cairo_public cairo_status_t
+cairo_pattern_status (cairo_pattern_t *pattern);
+
+cairo_public void *
+cairo_pattern_get_user_data (cairo_pattern_t *pattern,
+ const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_pattern_set_user_data (cairo_pattern_t *pattern,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy);
+
+/**
+ * cairo_pattern_type_t:
+ * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform)
+ * color. It may be opaque or translucent, since 1.2.
+ * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image), since 1.2.
+ * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient, since 1.2.
+ * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient, since 1.2.
+ * @CAIRO_PATTERN_TYPE_MESH: The pattern is a mesh, since 1.12.
+ * @CAIRO_PATTERN_TYPE_RASTER_SOURCE: The pattern is a user pattern providing raster data, since 1.12.
+ *
+ * #cairo_pattern_type_t is used to describe the type of a given pattern.
+ *
+ * The type of a pattern is determined by the function used to create
+ * it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba()
+ * functions create SOLID patterns. The remaining
+ * cairo_pattern_create<!-- --> functions map to pattern types in obvious
+ * ways.
+ *
+ * The pattern type can be queried with cairo_pattern_get_type()
+ *
+ * Most #cairo_pattern_t functions can be called with a pattern of any
+ * type, (though trying to change the extend or filter for a solid
+ * pattern will have no effect). A notable exception is
+ * cairo_pattern_add_color_stop_rgb() and
+ * cairo_pattern_add_color_stop_rgba() which must only be called with
+ * gradient patterns (either LINEAR or RADIAL). Otherwise the pattern
+ * will be shutdown and put into an error state.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.2
+ **/
+typedef enum _cairo_pattern_type {
+ CAIRO_PATTERN_TYPE_SOLID,
+ CAIRO_PATTERN_TYPE_SURFACE,
+ CAIRO_PATTERN_TYPE_LINEAR,
+ CAIRO_PATTERN_TYPE_RADIAL,
+ CAIRO_PATTERN_TYPE_MESH,
+ CAIRO_PATTERN_TYPE_RASTER_SOURCE
+} cairo_pattern_type_t;
+
+cairo_public cairo_pattern_type_t
+cairo_pattern_get_type (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
+ double offset,
+ double red, double green, double blue);
+
+cairo_public void
+cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
+ double offset,
+ double red, double green, double blue,
+ double alpha);
+
+cairo_public void
+cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3);
+
+cairo_public void
+cairo_mesh_pattern_line_to (cairo_pattern_t *pattern,
+ double x, double y);
+
+cairo_public void
+cairo_mesh_pattern_move_to (cairo_pattern_t *pattern,
+ double x, double y);
+
+cairo_public void
+cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern,
+ unsigned int point_num,
+ double x, double y);
+
+cairo_public void
+cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern,
+ unsigned int corner_num,
+ double red, double green, double blue);
+
+cairo_public void
+cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern,
+ unsigned int corner_num,
+ double red, double green, double blue,
+ double alpha);
+
+cairo_public void
+cairo_pattern_set_matrix (cairo_pattern_t *pattern,
+ const cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_pattern_get_matrix (cairo_pattern_t *pattern,
+ cairo_matrix_t *matrix);
+
+/**
+ * cairo_extend_t:
+ * @CAIRO_EXTEND_NONE: pixels outside of the source pattern
+ * are fully transparent (Since 1.0)
+ * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating (Since 1.0)
+ * @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting
+ * at the edges (Since 1.0; but only implemented for surface patterns since 1.6)
+ * @CAIRO_EXTEND_PAD: pixels outside of the pattern copy
+ * the closest pixel from the source (Since 1.2; but only
+ * implemented for surface patterns since 1.6)
+ *
+ * #cairo_extend_t is used to describe how pattern color/alpha will be
+ * determined for areas "outside" the pattern's natural area, (for
+ * example, outside the surface bounds or outside the gradient
+ * geometry).
+ *
+ * Mesh patterns are not affected by the extend mode.
+ *
+ * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
+ * and %CAIRO_EXTEND_PAD for gradient patterns.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_extend {
+ CAIRO_EXTEND_NONE,
+ CAIRO_EXTEND_REPEAT,
+ CAIRO_EXTEND_REFLECT,
+ CAIRO_EXTEND_PAD
+} cairo_extend_t;
+
+cairo_public void
+cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend);
+
+cairo_public cairo_extend_t
+cairo_pattern_get_extend (cairo_pattern_t *pattern);
+
+/**
+ * cairo_filter_t:
+ * @CAIRO_FILTER_FAST: A high-performance filter, with quality similar
+ * to %CAIRO_FILTER_NEAREST (Since 1.0)
+ * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality
+ * similar to %CAIRO_FILTER_BILINEAR (Since 1.0)
+ * @CAIRO_FILTER_BEST: The highest-quality available, performance may
+ * not be suitable for interactive use. (Since 1.0)
+ * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering (Since 1.0)
+ * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions (Since 1.0)
+ * @CAIRO_FILTER_GAUSSIAN: This filter value is currently
+ * unimplemented, and should not be used in current code. (Since 1.0)
+ *
+ * #cairo_filter_t is used to indicate what filtering should be
+ * applied when reading pixel values from patterns. See
+ * cairo_pattern_set_filter() for indicating the desired filter to be
+ * used with a particular pattern.
+ *
+ * Since: 1.0
+ **/
+typedef enum _cairo_filter {
+ CAIRO_FILTER_FAST,
+ CAIRO_FILTER_GOOD,
+ CAIRO_FILTER_BEST,
+ CAIRO_FILTER_NEAREST,
+ CAIRO_FILTER_BILINEAR,
+ CAIRO_FILTER_GAUSSIAN
+} cairo_filter_t;
+
+cairo_public void
+cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter);
+
+cairo_public cairo_filter_t
+cairo_pattern_get_filter (cairo_pattern_t *pattern);
+
+cairo_public cairo_status_t
+cairo_pattern_set_sigma (cairo_pattern_t *pattern,
+ const double x_sigma,
+ const double y_sigma);
+
+cairo_public cairo_status_t
+cairo_pattern_get_sigma (cairo_pattern_t *pattern,
+ double *x_sigma,
+ double *y_sigma);
+
+/* since 1.12.14 */
+typedef enum _cairo_shadow_type {
+ CAIRO_SHADOW_NONE = 0,
+ CAIRO_SHADOW_DROP,
+ CAIRO_SHADOW_INSET
+} cairo_shadow_type_t;
+
+cairo_public void
+cairo_set_shadow (cairo_t *cr, cairo_shadow_type_t shadow);
+
+cairo_public void
+cairo_set_shadow_offset (cairo_t *cr, double x_offset, double y_offset);
+
+cairo_public void
+cairo_set_shadow_rgb (cairo_t *cr, double red, double green, double blue);
+
+cairo_public void
+cairo_set_shadow_rgba (cairo_t *cr, double red, double green,
+ double blue, double alpha);
+
+cairo_public void
+cairo_set_shadow_blur (cairo_t *cr, double x_blur, double y_blur);
+
+cairo_public void
+cairo_set_draw_shadow_only (cairo_t *cr, cairo_bool_t draw_shadow_only);
+
+cairo_public void
+cairo_shadow_enable_cache (cairo_t *cr, cairo_bool_t enable);
+
+cairo_public void
+cairo_set_path_is_inset_shadow_with_spread (cairo_t *cr,
+ cairo_bool_t is_spread_path);
+
+cairo_public cairo_status_t
+cairo_pattern_get_rgba (cairo_pattern_t *pattern,
+ double *red, double *green,
+ double *blue, double *alpha);
+
+cairo_public cairo_status_t
+cairo_pattern_get_surface (cairo_pattern_t *pattern,
+ cairo_surface_t **surface);
+
+
+cairo_public cairo_status_t
+cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern,
+ int index, double *offset,
+ double *red, double *green,
+ double *blue, double *alpha);
+
+cairo_public cairo_status_t
+cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern,
+ int *count);
+
+cairo_public cairo_status_t
+cairo_pattern_get_linear_points (cairo_pattern_t *pattern,
+ double *x0, double *y0,
+ double *x1, double *y1);
+
+cairo_public cairo_status_t
+cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
+ double *x0, double *y0, double *r0,
+ double *x1, double *y1, double *r1);
+
+cairo_public cairo_status_t
+cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern,
+ unsigned int *count);
+
+cairo_public cairo_path_t *
+cairo_mesh_pattern_get_path (cairo_pattern_t *pattern,
+ unsigned int patch_num);
+
+cairo_public cairo_status_t
+cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern,
+ unsigned int patch_num,
+ unsigned int corner_num,
+ double *red, double *green,
+ double *blue, double *alpha);
+
+cairo_public cairo_status_t
+cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
+ unsigned int patch_num,
+ unsigned int point_num,
+ double *x, double *y);
+
+/* Matrix functions */
+
+cairo_public void
+cairo_matrix_init (cairo_matrix_t *matrix,
+ double xx, double yx,
+ double xy, double yy,
+ double x0, double y0);
+
+cairo_public void
+cairo_matrix_init_identity (cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_matrix_init_translate (cairo_matrix_t *matrix,
+ double tx, double ty);
+
+cairo_public void
+cairo_matrix_init_scale (cairo_matrix_t *matrix,
+ double sx, double sy);
+
+cairo_public void
+cairo_matrix_init_rotate (cairo_matrix_t *matrix,
+ double radians);
+
+cairo_public void
+cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty);
+
+cairo_public void
+cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy);
+
+cairo_public void
+cairo_matrix_rotate (cairo_matrix_t *matrix, double radians);
+
+cairo_public cairo_status_t
+cairo_matrix_invert (cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_matrix_multiply (cairo_matrix_t *result,
+ const cairo_matrix_t *a,
+ const cairo_matrix_t *b);
+
+cairo_public void
+cairo_matrix_transform_distance (const cairo_matrix_t *matrix,
+ double *dx, double *dy);
+
+cairo_public void
+cairo_matrix_transform_point (const cairo_matrix_t *matrix,
+ double *x, double *y);
+
+/* Region functions */
+
+/**
+ * cairo_region_t:
+ *
+ * A #cairo_region_t represents a set of integer-aligned rectangles.
+ *
+ * It allows set-theoretical operations like cairo_region_union() and
+ * cairo_region_intersect() to be performed on them.
+ *
+ * Memory management of #cairo_region_t is done with
+ * cairo_region_reference() and cairo_region_destroy().
+ *
+ * Since: 1.10
+ **/
+typedef struct _cairo_region cairo_region_t;
+
+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/src/cairo.pc.in b/src/cairo.pc.in
new file mode 100755
index 000000000..b361edf18
--- /dev/null
+++ b/src/cairo.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: cairo
+Description: Multi-platform 2D graphics library
+Version: @VERSION@
+
+@PKGCONFIG_REQUIRES@: @CAIRO_REQUIRES@
+Libs: -L${libdir} -lcairo
+Libs.private: @CAIRO_NONPKGCONFIG_LIBS@
+Cflags: -I${includedir}/cairo
diff --git a/src/cairoint.h b/src/cairoint.h
new file mode 100755
index 000000000..e4f483a3a
--- /dev/null
+++ b/src/cairoint.h
@@ -0,0 +1,2105 @@
+/* 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"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_PDF_SURFACE || \
+ CAIRO_HAS_PS_SURFACE || \
+ CAIRO_HAS_SCRIPT_SURFACE || \
+ CAIRO_HAS_XML_SURFACE
+#define CAIRO_HAS_DEFLATE_STREAM 1
+#endif
+
+#if CAIRO_HAS_PS_SURFACE || \
+ CAIRO_HAS_PDF_SURFACE || \
+ CAIRO_HAS_SVG_SURFACE || \
+ CAIRO_HAS_WIN32_SURFACE
+#define CAIRO_HAS_FONT_SUBSET 1
+#endif
+
+#if CAIRO_HAS_PS_SURFACE || \
+ CAIRO_HAS_PDF_SURFACE || \
+ CAIRO_HAS_FONT_SUBSET
+#define CAIRO_HAS_PDF_OPERATORS 1
+#endif
+
+CAIRO_BEGIN_DECLS
+
+#if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */
+cairo_private FILE *
+_cairo_win32_tmpfile (void);
+#define tmpfile() _cairo_win32_tmpfile()
+#endif
+
+#undef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#undef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.707106781186547524400844362104849039
+#endif
+
+#undef ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+
+#undef STRINGIFY
+#undef STRINGIFY_ARG
+#define STRINGIFY(macro_or_string) STRINGIFY_ARG (macro_or_string)
+#define STRINGIFY_ARG(contents) #contents
+
+#if defined (__GNUC__)
+#define cairo_container_of(ptr, type, member) ({ \
+ const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \
+ (type *) ((char *) mptr__ - offsetof (type, member)); \
+})
+#else
+#define cairo_container_of(ptr, type, member) \
+ ((type *)((char *) (ptr) - (char *) &((type *)0)->member))
+#endif
+
+
+#define ASSERT_NOT_REACHED \
+do { \
+ assert (!"reached"); \
+} while (0)
+#define COMPILE_TIME_ASSERT1(condition, line) \
+ typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1]
+#define COMPILE_TIME_ASSERT0(condition, line) COMPILE_TIME_ASSERT1(condition, line)
+#define COMPILE_TIME_ASSERT(condition) COMPILE_TIME_ASSERT0(condition, __LINE__)
+
+#define CAIRO_ALPHA_IS_CLEAR(alpha) ((alpha) <= ((double)0x00ff / (double)0xffff))
+#define CAIRO_ALPHA_SHORT_IS_CLEAR(alpha) ((alpha) <= 0x00ff)
+
+#define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff))
+#define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00)
+#define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0)
+
+#define CAIRO_COLOR_IS_CLEAR(color) CAIRO_ALPHA_SHORT_IS_CLEAR ((color)->alpha_short)
+#define CAIRO_COLOR_IS_OPAQUE(color) CAIRO_ALPHA_SHORT_IS_OPAQUE ((color)->alpha_short)
+
+/* Reverse the bits in a byte with 7 operations (no 64-bit):
+ * Devised by Sean Anderson, July 13, 2001.
+ * Source: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
+ */
+#define CAIRO_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16)
+
+/* Return the number of 1 bits in mask.
+ *
+ * GCC 3.4 supports a "population count" builtin, which on many targets is
+ * implemented with a single instruction. There is a fallback definition
+ * in libgcc in case a target does not have one, which should be just as
+ * good as the open-coded solution below, (which is "HACKMEM 169").
+ */
+static inline int cairo_const
+_cairo_popcount (uint32_t mask)
+{
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ return __builtin_popcount (mask);
+#else
+ register int y;
+
+ y = (mask >> 1) &033333333333;
+ y = mask - y - ((y >>1) & 033333333333);
+ return (((y + (y >> 3)) & 030707070707) % 077);
+#endif
+}
+
+static cairo_always_inline cairo_bool_t
+_cairo_is_little_endian (void)
+{
+ static const int i = 1;
+ return *((char *) &i) == 0x01;
+}
+
+#ifdef WORDS_BIGENDIAN
+#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c)
+#else
+#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) CAIRO_BITSWAP8(c)
+#endif
+
+#ifdef WORDS_BIGENDIAN
+
+#define cpu_to_be16(v) (v)
+#define be16_to_cpu(v) (v)
+#define cpu_to_be32(v) (v)
+#define be32_to_cpu(v) (v)
+
+#else
+
+static inline uint16_t cairo_const
+cpu_to_be16(uint16_t v)
+{
+ return (v << 8) | (v >> 8);
+}
+
+static inline uint16_t cairo_const
+be16_to_cpu(uint16_t v)
+{
+ return cpu_to_be16 (v);
+}
+
+static inline uint32_t cairo_const
+cpu_to_be32(uint32_t v)
+{
+ return (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"
+#include "cairo-surface-private.h"
+
+cairo_private void
+_cairo_box_from_doubles (cairo_box_t *box,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+cairo_private void
+_cairo_box_to_doubles (const cairo_box_t *box,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+cairo_private void
+_cairo_box_from_rectangle (cairo_box_t *box,
+ const cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_box_round_to_rectangle (const cairo_box_t *box,
+ cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_box_add_curve_to (cairo_box_t *extents,
+ const cairo_point_t *a,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d);
+
+cairo_private void
+_cairo_boxes_get_extents (const cairo_box_t *boxes,
+ int num_boxes,
+ cairo_box_t *extents);
+
+cairo_private extern const cairo_rectangle_int_t _cairo_empty_rectangle;
+cairo_private extern const cairo_rectangle_int_t _cairo_unbounded_rectangle;
+
+static inline void
+_cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect)
+{
+ *rect = _cairo_unbounded_rectangle;
+}
+
+cairo_private_no_warn cairo_bool_t
+_cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
+ const cairo_rectangle_int_t *src);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_rectangle_exact_intersect (cairo_rectangle_t *dst,
+ const cairo_rectangle_t *src);
+
+static inline cairo_bool_t
+_cairo_rectangle_intersects (const cairo_rectangle_int_t *dst,
+ const cairo_rectangle_int_t *src)
+{
+ return !(src->x >= dst->x + (int) dst->width ||
+ src->x + (int) src->width <= dst->x ||
+ src->y >= dst->y + (int) dst->height ||
+ src->y + (int) src->height <= dst->y);
+}
+
+static inline cairo_bool_t
+_cairo_rectangle_contains_rectangle (const cairo_rectangle_int_t *a,
+ const cairo_rectangle_int_t *b)
+{
+ return (a->x <= b->x &&
+ a->x + (int) a->width >= b->x + (int) b->width &&
+ a->y <= b->y &&
+ a->y + (int) a->height >= b->y + (int) b->height);
+}
+
+cairo_private void
+_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti,
+ const cairo_rectangle_t *rectf);
+
+/* Extends the dst rectangle to also contain src.
+ * If one of the rectangles is empty, the result is undefined
+ */
+cairo_private void
+_cairo_rectangle_union (cairo_rectangle_int_t *dst,
+ const cairo_rectangle_int_t *src);
+
+cairo_private cairo_bool_t
+_cairo_box_intersects_line_segment (const cairo_box_t *box,
+ cairo_line_t *line) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_spline_intersects (const cairo_point_t *a,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d,
+ const cairo_box_t *box) cairo_pure;
+
+typedef struct {
+ const cairo_user_data_key_t *key;
+ void *user_data;
+ cairo_destroy_func_t destroy;
+} cairo_user_data_slot_t;
+
+cairo_private void
+_cairo_user_data_array_init (cairo_user_data_array_t *array);
+
+cairo_private void
+_cairo_user_data_array_fini (cairo_user_data_array_t *array);
+
+cairo_private void *
+_cairo_user_data_array_get_data (cairo_user_data_array_t *array,
+ const cairo_user_data_key_t *key);
+
+cairo_private cairo_status_t
+_cairo_user_data_array_set_data (cairo_user_data_array_t *array,
+ const cairo_user_data_key_t *key,
+ void *user_data,
+ cairo_destroy_func_t destroy);
+
+cairo_private cairo_status_t
+_cairo_user_data_array_copy (cairo_user_data_array_t *dst,
+ const cairo_user_data_array_t *src);
+
+cairo_private void
+_cairo_user_data_array_foreach (cairo_user_data_array_t *array,
+ void (*func) (const void *key,
+ void *elt,
+ void *closure),
+ void *closure);
+
+#define _CAIRO_HASH_INIT_VALUE 5381
+
+cairo_private unsigned long
+_cairo_hash_string (const char *c);
+
+cairo_private unsigned long
+_cairo_hash_bytes (unsigned long hash,
+ const void *bytes,
+ unsigned int length);
+
+#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_default_context_reset_static_data (void);
+
+cairo_private void
+_cairo_toy_font_face_reset_static_data (void);
+
+cairo_private void
+_cairo_ft_font_reset_static_data (void);
+
+cairo_private void
+_cairo_win32_font_reset_static_data (void);
+
+#if CAIRO_HAS_COGL_SURFACE
+void
+_cairo_cogl_context_reset_static_data (void);
+#endif
+
+/* the font backend interface */
+
+struct _cairo_unscaled_font_backend {
+ 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;
+ char **utf8;
+ char **glyph_names;
+ int *to_latin_char;
+ unsigned long *latin_to_subset_glyph_index;
+ unsigned int num_glyphs;
+ cairo_bool_t is_composite;
+ cairo_bool_t is_scaled;
+ cairo_bool_t is_latin;
+} cairo_scaled_font_subset_t;
+
+struct _cairo_scaled_font_backend {
+ cairo_font_type_t type;
+
+ void
+ (*fini) (void *scaled_font);
+
+ cairo_warn cairo_int_status_t
+ (*scaled_glyph_init) (void *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_glyph_info_t info);
+
+ /* A backend only needs to implement this or ucs4_to_index(), not
+ * both. This allows the backend to do something more sophisticated
+ * then just converting characters one by one.
+ */
+ cairo_warn cairo_int_status_t
+ (*text_to_glyphs) (void *scaled_font,
+ double x,
+ double y,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs,
+ cairo_text_cluster_t **clusters,
+ int *num_clusters,
+ cairo_text_cluster_flags_t *cluster_flags);
+
+ unsigned long
+ (*ucs4_to_index) (void *scaled_font,
+ uint32_t ucs4);
+
+ /* Read data from a sfnt font table.
+ * @scaled_font: font
+ * @tag: 4 byte table name specifying the table to read.
+ * @offset: offset into the table
+ * @buffer: buffer to write data into. Caller must ensure there is sufficient space.
+ * If NULL, return the size of the table in @length.
+ * @length: If @buffer is NULL, the size of the table will be returned in @length.
+ * If @buffer is not null, @length specifies the number of bytes to read.
+ *
+ * If less than @length bytes are available to read this function
+ * returns CAIRO_INT_STATUS_UNSUPPORTED. Note that requesting more
+ * bytes than are available in the table may continue reading data
+ * from the following table and return success. If this is
+ * undesirable the caller should first query the table size. If an
+ * error occurs the output value of @length is undefined.
+ *
+ * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a sfnt style font or table not found.
+ */
+ cairo_warn cairo_int_status_t
+ (*load_truetype_table)(void *scaled_font,
+ unsigned long tag,
+ long offset,
+ unsigned char *buffer,
+ unsigned long *length);
+
+ /* ucs4 is set to -1 if the unicode character could not be found
+ * for the glyph */
+ cairo_warn cairo_int_status_t
+ (*index_to_ucs4)(void *scaled_font,
+ unsigned long index,
+ uint32_t *ucs4);
+
+ cairo_warn cairo_bool_t
+ (*is_synthetic)(void *scaled_font);
+
+ /* For type 1 fonts, return the glyph name for a given glyph index.
+ * A glyph index and list of glyph names in the Type 1 fonts is provided.
+ * The function returns the index of the glyph in the list of glyph names.
+ * @scaled_font: font
+ * @glyph_names: the names of each glyph in the Type 1 font in the
+ * order they appear in the CharStrings array
+ * @num_glyph_names: the number of names in the glyph_names array
+ * @glyph_index: the given glyph index
+ * @glyph_array_index: (index into glyph_names) the glyph name corresponding
+ * to the glyph_index
+ */
+
+ cairo_warn cairo_int_status_t
+ (*index_to_glyph_name)(void *scaled_font,
+ char **glyph_names,
+ int num_glyph_names,
+ unsigned long glyph_index,
+ unsigned long *glyph_array_index);
+
+ /* Read data from a PostScript font.
+ * @scaled_font: font
+ * @offset: offset into the table
+ * @buffer: buffer to write data into. Caller must ensure there is sufficient space.
+ * If NULL, return the size of the table in @length.
+ * @length: If @buffer is NULL, the size of the table will be returned in @length.
+ * If @buffer is not null, @length specifies the number of bytes to read.
+ *
+ * If less than @length bytes are available to read this function
+ * returns CAIRO_INT_STATUS_UNSUPPORTED. If an error occurs the
+ * output value of @length is undefined.
+ *
+ * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a Type 1 font.
+ */
+ cairo_warn cairo_int_status_t
+ (*load_type1_data) (void *scaled_font,
+ long offset,
+ unsigned char *buffer,
+ unsigned long *length);
+};
+
+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_QUARTZ_FONT
+
+extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend;
+
+#endif
+
+#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE
+#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD
+#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD
+
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_clear;
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black;
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white;
+
+struct _cairo_surface_attributes {
+ cairo_matrix_t matrix;
+ cairo_extend_t extend;
+ cairo_filter_t filter;
+ cairo_bool_t has_component_alpha;
+ int x_offset;
+ int y_offset;
+ void *extra;
+};
+
+#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL
+#define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL
+
+#define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial"
+#define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT "Helvetica"
+#define CAIRO_FT_FONT_FAMILY_DEFAULT ""
+#define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:"
+
+#if CAIRO_HAS_WIN32_FONT
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend
+
+#elif CAIRO_HAS_QUARTZ_FONT
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_QUARTZ_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_quartz_font_face_backend
+
+#elif CAIRO_HAS_FT_FONT
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_ft_font_face_backend
+
+#else
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_user_font_face_backend
+
+#endif
+
+#define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER
+#define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.1
+#define CAIRO_GSTATE_FILL_RULE_DEFAULT CAIRO_FILL_RULE_WINDING
+#define CAIRO_GSTATE_LINE_WIDTH_DEFAULT 2.0
+#define CAIRO_GSTATE_LINE_CAP_DEFAULT CAIRO_LINE_CAP_BUTT
+#define CAIRO_GSTATE_LINE_JOIN_DEFAULT CAIRO_LINE_JOIN_MITER
+#define CAIRO_GSTATE_MITER_LIMIT_DEFAULT 10.0
+#define CAIRO_GSTATE_DEFAULT_FONT_SIZE 10.0
+
+#define CAIRO_SURFACE_RESOLUTION_DEFAULT 72.0
+#define CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT 300.0
+
+typedef struct _cairo_stroke_face {
+ cairo_point_t ccw;
+ cairo_point_t point;
+ cairo_point_t cw;
+ cairo_slope_t dev_vector;
+ cairo_point_double_t dev_slope;
+ cairo_point_double_t usr_vector;
+ double length;
+} cairo_stroke_face_t;
+
+/* cairo.c */
+
+static inline double cairo_const
+_cairo_restrict_value (double value, double min, double max)
+{
+ if (value < min)
+ return min;
+ else if (value > max)
+ return max;
+ else
+ return value;
+}
+
+/* C99 round() rounds to the nearest integral value with halfway cases rounded
+ * away from 0. _cairo_round rounds halfway cases toward positive infinity.
+ * This matches the rounding behaviour of _cairo_lround. */
+static inline double cairo_const
+_cairo_round (double r)
+{
+ return floor (r + .5);
+}
+
+#if DISABLE_SOME_FLOATING_POINT
+cairo_private int
+_cairo_lround (double d) cairo_const;
+#else
+static inline int cairo_const
+_cairo_lround (double r)
+{
+ return _cairo_round (r);
+}
+#endif
+
+cairo_private uint16_t
+_cairo_half_from_float (float f) cairo_const;
+
+cairo_private cairo_bool_t
+_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const;
+
+cairo_private cairo_bool_t
+_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const;
+
+enum {
+ CAIRO_OPERATOR_BOUND_BY_MASK = 1 << 1,
+ CAIRO_OPERATOR_BOUND_BY_SOURCE = 1 << 2,
+};
+
+cairo_private uint32_t
+_cairo_operator_bounded_by_either (cairo_operator_t op) cairo_const;
+/* cairo-color.c */
+cairo_private const cairo_color_t *
+_cairo_stock_color (cairo_stock_t stock) cairo_pure;
+
+#define CAIRO_COLOR_WHITE _cairo_stock_color (CAIRO_STOCK_WHITE)
+#define CAIRO_COLOR_BLACK _cairo_stock_color (CAIRO_STOCK_BLACK)
+#define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT)
+
+cairo_private uint16_t
+_cairo_color_double_to_short (double d) cairo_const;
+
+cairo_private void
+_cairo_color_init_rgba (cairo_color_t *color,
+ double red, double green, double blue,
+ double alpha);
+
+cairo_private void
+_cairo_color_multiply_alpha (cairo_color_t *color,
+ double alpha);
+
+cairo_private void
+_cairo_color_get_rgba (cairo_color_t *color,
+ double *red,
+ double *green,
+ double *blue,
+ double *alpha);
+
+cairo_private void
+_cairo_color_get_rgba_premultiplied (cairo_color_t *color,
+ double *red,
+ double *green,
+ double *blue,
+ double *alpha);
+
+cairo_private cairo_bool_t
+_cairo_color_equal (const cairo_color_t *color_a,
+ const cairo_color_t *color_b) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_color_stop_equal (const cairo_color_stop_t *color_a,
+ const cairo_color_stop_t *color_b) cairo_pure;
+
+cairo_private cairo_content_t
+_cairo_color_get_content (const cairo_color_t *color) cairo_pure;
+
+/* cairo-font-face.c */
+
+extern const cairo_private cairo_font_face_t _cairo_font_face_nil;
+
+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 void
+_cairo_path_fixed_fini (cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_path_fixed_destroy (cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y);
+
+cairo_private void
+_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
+ cairo_fixed_t dx,
+ cairo_fixed_t dy);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
+ cairo_fixed_t dx,
+ cairo_fixed_t dy);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
+ cairo_fixed_t x0, cairo_fixed_t y0,
+ cairo_fixed_t x1, cairo_fixed_t y1,
+ cairo_fixed_t x2, cairo_fixed_t y2);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
+ cairo_fixed_t dx0, cairo_fixed_t dy0,
+ cairo_fixed_t dx1, cairo_fixed_t dy1,
+ cairo_fixed_t dx2, cairo_fixed_t dy2);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_close_path (cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
+ cairo_fixed_t *x,
+ cairo_fixed_t *y);
+
+typedef cairo_status_t
+(cairo_path_fixed_move_to_func_t) (void *closure,
+ const cairo_point_t *point);
+
+typedef cairo_status_t
+(cairo_path_fixed_line_to_func_t) (void *closure,
+ const cairo_point_t *point);
+
+typedef cairo_status_t
+(cairo_path_fixed_curve_to_func_t) (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2);
+
+typedef cairo_status_t
+(cairo_path_fixed_close_path_func_t) (void *closure);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_curve_to_func_t *curve_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure,
+ double tolerance);
+
+
+cairo_private cairo_bool_t
+_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
+ cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_extents (const cairo_path_fixed_t *path,
+ cairo_box_t *box);
+
+cairo_private void
+_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_path_fixed_approximate_fill_exact_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_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_fill_exact_extents (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_rectangle_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 void
+_cairo_path_fixed_approximate_stroke_exact_extents (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ cairo_rectangle_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 cairo_status_t
+_cairo_path_fixed_stroke_exact_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_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_single_arc (const cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_single_line (const cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
+ cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path,
+ cairo_box_t *box);
+
+/* cairo-path-in-fill.c */
+cairo_private cairo_bool_t
+_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ double x,
+ double y);
+
+/* cairo-path-fill.c */
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
+ double tolerance,
+ cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
+ cairo_antialias_t antialias,
+ cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes);
+
+cairo_private cairo_region_t *
+_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_traps_t *traps);
+
+/* cairo-path-stroke.c */
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_tristrip_t *strip);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_status_t (*add_triangle) (void *closure,
+ const cairo_point_t triangle[3]),
+ cairo_status_t (*add_triangle_fan) (void *closure,
+ const cairo_point_t *midpt,
+ const cairo_point_t *points,
+ int npoints),
+ cairo_status_t (*add_quad) (void *closure,
+ const cairo_point_t quad[4]),
+ void *closure);
+
+/* cairo-scaled-font.c */
+
+cairo_private void
+_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
+ cairo_status_t status);
+
+cairo_private cairo_scaled_font_t *
+_cairo_scaled_font_create_in_error (cairo_status_t status);
+
+cairo_private void
+_cairo_scaled_font_reset_static_data (void);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font,
+ cairo_font_face_t *font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ const cairo_scaled_font_backend_t *backend);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font,
+ cairo_font_extents_t *fs_metrics);
+
+/* This should only be called on an error path by a scaled_font constructor */
+cairo_private void
+_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font,
+ cairo_font_extents_t *extents);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_rectangle_int_t *extents,
+ cairo_bool_t *overlap);
+
+cairo_private cairo_bool_t
+_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_surface_t *surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_text_extents_t *fs_metrics);
+
+cairo_private void
+_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_image_surface_t *surface);
+
+cairo_private void
+_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font,
+ cairo_surface_t *recording_surface);
+
+cairo_private cairo_int_status_t
+_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
+ unsigned long index,
+ cairo_scaled_glyph_info_t info,
+ cairo_scaled_glyph_t **scaled_glyph_ret);
+
+cairo_private double
+_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_map_destroy (void);
+
+/* cairo-stroke-style.c */
+
+cairo_private void
+_cairo_stroke_style_init (cairo_stroke_style_t *style);
+
+cairo_private cairo_status_t
+_cairo_stroke_style_init_copy (cairo_stroke_style_t *style,
+ const cairo_stroke_style_t *other);
+
+cairo_private void
+_cairo_stroke_style_fini (cairo_stroke_style_t *style);
+
+cairo_private void
+_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
+ const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm,
+ double *dx, double *dy);
+cairo_private void
+_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style,
+ const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm,
+ double *dx, double *dy);
+
+cairo_private void
+_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style,
+ const cairo_path_fixed_t *path,
+ const cairo_matrix_t *ctm,
+ double *dx, double *dy);
+
+cairo_private double
+_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style);
+
+cairo_private double
+_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style);
+
+cairo_private cairo_bool_t
+_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ double tolerance);
+
+cairo_private void
+_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ double tolerance,
+ double *dash_offset,
+ double *dashes,
+ unsigned int *num_dashes);
+
+
+/* cairo-surface.c */
+
+cairo_private cairo_status_t
+_cairo_surface_copy_mime_data (cairo_surface_t *dst,
+ cairo_surface_t *src);
+
+cairo_private_no_warn cairo_int_status_t
+_cairo_surface_set_error (cairo_surface_t *surface,
+ cairo_int_status_t status);
+
+cairo_private void
+_cairo_surface_set_resolution (cairo_surface_t *surface,
+ double x_res,
+ double y_res);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_similar_scratch (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_for_rectangle_int (cairo_surface_t *target,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_similar_solid (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height,
+ const cairo_color_t *color);
+
+cairo_private void
+_cairo_surface_init (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend,
+ cairo_device_t *device,
+ cairo_content_t content);
+
+cairo_private void
+_cairo_surface_set_font_options (cairo_surface_t *surface,
+ cairo_font_options_t *options);
+
+cairo_private cairo_status_t
+_cairo_surface_paint (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_image_surface_t *
+_cairo_surface_map_to_image (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_surface_unmap_image (cairo_surface_t *surface,
+ cairo_image_surface_t *image);
+
+cairo_private cairo_status_t
+_cairo_surface_mask (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fill_stroke (cairo_surface_t *surface,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_stroke (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fill (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_show_text_glyphs (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_acquire_source_image (cairo_surface_t *surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+cairo_private void
+_cairo_surface_release_source_image (cairo_surface_t *surface,
+ cairo_image_surface_t *image,
+ void *image_extra);
+
+cairo_private cairo_surface_t *
+_cairo_surface_snapshot (cairo_surface_t *surface);
+
+cairo_private void
+_cairo_surface_attach_snapshot (cairo_surface_t *surface,
+ cairo_surface_t *snapshot,
+ cairo_surface_func_t detach_func);
+
+cairo_private cairo_surface_t *
+_cairo_surface_has_snapshot (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend);
+
+cairo_private void
+_cairo_surface_detach_snapshot (cairo_surface_t *snapshot);
+
+cairo_private cairo_status_t
+_cairo_surface_begin_modification (cairo_surface_t *surface);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_surface_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_int_t *extents);
+
+cairo_private 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_RGB30)
+
+/* pixman-required stride alignment in bytes. */
+#define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t))
+#define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \
+ ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT)
+
+#define CAIRO_CONTENT_VALID(content) ((content) && \
+ (((content) & ~(CAIRO_CONTENT_COLOR | \
+ CAIRO_CONTENT_ALPHA | \
+ CAIRO_CONTENT_COLOR_ALPHA))\
+ == 0))
+
+cairo_private int
+_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const;
+
+cairo_private cairo_format_t
+_cairo_format_from_content (cairo_content_t content) cairo_const;
+
+cairo_private cairo_format_t
+_cairo_format_from_pixman_format (pixman_format_code_t pixman_format);
+
+cairo_private cairo_content_t
+_cairo_content_from_format (cairo_format_t format) cairo_const;
+
+cairo_private cairo_content_t
+_cairo_content_from_pixman_format (pixman_format_code_t pixman_format);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
+ pixman_format_code_t pixman_format);
+
+cairo_private pixman_format_code_t
+_cairo_format_to_pixman_format_code (cairo_format_t format);
+
+cairo_private cairo_bool_t
+_pixman_format_from_masks (cairo_format_masks_t *masks,
+ pixman_format_code_t *format_ret);
+
+cairo_private cairo_bool_t
+_pixman_format_to_masks (pixman_format_code_t pixman_format,
+ cairo_format_masks_t *masks);
+
+cairo_private void
+_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph);
+
+cairo_private void
+_cairo_image_reset_static_data (void);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_create_with_pixman_format (unsigned char *data,
+ pixman_format_code_t pixman_format,
+ int width,
+ int height,
+ int stride);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_create_with_content (cairo_content_t content,
+ int width,
+ int height);
+
+cairo_private void
+_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface);
+
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_coerce (cairo_image_surface_t *surface);
+
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface,
+ cairo_format_t format);
+
+cairo_private cairo_image_transparency_t
+_cairo_image_analyze_transparency (cairo_image_surface_t *image);
+
+cairo_private cairo_image_color_t
+_cairo_image_analyze_color (cairo_image_surface_t *image);
+
+/* cairo-pen.c */
+cairo_private int
+_cairo_pen_vertices_needed (double tolerance,
+ double radius,
+ const cairo_matrix_t *matrix);
+
+cairo_private cairo_status_t
+_cairo_pen_init (cairo_pen_t *pen,
+ double radius,
+ double tolerance,
+ const cairo_matrix_t *ctm);
+
+cairo_private void
+_cairo_pen_init_empty (cairo_pen_t *pen);
+
+cairo_private cairo_status_t
+_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other);
+
+cairo_private void
+_cairo_pen_fini (cairo_pen_t *pen);
+
+cairo_private cairo_status_t
+_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points);
+
+cairo_private int
+_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen,
+ const cairo_slope_t *slope);
+
+cairo_private int
+_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
+ const cairo_slope_t *slope);
+
+cairo_private void
+_cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen,
+ const cairo_slope_t *in,
+ const cairo_slope_t *out,
+ int *start, int *stop);
+
+cairo_private void
+_cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen,
+ const cairo_slope_t *in,
+ const cairo_slope_t *out,
+ int *start, int *stop);
+
+/* cairo-polygon.c */
+cairo_private void
+_cairo_polygon_init (cairo_polygon_t *polygon,
+ const cairo_box_t *boxes,
+ int num_boxes);
+
+cairo_private void
+_cairo_polygon_init_with_clip (cairo_polygon_t *polygon,
+ const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_polygon_init_boxes (cairo_polygon_t *polygon,
+ const cairo_boxes_t *boxes);
+
+cairo_private cairo_status_t
+_cairo_polygon_init_box_array (cairo_polygon_t *polygon,
+ cairo_box_t *boxes,
+ int num_boxes);
+
+cairo_private void
+_cairo_polygon_limit (cairo_polygon_t *polygon,
+ const cairo_box_t *limits,
+ int num_limits);
+
+cairo_private void
+_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_polygon_fini (cairo_polygon_t *polygon);
+
+cairo_private 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_add_contour (cairo_polygon_t *polygon,
+ const cairo_contour_t *contour);
+
+cairo_private void
+_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy);
+
+cairo_private cairo_status_t
+_cairo_polygon_reduce (cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a,
+ cairo_polygon_t *b, int winding_b);
+
+cairo_private cairo_status_t
+_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon,
+ cairo_fill_rule_t *winding,
+ cairo_box_t *boxes,
+ int num_boxes);
+
+static inline cairo_bool_t
+_cairo_polygon_is_empty (const cairo_polygon_t *polygon)
+{
+ return
+ polygon->num_edges == 0 ||
+ polygon->extents.p2.x <= polygon->extents.p1.x;
+}
+
+#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status
+
+/* cairo-spline.c */
+cairo_private cairo_bool_t
+_cairo_spline_init (cairo_spline_t *spline,
+ cairo_spline_add_point_func_t add_point_func,
+ void *closure,
+ const cairo_point_t *a, const cairo_point_t *b,
+ const cairo_point_t *c, const cairo_point_t *d);
+
+cairo_private cairo_status_t
+_cairo_spline_decompose (cairo_spline_t *spline, double tolerance);
+
+cairo_private cairo_status_t
+_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
+ void *closure,
+ const cairo_point_t *p0, const cairo_point_t *p1,
+ const cairo_point_t *p2, const cairo_point_t *p3);
+
+/* cairo-matrix.c */
+cairo_private void
+_cairo_matrix_get_affine (const cairo_matrix_t *matrix,
+ double *xx, double *yx,
+ double *xy, double *yy,
+ double *x0, double *y0);
+
+cairo_private void
+_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
+ double *x1, double *y1,
+ double *x2, double *y2,
+ cairo_bool_t *is_tight);
+
+cairo_private void
+_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix,
+ cairo_box_t *bbox,
+ cairo_bool_t *is_tight);
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private double
+_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_status_t
+_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
+ double *sx, double *sy, int x_major);
+
+static inline cairo_bool_t
+_cairo_matrix_is_identity (const cairo_matrix_t *matrix)
+{
+ return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
+ matrix->xy == 0.0 && matrix->yy == 1.0 &&
+ matrix->x0 == 0.0 && matrix->y0 == 0.0);
+}
+
+static inline cairo_bool_t
+_cairo_matrix_is_translation (const cairo_matrix_t *matrix)
+{
+ return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
+ matrix->xy == 0.0 && matrix->yy == 1.0);
+}
+
+static inline cairo_bool_t
+_cairo_matrix_is_scale (const cairo_matrix_t *matrix)
+{
+ return matrix->yx == 0.0 && matrix->xy == 0.0;
+}
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix,
+ int *itx, int *ity);
+
+cairo_private cairo_bool_t
+_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private double
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ double radius) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix,
+ cairo_filter_t filter,
+ int *out_x_offset,
+ int *out_y_offset);
+
+cairo_private cairo_status_t
+_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix,
+ cairo_filter_t filter,
+ double xc,
+ double yc,
+ pixman_transform_t *out_transform,
+ int *out_x_offset,
+ int *out_y_offset);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps,
+ const cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
+ const cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in,
+ cairo_fill_rule_t fill_rule,
+ cairo_boxes_t *out);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
+ cairo_trapezoid_t *src_traps,
+ int num_traps,
+ double tx, double ty,
+ double sx, double sy);
+
+#if CAIRO_HAS_DRM_SURFACE
+
+cairo_private void
+_cairo_drm_device_reset_static_data (void);
+
+#endif
+
+cairo_private void
+_cairo_clip_reset_static_data (void);
+
+cairo_private void
+_cairo_pattern_reset_static_data (void);
+
+/* cairo-unicode.c */
+
+cairo_private int
+_cairo_utf8_get_char_validated (const char *p,
+ uint32_t *unicode);
+
+cairo_private cairo_status_t
+_cairo_utf8_to_ucs4 (const char *str,
+ int len,
+ uint32_t **result,
+ int *items_written);
+
+cairo_private int
+_cairo_ucs4_to_utf8 (uint32_t unicode,
+ char *utf8);
+
+#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS
+# define CAIRO_HAS_UTF8_TO_UTF16 1
+#endif
+#if CAIRO_HAS_UTF8_TO_UTF16
+cairo_private cairo_status_t
+_cairo_utf8_to_utf16 (const char *str,
+ int len,
+ uint16_t **result,
+ int *items_written);
+#endif
+
+cairo_private void
+_cairo_matrix_multiply (cairo_matrix_t *r,
+ const cairo_matrix_t *a,
+ const cairo_matrix_t *b);
+
+/* cairo-observer.c */
+
+cairo_private void
+_cairo_observers_notify (cairo_list_t *observers, void *arg);
+
+/* 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_font_color);
+slim_hidden_proto (cairo_font_options_set_hint_metrics);
+slim_hidden_proto (cairo_font_options_set_hint_style);
+slim_hidden_proto (cairo_font_options_set_subpixel_order);
+slim_hidden_proto (cairo_font_options_status);
+slim_hidden_proto (cairo_format_stride_for_width);
+slim_hidden_proto (cairo_get_current_point);
+slim_hidden_proto (cairo_get_line_width);
+slim_hidden_proto (cairo_get_matrix);
+slim_hidden_proto (cairo_get_scaled_font);
+slim_hidden_proto (cairo_get_target);
+slim_hidden_proto (cairo_get_tolerance);
+slim_hidden_proto (cairo_glyph_allocate);
+slim_hidden_proto (cairo_glyph_free);
+slim_hidden_proto (cairo_image_surface_create);
+slim_hidden_proto (cairo_image_surface_create_for_data);
+slim_hidden_proto (cairo_image_surface_get_data);
+slim_hidden_proto (cairo_image_surface_get_format);
+slim_hidden_proto (cairo_image_surface_get_height);
+slim_hidden_proto (cairo_image_surface_get_stride);
+slim_hidden_proto (cairo_image_surface_get_width);
+slim_hidden_proto (cairo_line_to);
+slim_hidden_proto (cairo_mask);
+slim_hidden_proto (cairo_matrix_init);
+slim_hidden_proto (cairo_matrix_init_identity);
+slim_hidden_proto (cairo_matrix_init_rotate);
+slim_hidden_proto (cairo_matrix_init_scale);
+slim_hidden_proto (cairo_matrix_init_translate);
+slim_hidden_proto (cairo_matrix_invert);
+slim_hidden_proto (cairo_matrix_multiply);
+slim_hidden_proto (cairo_matrix_scale);
+slim_hidden_proto (cairo_matrix_transform_distance);
+slim_hidden_proto (cairo_matrix_transform_point);
+slim_hidden_proto (cairo_matrix_translate);
+slim_hidden_proto (cairo_move_to);
+slim_hidden_proto (cairo_new_path);
+slim_hidden_proto (cairo_paint);
+slim_hidden_proto (cairo_pattern_add_color_stop_rgba);
+slim_hidden_proto (cairo_pattern_create_for_surface);
+slim_hidden_proto (cairo_pattern_create_rgb);
+slim_hidden_proto (cairo_pattern_create_rgba);
+slim_hidden_proto (cairo_pattern_destroy);
+slim_hidden_proto (cairo_pattern_get_extend);
+slim_hidden_proto (cairo_mesh_pattern_curve_to);
+slim_hidden_proto (cairo_mesh_pattern_get_control_point);
+slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba);
+slim_hidden_proto (cairo_mesh_pattern_get_patch_count);
+slim_hidden_proto (cairo_mesh_pattern_get_path);
+slim_hidden_proto (cairo_mesh_pattern_line_to);
+slim_hidden_proto (cairo_mesh_pattern_move_to);
+slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba);
+slim_hidden_proto_no_warn (cairo_pattern_reference);
+slim_hidden_proto (cairo_pattern_set_matrix);
+slim_hidden_proto (cairo_pop_group);
+slim_hidden_proto (cairo_push_group_with_content);
+slim_hidden_proto_no_warn (cairo_path_destroy);
+slim_hidden_proto (cairo_recording_surface_create);
+slim_hidden_proto (cairo_rel_line_to);
+slim_hidden_proto (cairo_restore);
+slim_hidden_proto (cairo_save);
+slim_hidden_proto (cairo_scale);
+slim_hidden_proto (cairo_scaled_font_create);
+slim_hidden_proto (cairo_scaled_font_destroy);
+slim_hidden_proto (cairo_scaled_font_extents);
+slim_hidden_proto (cairo_scaled_font_get_ctm);
+slim_hidden_proto (cairo_scaled_font_get_font_face);
+slim_hidden_proto (cairo_scaled_font_get_font_matrix);
+slim_hidden_proto (cairo_scaled_font_get_font_options);
+slim_hidden_proto (cairo_scaled_font_glyph_extents);
+slim_hidden_proto_no_warn (cairo_scaled_font_reference);
+slim_hidden_proto (cairo_scaled_font_status);
+slim_hidden_proto (cairo_scaled_font_get_user_data);
+slim_hidden_proto (cairo_scaled_font_set_user_data);
+slim_hidden_proto (cairo_scaled_font_text_to_glyphs);
+slim_hidden_proto (cairo_set_font_matrix);
+slim_hidden_proto (cairo_set_font_options);
+slim_hidden_proto (cairo_set_font_size);
+slim_hidden_proto (cairo_set_line_cap);
+slim_hidden_proto (cairo_set_line_join);
+slim_hidden_proto (cairo_set_line_width);
+slim_hidden_proto (cairo_set_matrix);
+slim_hidden_proto (cairo_set_operator);
+slim_hidden_proto (cairo_set_source);
+slim_hidden_proto (cairo_set_source_rgb);
+slim_hidden_proto (cairo_set_source_surface);
+slim_hidden_proto (cairo_set_tolerance);
+slim_hidden_proto (cairo_status);
+slim_hidden_proto (cairo_stroke);
+slim_hidden_proto (cairo_stroke_preserve);
+slim_hidden_proto (cairo_surface_copy_page);
+slim_hidden_proto (cairo_surface_create_similar_image);
+slim_hidden_proto (cairo_surface_destroy);
+slim_hidden_proto (cairo_surface_finish);
+slim_hidden_proto (cairo_surface_flush);
+slim_hidden_proto (cairo_surface_get_device_offset);
+slim_hidden_proto (cairo_surface_get_font_options);
+slim_hidden_proto (cairo_surface_get_mime_data);
+slim_hidden_proto (cairo_surface_has_show_text_glyphs);
+slim_hidden_proto (cairo_surface_mark_dirty);
+slim_hidden_proto (cairo_surface_mark_dirty_rectangle);
+slim_hidden_proto_no_warn (cairo_surface_reference);
+slim_hidden_proto (cairo_surface_set_device_offset);
+slim_hidden_proto (cairo_surface_set_fallback_resolution);
+slim_hidden_proto (cairo_surface_set_mime_data);
+slim_hidden_proto (cairo_surface_show_page);
+slim_hidden_proto (cairo_surface_status);
+slim_hidden_proto (cairo_surface_supports_mime_type);
+slim_hidden_proto (cairo_text_cluster_allocate);
+slim_hidden_proto (cairo_text_cluster_free);
+slim_hidden_proto (cairo_toy_font_face_create);
+slim_hidden_proto (cairo_toy_font_face_get_slant);
+slim_hidden_proto (cairo_toy_font_face_get_weight);
+slim_hidden_proto (cairo_translate);
+slim_hidden_proto (cairo_transform);
+slim_hidden_proto (cairo_user_font_face_create);
+slim_hidden_proto (cairo_user_font_face_set_init_func);
+slim_hidden_proto (cairo_user_font_face_set_render_glyph_func);
+slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func);
+slim_hidden_proto (cairo_device_to_user);
+slim_hidden_proto (cairo_user_to_device);
+slim_hidden_proto (cairo_user_to_device_distance);
+slim_hidden_proto (cairo_version_string);
+slim_hidden_proto (cairo_region_create);
+slim_hidden_proto (cairo_region_create_rectangle);
+slim_hidden_proto (cairo_region_create_rectangles);
+slim_hidden_proto (cairo_region_copy);
+slim_hidden_proto (cairo_region_reference);
+slim_hidden_proto (cairo_region_destroy);
+slim_hidden_proto (cairo_region_equal);
+slim_hidden_proto (cairo_region_status);
+slim_hidden_proto (cairo_region_get_extents);
+slim_hidden_proto (cairo_region_num_rectangles);
+slim_hidden_proto (cairo_region_get_rectangle);
+slim_hidden_proto (cairo_region_is_empty);
+slim_hidden_proto (cairo_region_contains_rectangle);
+slim_hidden_proto (cairo_region_contains_point);
+slim_hidden_proto (cairo_region_translate);
+slim_hidden_proto (cairo_region_subtract);
+slim_hidden_proto (cairo_region_subtract_rectangle);
+slim_hidden_proto (cairo_region_intersect);
+slim_hidden_proto (cairo_region_intersect_rectangle);
+slim_hidden_proto (cairo_region_union);
+slim_hidden_proto (cairo_region_union_rectangle);
+slim_hidden_proto (cairo_region_xor);
+slim_hidden_proto (cairo_region_xor_rectangle);
+
+slim_hidden_proto (cairo_set_shadow);
+slim_hidden_proto (cairo_set_shadow_offset);
+slim_hidden_proto (cairo_set_shadow_rgba);
+slim_hidden_proto (cairo_set_shadow_rgb);
+slim_hidden_proto (cairo_set_shadow_blur);
+slim_hidden_proto (cairo_set_draw_shadow_only);
+slim_hidden_proto (cairo_shadow_enable_cache);
+slim_hidden_proto (cairo_set_path_is_inset_shadow_with_spread);
+#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_polygon (FILE *stream, cairo_polygon_t *polygon);
+
+cairo_private void
+_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps);
+
+cairo_private void
+_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip);
+
+#if 0
+#define TRACE(x) fprintf (stderr, "%s: ", __FILE__), fprintf x
+#define TRACE_(x) x
+#else
+#define TRACE(x)
+#define TRACE_(x)
+#endif
+
+#endif
diff --git a/src/check-def.sh b/src/check-def.sh
new file mode 100755
index 000000000..9008c5871
--- /dev/null
+++ b/src/check-def.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if which nm 2>/dev/null >/dev/null; then
+ :
+else
+ echo "'nm' not found; skipping test"
+ exit 0
+fi
+
+test -z "$srcdir" && srcdir=.
+test -z "$MAKE" && MAKE=make
+stat=0
+
+$MAKE check-has-hidden-symbols.i > /dev/null || exit 1
+if tail -1 check-has-hidden-symbols.i | grep CAIRO_HAS_HIDDEN_SYMBOLS >/dev/null; then
+ echo "Compiler doesn't support symbol visibility; skipping test"
+ exit 0
+fi
+
+if [ "`uname -s`" = "Linux" ]; then
+ get_cairo_syms='( objdump -t "$so" | grep "^[^ ]* [^l.*]*[.]"; objdump -t "$so" | grep "[.]hidden.*\\<cairo"; ) | sed "s/.* //"'
+else
+ get_cairo_syms='nm "$so" | grep " [BCDGINRSTVW] " | cut -d" " -f3'
+fi
+
+defs="cairo.def"
+$MAKE $defs > /dev/null
+for def in $defs; do
+ lib=`echo "$def" | sed 's/[.]def$//'`
+ lib=`echo "$lib" | sed 's@.*/@@'`
+ so=.libs/lib${lib}.so
+
+ test -f "$so" || continue
+
+ echo Checking that $so has the same symbol list as $def
+
+ {
+ echo EXPORTS
+ eval $get_cairo_syms | c++filt --no-params | grep -v '^_cairo_test_\|^_fini\|^_init\|^_save[fg]pr\|^_rest[fg]pr\|^_Z\|^__gnu' | sort -u
+ # cheat: copy the last line from the def file!
+ tail -n1 "$def"
+ } | diff "$def" - >&2 || stat=1
+done
+
+exit $stat
diff --git a/src/check-doc-syntax.awk b/src/check-doc-syntax.awk
new file mode 100755
index 000000000..5fdabdac9
--- /dev/null
+++ b/src/check-doc-syntax.awk
@@ -0,0 +1,108 @@
+#!/usr/bin/awk -f
+
+BEGIN {
+ name_found = 1
+ SECTION_DOC = 0
+ PUBLIC_DOC = 1
+ PRIVATE_DOC = 2
+}
+
+function log_msg(severity, msg)
+{
+ printf "%s (%d): %s: %s %s\n", FILENAME, FNR, severity, doc_name, msg
+}
+
+function log_error(msg)
+{
+ log_msg("ERROR", msg)
+}
+
+function log_warning(msg)
+{
+ log_msg("WARNING", msg)
+}
+
+/^\/\*\*$/ {
+ in_doc = 1
+ doc_line = 0
+}
+
+/^(\/\*\*$| \*[ \t]| \*$| \*\*\/$)/ {
+ valid_doc = 1
+}
+
+in_doc {
+ if (!valid_doc)
+ log_error("bad line: '" $0 "'")
+ valid_doc = 0
+
+ doc_line++
+ if (doc_line == 2) {
+ # new doc name. Did we find the previous one?
+ # (macros are not expected to be found in the same place as
+ # their documentation)
+ if (!name_found && doc_name !~ /CAIRO_/)
+ log_warning("not found")
+ doc_name = $2
+ if (doc_name ~ /^SECTION:.*$/) {
+ doc_type = SECTION_DOC
+ name_found = 1
+ } else if (tolower(doc_name) ~ /^cairo_[a-z0-9_]*:$/) {
+ doc_type = PUBLIC_DOC
+ name_found = 0
+ real_name = substr(doc_name, 1, length(doc_name) - 1)
+ } else if (tolower(doc_name) ~ /^_[a-z0-9_]*:$/) {
+ doc_type = PRIVATE_DOC
+ name_found = 0
+ real_name = substr(doc_name, 1, length(doc_name) - 1)
+ } else {
+ log_error("invalid doc id (should be 'cairo_...:')")
+ name_found = 1
+ }
+ }
+}
+
+!in_doc {
+ regex = "(^|[ \\t\\*])" real_name "([ ;()]|$)"
+ if ($0 ~ regex)
+ name_found = 1
+}
+
+/^ \* Since: ([0-9]*.[0-9]*|TBD)$/ {
+ if (doc_has_since != 0) {
+ log_error("Duplicate 'Since' field")
+ }
+ doc_has_since = doc_line
+}
+
+/^ \*\*\// {
+ if (doc_type == PUBLIC_DOC) {
+ if (!doc_has_since) {
+ # private types can start with cairo_
+ if (doc_name ~ /^cairo_.*_t:$/)
+ log_warning("missing 'Since' field (is it a private type?)")
+ else
+ log_error("missing 'Since' field")
+ } else if (doc_has_since != doc_line - 1)
+ log_warning("misplaced 'Since' field (should be right before the end of the comment)")
+ } else {
+ if (doc_has_since)
+ log_warning("'Since' field in non-public element")
+ }
+
+ in_doc = 0
+ doc_has_since = 0
+ doc_type = 0
+}
+
+/\*\// {
+ if (in_doc) {
+ in_doc = 0
+ log_error("documentation comment not closed with **/")
+ }
+}
+
+END {
+ if (!name_found)
+ log_warning("not found")
+}
diff --git a/src/check-doc-syntax.sh b/src/check-doc-syntax.sh
new file mode 100755
index 000000000..c74fb875d
--- /dev/null
+++ b/src/check-doc-syntax.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if grep --version 2>/dev/null | grep GNU >/dev/null; then
+ :
+else
+ echo "GNU grep not found; skipping test"
+ exit 0
+fi
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+echo Checking documentation for incorrect syntax
+
+cd "$srcdir"
+
+if test "x$SGML_DOCS" = x; then
+ FILES=$all_cairo_files
+ if test "x$FILES" = x; then
+ FILES=`find . -name 'cairo*.h' -or -name 'cairo*.c' -or -name 'cairo*.cpp'`
+ fi
+fi
+
+enum_regexp="\([^%@']\|^\)\<\(FALSE\|TRUE\|NULL\|CAIRO_[0-9A-Z_]*\)\($\|[^(A-Za-z0-9_]\)"
+if test "x$SGML_DOCS" = x; then
+ enum_regexp='^[^:]*:[/ ][*]\(\|[ \t].*\)'$enum_regexp\($\|[^:]\)
+fi
+if echo $FILES | xargs grep . /dev/null | sed -e '/<programlisting>/,/<\/programlisting>/d' | grep "$enum_regexp" | grep -v '#####'; then
+ stat=1
+ echo Error: some macros in the docs are not prefixed by percent sign.
+ echo Fix this by searching for the following regexp in the above files:
+ echo " '$enum_regexp'"
+fi >&2
+
+type_regexp="\( .*[^#']\| \|^\)\<cairo[0-9a-z_]*_t\>\($\|[^:]$\|[^:].\)"
+if test "x$SGML_DOCS" = x; then
+ type_regexp='^[^:]*:[/ ][*]'$type_regexp
+else
+ type_regexp='\(.'$type_regexp'\)\|\('$type_regexp'.\)'
+fi
+
+if echo $FILES | xargs grep . /dev/null | sed -e '/<programlisting>/,/<\/programlisting>/d' | grep -v "@Title" | grep "$type_regexp" | grep -v '#####'; then
+ stat=1
+ echo Error: some type names in the docs are not prefixed by hash sign,
+ echo neither are the only token in the doc line followed by colon.
+ echo Fix this by searching for the following regexp in the above files:
+ echo " '$type_regexp'"
+fi >&2
+
+func_regexp="\([^#']\|^\)\<\(cairo_[][<>/0-9a-z_]*\>[^][<>(]\)"
+if test "x$SGML_DOCS" = x; then
+ func_regexp='^[^:]*:[/ ][*]\(\|[ \t].*\)'$func_regexp
+fi
+
+# We need to filter out gtk-doc markup errors for program listings.
+if echo $FILES | xargs grep . /dev/null | sed -e '/<programlisting>/,/<\/programlisting>/d' | grep "$func_regexp" | grep -v '^[^:]*: [*] [a-z_0-9]*:$' | grep -v '#####'; then
+ stat=1
+ echo Error: some function names in the docs are not followed by parentheses.
+ echo Fix this by searching for the following regexp in the above files:
+ echo " '$func_regexp'"
+fi >&2
+
+note_regexp='\<NOTE\>'
+if echo $FILES | xargs grep "$note_regexp" /dev/null; then
+ stat=1
+ echo Error: some source files contain the string 'NOTE'.
+ echo Be civil and replace it by 'Note' please.
+fi >&2
+
+# Only run the syntax checker on the source files (not doc/)
+if test -e ./check-doc-syntax.awk; then
+ if echo $FILES | xargs ./check-doc-syntax.awk ; then
+ :
+ else
+ stat=1
+ fi >&2
+fi
+
+exit $stat
diff --git a/src/check-has-hidden-symbols.c b/src/check-has-hidden-symbols.c
new file mode 100755
index 000000000..120412776
--- /dev/null
+++ b/src/check-has-hidden-symbols.c
@@ -0,0 +1,3 @@
+#include "cairoint.h"
+
+CAIRO_HAS_HIDDEN_SYMBOLS
diff --git a/src/check-headers.sh b/src/check-headers.sh
new file mode 100755
index 000000000..61232954b
--- /dev/null
+++ b/src/check-headers.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+echo Checking public headers for missing cairo_public decorators
+
+cd "$srcdir"
+FILES=$all_cairo_headers
+if test "x$FILES" = x; then
+ FILES=`find . -name 'cairo*.h' ! -name '*-private.h' ! -name 'cairoint.h'`
+fi
+
+grep -B 1 '^cairo_.*[ ]\+(' /dev/null $FILES |
+awk '
+/^--$/ { context=""; public=0; next; }
+/:cairo_.*[ ]+\(/ { if (!public) {print context; print; print "--";} next; }
+/-cairo_public.*[ ]/ {public=1;}
+{ context=$0; }
+' |
+sed 's/[.]h-/.h:/' |
+grep . >&2 && stat=1
+
+exit $stat
diff --git a/src/check-link.c b/src/check-link.c
new file mode 100755
index 000000000..66ca1b241
--- /dev/null
+++ b/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/src/check-plt.sh b/src/check-plt.sh
new file mode 100755
index 000000000..5a9dae126
--- /dev/null
+++ b/src/check-plt.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if which readelf 2>/dev/null >/dev/null; then
+ :
+else
+ echo "'readelf' not found; skipping test"
+ exit 0
+fi
+
+test -z "$srcdir" && srcdir=.
+test -z "$MAKE" && MAKE=make
+stat=0
+
+$MAKE check-has-hidden-symbols.i > /dev/null || exit 1
+if tail -1 check-has-hidden-symbols.i | grep CAIRO_HAS_HIDDEN_SYMBOLS >/dev/null; then
+ echo "Compiler doesn't support symbol visibility; skipping test"
+ exit 0
+fi
+
+for so in .libs/lib*.so; do
+ echo Checking "$so" for local PLT entries
+ readelf -W -r "$so" | grep 'JU\?MP_SLO' | grep 'cairo' >&2 && stat=1
+done
+
+exit $stat
diff --git a/src/check-preprocessor-syntax.sh b/src/check-preprocessor-syntax.sh
new file mode 100755
index 000000000..c4154151d
--- /dev/null
+++ b/src/check-preprocessor-syntax.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+cd "$srcdir"
+stat=0
+
+
+HEADERS=$all_cairo_headers
+test "x$HEADERS" = x && HEADERS=`find . -name 'cairo*.h' ! -name 'cairo*-private.h' ! -name 'cairoint.h'`
+
+PRIVATE=$all_cairo_private
+test "x$PRIVATE" = x && PRIVATE=`find . -name 'cairo*-private.h' -or -name 'cairoint.h'`
+
+SOURCES=$all_cairo_sources
+test "x$SOURCES" = x && SOURCES=`find . -name 'cairo*.c' -or -name 'cairo*.cpp'`
+
+ALL="/dev/null $HEADERS $PRIVATE $SOURCES"
+
+echo 'Checking that public header files #include "cairo.h" first (or none)'
+
+for x in $HEADERS; do
+ grep '#.*\<include\>' "$x" /dev/null | head -n 1
+done |
+grep -v '"cairo[.]h"' |
+grep -v 'cairo[.]h:' |
+grep . >&2 && stat=1
+
+
+echo 'Checking that private header files #include "some cairo header" first (or none)'
+
+for x in $PRIVATE; do
+ grep '#.*\<include\>' "$x" /dev/null | head -n 1
+done |
+grep -v '"cairo.*[.]h"' |
+grep -v 'cairoint[.]h:' |
+grep . >&2 && stat=1
+
+
+echo 'Checking that source files #include "cairoint.h" first (or none)'
+
+for x in $SOURCES; do
+ grep '#.*\<include\>' "$x" /dev/null | head -n 1
+done |
+grep -v '"cairoint[.]h"' |
+grep . >&2 && stat=1
+
+
+echo 'Checking that there is no #include <cairo.*.h>'
+grep '#.*\<include\>.*<.*cairo' $ALL >&2 && stat=1
+
+
+echo 'Checking that feature conditionals are used with #if only (not #ifdef)'
+grep '#ifdef CAIRO_HAS_' $ALL && stat=1
+grep '#if.*defined[ ]*(CAIRO_HAS_' $ALL && stat=1
+
+exit $stat
diff --git a/src/drm/cairo-drm-bo.c b/src/drm/cairo-drm-bo.c
new file mode 100755
index 000000000..a5b59f2c9
--- /dev/null
+++ b/src/drm/cairo-drm-bo.c
@@ -0,0 +1,130 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+
+#include "cairo-error-private.h"
+
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#define ERR_DEBUG(x) x
+
+struct drm_gem_close {
+ /** Handle of the object to be closed. */
+ uint32_t handle;
+ uint32_t pad;
+};
+
+struct drm_gem_flink {
+ /** Handle for the object being named */
+ uint32_t handle;
+
+ /** Returned global name */
+ uint32_t name;
+};
+
+struct drm_gem_open {
+ /** Name of object being opened */
+ uint32_t name;
+
+ /** Returned handle for the object */
+ uint32_t handle;
+
+ /** Returned size of the object */
+ uint64_t size;
+};
+
+#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close)
+#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink)
+#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open)
+
+cairo_status_t
+_cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev,
+ cairo_drm_bo_t *bo,
+ uint32_t name)
+{
+ struct drm_gem_open open;
+ int ret;
+
+ open.name = name;
+ open.handle = 0;
+ open.size = 0;
+ do {
+ ret = ioctl (dev->fd, DRM_IOCTL_GEM_OPEN, &open);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1) {
+ ERR_DEBUG((fprintf (stderr, "Failed to open bo for name %d: %s\n",
+ name, strerror (errno))));
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ bo->name = name;
+ bo->size = open.size;
+ bo->handle = open.handle;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_drm_bo_flink (const cairo_drm_device_t *dev,
+ cairo_drm_bo_t *bo)
+{
+ struct drm_gem_flink flink;
+ int ret;
+
+ memset (&flink, 0, sizeof (flink));
+ flink.handle = bo->handle;
+ ret = ioctl (dev->fd, DRM_IOCTL_GEM_FLINK, &flink);
+ if (ret == -1) {
+ ERR_DEBUG((fprintf (stderr, "Failed to flink bo: %s\n",
+ strerror (errno))));
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ bo->name = flink.name;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_drm_bo_close (const cairo_drm_device_t *dev,
+ cairo_drm_bo_t *bo)
+{
+ struct drm_gem_close close;
+ int ret;
+
+ close.handle = bo->handle;
+ do {
+ ret = ioctl (dev->fd, DRM_IOCTL_GEM_CLOSE, &close);
+ } while (ret == -1 && errno == EINTR);
+}
diff --git a/src/drm/cairo-drm-gallium-surface.c b/src/drm/cairo-drm-gallium-surface.c
new file mode 100755
index 000000000..164ab03ce
--- /dev/null
+++ b/src/drm/cairo-drm-gallium-surface.c
@@ -0,0 +1,826 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2009 Eric Anholt
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+
+#include <dlfcn.h>
+
+#include <state_tracker/drm_api.h>
+#include <pipe/p_format.h>
+#include <pipe/p_screen.h>
+#include <pipe/p_context.h>
+#include <pipe/p_state.h>
+
+#include <util/u_inlines.h>
+
+typedef struct _gallium_surface gallium_surface_t;
+typedef struct _gallium_device gallium_device_t;
+
+struct _gallium_device {
+ cairo_drm_device_t drm;
+
+ void *dlhandle;
+ struct drm_api *api;
+
+ struct pipe_screen *screen;
+ struct pipe_context *pipe;
+
+ int max_size;
+};
+
+struct _gallium_surface {
+ cairo_drm_surface_t drm;
+
+ enum pipe_format pipe_format;
+
+ struct pipe_resource *texture;
+ struct pipe_transfer *map_transfer;
+
+ cairo_surface_t *fallback;
+};
+
+static cairo_surface_t *
+gallium_surface_create_internal (gallium_device_t *device,
+ enum pipe_format format,
+ int width, int height);
+
+static inline gallium_device_t *
+gallium_device (gallium_surface_t *surface)
+{
+ return (gallium_device_t *) surface->drm.base.device;
+}
+
+static cairo_format_t
+_cairo_format_from_pipe_format (enum pipe_format format)
+{
+ switch ((int) format) {
+ case PIPE_FORMAT_A8_UNORM:
+ return CAIRO_FORMAT_A8;
+ case PIPE_FORMAT_A8R8G8B8_UNORM:
+ return CAIRO_FORMAT_ARGB32;
+ default:
+ return CAIRO_FORMAT_INVALID;
+ }
+}
+
+static enum pipe_format
+pipe_format_from_format (cairo_format_t format)
+{
+ switch ((int) format) {
+ case CAIRO_FORMAT_A8:
+ return PIPE_FORMAT_A8_UNORM;
+ case CAIRO_FORMAT_ARGB32:
+ return PIPE_FORMAT_A8R8G8B8_UNORM;
+ default:
+ return (enum pipe_format) -1;
+ }
+}
+
+static enum pipe_format
+pipe_format_from_content (cairo_content_t content)
+{
+ if (content == CAIRO_CONTENT_ALPHA)
+ return PIPE_FORMAT_A8_UNORM;
+ else
+ return PIPE_FORMAT_A8R8G8B8_UNORM;
+}
+
+static cairo_bool_t
+format_is_supported_destination (gallium_device_t *device,
+ enum pipe_format format)
+{
+ if (format == (enum pipe_format) -1)
+ return FALSE;
+
+ return device->screen->is_format_supported (device->screen,
+ format,
+ 0,
+ PIPE_BIND_RENDER_TARGET,
+ 0);
+}
+
+#if 0
+static cairo_bool_t
+format_is_supported_source (gallium_device_t *device,
+ enum pipe_format format)
+{
+ return device->screen->is_format_supported (device->screen,
+ format,
+ 0,
+ PIPE_BIND_SAMPLER_VIEW,
+ 0);
+}
+#endif
+
+static cairo_surface_t *
+gallium_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ gallium_surface_t *other = abstract_src;
+ gallium_device_t *device = gallium_device (other);
+ enum pipe_format pipe_format;
+ cairo_surface_t *surface = NULL;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&device->drm.base);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (MAX (width, height) > device->max_size)
+ goto RELEASE;
+
+ if (content == other->drm.base.content)
+ pipe_format = other->pipe_format;
+ else
+ pipe_format = pipe_format_from_content (content);
+
+ if (! format_is_supported_destination (device, pipe_format))
+ goto RELEASE;
+
+ surface = gallium_surface_create_internal (device,
+ pipe_format,
+ width, height);
+
+RELEASE:
+ cairo_device_release (&device->drm.base);
+
+ return surface;
+}
+
+static cairo_status_t
+gallium_surface_finish (void *abstract_surface)
+{
+ gallium_surface_t *surface = abstract_surface;
+ gallium_device_t *device = gallium_device (surface);
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&device->drm.base);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ pipe_resource_reference (&surface->texture, NULL);
+ cairo_device_release (&device->drm.base);
+ }
+
+ return _cairo_drm_surface_finish (&surface->drm);
+}
+
+static cairo_surface_t *
+gallium_surface_map_to_image (gallium_surface_t *surface)
+{
+ gallium_device_t *device = gallium_device (surface);
+ cairo_status_t status;
+ void *ptr = NULL;
+
+ status = cairo_device_acquire (&device->drm.base);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ surface->map_transfer =
+ pipe_get_transfer (device->pipe,
+ surface->texture, 0, 0, 0,
+ PIPE_TRANSFER_MAP_DIRECTLY |
+ PIPE_TRANSFER_READ_WRITE,
+ 0, 0,
+ surface->drm.width,
+ surface->drm.height);
+ if (likely (surface->map_transfer != NULL))
+ ptr = device->pipe->transfer_map (device->pipe, surface->map_transfer);
+
+ cairo_device_release (&device->drm.base);
+
+ if (unlikely (ptr == NULL)) {
+ if (surface->map_transfer != NULL) {
+ device->pipe->transfer_destroy (device->pipe,
+ surface->map_transfer);
+ surface->map_transfer = NULL;
+ }
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ return cairo_image_surface_create_for_data (ptr,
+ surface->drm.format,
+ surface->drm.width,
+ surface->drm.height,
+ surface->map_transfer->stride);
+}
+
+static cairo_status_t
+gallium_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ gallium_surface_t *surface = abstract_surface;
+ gallium_device_t *device = gallium_device (surface);
+ cairo_format_t format;
+ cairo_surface_t *image;
+ cairo_status_t status;
+ struct pipe_transfer *transfer;
+ void *ptr;
+
+ if (surface->fallback != NULL) {
+ *image_out = (cairo_image_surface_t *)
+ cairo_surface_reference (surface->fallback);
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (unlikely (surface->drm.width == 0 || surface->drm.height == 0)) {
+ image = cairo_image_surface_create (surface->drm.format, 0, 0);
+ if (unlikely (image->status))
+ return image->status;
+
+ *image_out = (cairo_image_surface_t *) image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ format = _cairo_format_from_pipe_format (surface->pipe_format);
+ if (format == CAIRO_FORMAT_INVALID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = cairo_device_acquire (&device->drm.base);
+ if (unlikely (status))
+ return status;
+
+ transfer = pipe_get_transfer (device->pipe,
+ surface->texture, 0, 0, 0,
+ PIPE_TRANSFER_READ,
+ 0, 0,
+ surface->drm.width,
+ surface->drm.height);
+ ptr = device->pipe->transfer_map (device->pipe, transfer);
+ cairo_device_release (&device->drm.base);
+
+ image = cairo_image_surface_create_for_data (ptr, format,
+ surface->drm.width,
+ surface->drm.height,
+ surface->drm.stride);
+ if (unlikely (image->status))
+ return image->status;
+
+ *image_out = (cairo_image_surface_t *) image;
+ *image_extra = transfer;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+gallium_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+
+ if (image_extra != NULL) {
+ gallium_device_t *device = gallium_device (abstract_surface);
+
+ device->pipe->transfer_unmap (device->pipe, image_extra);
+ device->pipe->transfer_destroy (device->pipe, image_extra);
+ }
+}
+
+static cairo_status_t
+gallium_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ gallium_surface_t *surface = abstract_surface;
+ gallium_device_t *device = gallium_device (surface);
+ cairo_status_t status;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->fallback == NULL) {
+ device->pipe->flush (device->pipe,
+ PIPE_FLUSH_RENDER_CACHE,
+ NULL);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* kill any outstanding maps */
+ cairo_surface_finish (surface->fallback);
+
+ status = cairo_device_acquire (&device->drm.base);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ device->pipe->transfer_unmap (device->pipe,
+ surface->map_transfer);
+ device->pipe->transfer_destroy (device->pipe,
+ surface->map_transfer);
+ surface->map_transfer = NULL;
+ cairo_device_release (&device->drm.base);
+ }
+
+ status = cairo_surface_status (surface->fallback);
+ cairo_surface_destroy (surface->fallback);
+ surface->fallback = NULL;
+
+ return status;
+}
+
+static cairo_int_status_t
+gallium_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ gallium_surface_t *surface = abstract_surface;
+
+ if (surface->fallback == NULL) {
+ /* XXX insert magic */
+ surface->fallback = gallium_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_paint (surface->fallback, op, source, clip);
+}
+
+static cairo_int_status_t
+gallium_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ gallium_surface_t *surface = abstract_surface;
+
+ if (surface->fallback == NULL) {
+ /* XXX insert magic */
+ surface->fallback = gallium_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_mask (surface->fallback,
+ op, source, mask,
+ clip);
+}
+
+static cairo_int_status_t
+gallium_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ gallium_surface_t *surface = abstract_surface;
+
+ if (surface->fallback == NULL) {
+ /* XXX insert magic */
+ surface->fallback = gallium_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_stroke (surface->fallback,
+ op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+gallium_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ gallium_surface_t *surface = abstract_surface;
+
+ if (surface->fallback == NULL) {
+ /* XXX insert magic */
+ surface->fallback = gallium_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_fill (surface->fallback,
+ op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+gallium_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ gallium_surface_t *surface = abstract_surface;
+
+ *num_remaining = 0;
+
+ if (surface->fallback == NULL) {
+ /* XXX insert magic */
+ surface->fallback = gallium_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_show_text_glyphs (surface->fallback,
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font,
+ clip);
+}
+
+static const cairo_surface_backend_t gallium_surface_backend = {
+ CAIRO_SURFACE_TYPE_DRM,
+ _cairo_default_context_create,
+
+ gallium_surface_create_similar,
+ gallium_surface_finish,
+
+ NULL,
+ gallium_surface_acquire_source_image,
+ gallium_surface_release_source_image,
+
+ NULL, //gallium_surface_acquire_dest_image,
+ NULL, //gallium_surface_release_dest_image,
+ NULL, //gallium_surface_clone_similar,
+ NULL, //gallium_surface_composite,
+ NULL, //gallium_surface_fill_rectangles,
+ NULL, //gallium_surface_composite_trapezoids,
+ NULL, //gallium_surface_create_span_renderer,
+ NULL, //gallium_surface_check_span_renderer,
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_drm_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_drm_surface_get_font_options,
+ gallium_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+ NULL, //gallium_surface_scaled_font_fini,
+ NULL, //gallium_surface_scaled_glyph_fini,
+
+ gallium_surface_paint,
+ gallium_surface_mask,
+ gallium_surface_stroke,
+ gallium_surface_fill,
+ gallium_surface_glyphs,
+
+ NULL, /* snapshot */
+
+ NULL, /* is_similar */
+
+ NULL, /* reset */
+};
+
+static int
+gallium_format_stride_for_width (enum pipe_format format, int width)
+{
+ int stride;
+
+ stride = 1024; /* XXX fugly */
+ while (stride < width)
+ stride *= 2;
+
+ if (format == PIPE_FORMAT_A8R8G8B8_UNORM)
+ stride *= 4;
+
+ return stride;
+}
+
+static cairo_drm_bo_t *
+_gallium_fake_bo_create (uint32_t size, uint32_t name)
+{
+ cairo_drm_bo_t *bo;
+
+ /* XXX integrate with winsys handle */
+
+ bo = malloc (sizeof (cairo_drm_bo_t));
+
+ CAIRO_REFERENCE_COUNT_INIT (&bo->ref_count, 1);
+ bo->name = name;
+ bo->handle = 0;
+ bo->size = size;
+
+ return bo;
+}
+
+static void
+_gallium_fake_bo_release (void *dev, void *bo)
+{
+ free (bo);
+}
+
+static cairo_surface_t *
+gallium_surface_create_internal (gallium_device_t *device,
+ enum pipe_format pipe_format,
+ int width, int height)
+{
+ gallium_surface_t *surface;
+ struct pipe_resource template;
+ cairo_status_t status;
+ cairo_format_t format;
+ int stride, size;
+
+ surface = malloc (sizeof (gallium_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ format = _cairo_format_from_pipe_format (pipe_format);
+ _cairo_surface_init (&surface->drm.base,
+ &gallium_surface_backend,
+ &device->drm.base,
+ _cairo_content_from_format (format));
+ _cairo_drm_surface_init (&surface->drm, format, width, height);
+
+ stride = gallium_format_stride_for_width (pipe_format, width);
+ size = stride * height;
+
+ surface->drm.stride = stride;
+ surface->drm.bo = _gallium_fake_bo_create (size, 0);
+
+ memset(&template, 0, sizeof(template));
+ template.target = PIPE_TEXTURE_2D;
+ template.format = pipe_format;
+ template.width0 = width;
+ template.height0 = height;
+ template.depth0 = 1;
+ template.last_level = 0;
+ template.bind = PIPE_BIND_RENDER_TARGET;
+ surface->texture = device->screen->resource_create (device->screen,
+ &template);
+
+ if (unlikely (surface->texture == NULL)) {
+ status = _cairo_drm_surface_finish (&surface->drm);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ surface->pipe_format = pipe_format;
+ surface->texture = NULL;
+
+ return &surface->drm.base;
+}
+
+static cairo_surface_t *
+gallium_surface_create (cairo_drm_device_t *base_dev,
+ cairo_format_t format,
+ int width, int height)
+{
+ gallium_device_t *device = (gallium_device_t *) base_dev;
+ cairo_surface_t *surface;
+ enum pipe_format pipe_format;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&device->drm.base);
+
+ if (MAX (width, height) > device->max_size) {
+ surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ goto RELEASE;
+ }
+
+ pipe_format = pipe_format_from_format (format);
+ if (! format_is_supported_destination (device, pipe_format)) {
+ surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ goto RELEASE;
+ }
+
+ surface = gallium_surface_create_internal (device,
+ pipe_format,
+ width, height);
+
+RELEASE:
+ cairo_device_release (&device->drm.base);
+
+ return surface;
+}
+
+#if 0
+static cairo_surface_t *
+gallium_surface_create_for_name (cairo_drm_device_t *base_dev,
+ unsigned int name,
+ cairo_format_t format,
+ int width, int height, int stride)
+{
+ gallium_device_t *device;
+ gallium_surface_t *surface;
+ cairo_status_t status;
+ cairo_content_t content;
+
+ surface = malloc (sizeof (gallium_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ case CAIRO_FORMAT_A8:
+ surface->pipe_format = PIPE_FORMAT_A8_UNORM;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_ARGB32:
+ surface->pipe_format = PIPE_FORMAT_A8R8G8B8_UNORM;
+ break;
+ }
+
+ status = cairo_device_acquire (&device->drm.base);
+
+ if (MAX (width, height) > device->max_size) {
+ cairo_device_release (&device->drm.base);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ }
+
+ if (! format_is_supported_destination (device, surface->pipe_format)) {
+ cairo_device_release (&device->drm.base);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ content = _cairo_content_from_format (format);
+ _cairo_surface_init (&surface->drm.base,
+ &gallium_surface_backend,
+ content);
+ _cairo_drm_surface_init (&surface->drm, base_dev);
+
+ surface->drm.bo = _gallium_fake_bo_create (height * stride, name);
+
+ surface->drm.width = width;
+ surface->drm.height = height;
+ surface->drm.stride = stride;
+
+#if 0
+ /* XXX screen->create_from_handle */
+ surface->buffer = device->api->buffer_from_handle (device->api,
+ device->screen,
+ "cairo-gallium alien",
+ name);
+ if (unlikely (surface->buffer == NULL)) {
+ status = _cairo_drm_surface_finish (&surface->drm);
+ cairo_device_release (&device->drm.base);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+#endif
+
+ surface->texture = NULL;
+
+ surface->fallback = NULL;
+
+ cairo_device_release (&device->drm.base);
+
+ return &surface->drm.base;
+}
+
+static cairo_int_status_t
+gallium_surface_flink (void *abstract_surface)
+{
+ gallium_surface_t *surface = abstract_surface;
+ gallium_device_t *device;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ status = cairo_device_acquire (&device->drm.base);
+ if (! device->api->global_handle_from_buffer (device->api,
+ device->screen,
+ surface->buffer,
+ &surface->drm.bo->name))
+ {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ cairo_device_release (&device->drm.base);
+
+ return status;
+}
+#endif
+
+static void
+gallium_device_destroy (void *abstract_device)
+{
+ gallium_device_t *device = abstract_device;
+
+ device->pipe->destroy (device->pipe);
+ device->screen->destroy (device->screen);
+ device->api->destroy (device->api);
+
+ dlclose (device->dlhandle);
+ free (device);
+}
+
+cairo_drm_device_t *
+_cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+ gallium_device_t *device;
+ cairo_status_t status;
+ void *handle;
+ const char *libdir;
+ char buf[4096];
+ struct drm_api *(*ctor) (void);
+
+ /* XXX need search path + probe */
+ libdir = getenv ("CAIRO_GALLIUM_LIBDIR");
+ if (libdir == NULL)
+ libdir = "/usr/lib/dri";
+ buf[snprintf (buf, sizeof (buf)-1, "%s/i915_dri.so", libdir)] = '\0';
+
+ handle = dlopen (buf, RTLD_LAZY);
+ if (handle == NULL)
+ return NULL;
+
+ ctor = dlsym (handle, "drm_api_create");
+ if (ctor == NULL) {
+ dlclose (handle);
+ return NULL;
+ }
+
+ device = malloc (sizeof (gallium_device_t));
+ if (device == NULL) {
+ dlclose (handle);
+ return _cairo_drm_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ device->dlhandle = handle;
+
+ device->drm.surface.create = gallium_surface_create;
+ device->drm.surface.create_for_name = NULL;
+ //device->drm.surface.create_for_name = gallium_surface_create_for_name;
+ device->drm.surface.enable_scan_out = NULL;
+ //device->drm.surface.flink = gallium_surface_flink;
+ device->drm.surface.flink = NULL;
+
+ device->drm.device.flush = NULL;
+ device->drm.device.throttle = NULL;
+ device->drm.device.destroy = gallium_device_destroy;
+
+ device->drm.bo.release = _gallium_fake_bo_release;
+
+ device->api = ctor ();
+ if (device->api == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ device->screen = device->api->create_screen (device->api, fd, NULL);
+ if (device->screen == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_API;
+ }
+
+ device->max_size = 1 << device->screen->get_param (device->screen,
+ PIPE_CAP_MAX_TEXTURE_2D_LEVELS);
+
+ device->pipe = device->screen->context_create (device->screen, device);
+ if (device->pipe == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_SCREEN;
+ }
+
+ return _cairo_drm_device_init (&device->drm,
+ fd, dev,
+ 0, 0,
+ device->max_size);
+
+CLEANUP_SCREEN:
+ device->screen->destroy (device->screen);
+CLEANUP_API:
+ device->api->destroy (device->api);
+CLEANUP:
+ free (device);
+ dlclose (handle);
+ return _cairo_drm_device_create_in_error (status);
+}
diff --git a/src/drm/cairo-drm-i915-glyphs.c b/src/drm/cairo-drm-i915-glyphs.c
new file mode 100755
index 000000000..9944f15a4
--- /dev/null
+++ b/src/drm/cairo-drm-i915-glyphs.c
@@ -0,0 +1,563 @@
+/* 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-composite-rectangles-private.h"
+#include "cairo-drm-i915-private.h"
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+
+static void
+i915_emit_glyph_rectangle_zero (i915_device_t *device,
+ i915_shader_t *shader,
+ int x1, int y1,
+ int x2, int y2,
+ intel_glyph_t *glyph)
+{
+ float *v;
+
+ /* Each vertex is:
+ * 2 vertex coordinates
+ */
+
+ v = i915_add_rectangle (device);
+ *v++ = x2; *v++ = y2;
+ *v++ = x1; *v++ = y2;
+ *v++ = x1; *v++ = y1;
+}
+
+static void
+i915_emit_glyph_rectangle_constant (i915_device_t *device,
+ i915_shader_t *shader,
+ int x1, int y1,
+ int x2, int y2,
+ intel_glyph_t *glyph)
+{
+ float *v;
+
+ /* Each vertex is:
+ * 2 vertex coordinates
+ * 2 glyph texture coordinates
+ */
+
+ v = i915_add_rectangle (device);
+
+ /* bottom right */
+ *v++ = x2; *v++ = y2;
+ *v++ = glyph->texcoord[0];
+
+ /* bottom left */
+ *v++ = x1; *v++ = y2;
+ *v++ = glyph->texcoord[1];
+
+ /* top left */
+ *v++ = x1; *v++ = y1;
+ *v++ = glyph->texcoord[2];
+}
+
+static void
+i915_emit_glyph_rectangle_general (i915_device_t *device,
+ i915_shader_t *shader,
+ int x1, int y1,
+ int x2, int y2,
+ intel_glyph_t *glyph)
+{
+ double s, t;
+ float *v;
+
+ /* Each vertex is:
+ * 2 vertex coordinates
+ * [0-2] source texture coordinates
+ * 2 glyph texture coordinates
+ */
+
+ v = i915_add_rectangle (device);
+
+ /* bottom right */
+ *v++ = x2; *v++ = y2;
+ s = x2, t = y2;
+ switch (shader->source.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
+ break;
+ case VS_TEXTURE:
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = s; *v++ = t;
+ break;
+ case VS_TEXTURE_16:
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = texcoord_2d_16 (s, t);
+ break;
+ }
+ *v++ = glyph->texcoord[0];
+
+ /* bottom left */
+ *v++ = x1; *v++ = y2;
+ s = x1, t = y2;
+ switch (shader->source.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
+ break;
+ case VS_TEXTURE:
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = s; *v++ = t;
+ break;
+ case VS_TEXTURE_16:
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = texcoord_2d_16 (s, t);
+ break;
+ }
+ *v++ = glyph->texcoord[1];
+
+ /* top left */
+ *v++ = x1; *v++ = y1;
+ s = x1, t = y2;
+ switch (shader->source.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
+ break;
+ case VS_TEXTURE:
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = s; *v++ = t;
+ break;
+ case VS_TEXTURE_16:
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = texcoord_2d_16 (s, t);
+ break;
+ }
+ *v++ = glyph->texcoord[2];
+}
+
+typedef void
+(*i915_emit_glyph_rectangle_func_t) (i915_device_t *device,
+ i915_shader_t *shader,
+ int x1, int y1,
+ int x2, int y2,
+ intel_glyph_t *glyph);
+
+static cairo_status_t
+i915_surface_mask_internal (i915_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ i915_surface_t *mask,
+ cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ i915_device_t *device;
+ i915_shader_t shader;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ i915_shader_init (&shader, dst, op, 1.);
+
+ status = i915_shader_acquire_pattern (&shader, &shader.source,
+ source, &extents->bounded);
+ if (unlikely (status))
+ return status;
+
+ shader.mask.type.vertex = VS_TEXTURE_16;
+ shader.mask.type.pattern = PATTERN_TEXTURE;
+ shader.mask.type.fragment = FS_TEXTURE;
+ shader.mask.base.content = mask->intel.drm.base.content;
+ shader.mask.base.texfmt = TEXCOORDFMT_2D_16;
+ shader.mask.base.n_samplers = 1;
+ shader.mask.base.sampler[0] =
+ (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+ i915_texture_filter (CAIRO_FILTER_NEAREST);
+ shader.mask.base.sampler[1] =
+ SS3_NORMALIZED_COORDS |
+ i915_texture_extend (CAIRO_EXTEND_NONE);
+
+ cairo_matrix_init_translate (&shader.mask.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ cairo_matrix_scale (&shader.mask.base.matrix,
+ 1. / mask->intel.drm.width,
+ 1. / mask->intel.drm.height);
+
+ shader.mask.base.bo = intel_bo_reference (to_intel_bo (mask->intel.drm.bo));
+ shader.mask.base.offset[0] = 0;
+ shader.mask.base.map[0] = mask->map0;
+ shader.mask.base.map[1] = mask->map1;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ i915_shader_set_clip (&shader, clip);
+ }
+
+ status = cairo_device_acquire (dst->intel.drm.base.device);
+ if (unlikely (status))
+ goto CLEANUP_SHADER;
+
+ device = i915_device (dst);
+
+ status = i915_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto CLEANUP_DEVICE;
+
+ if (clip_region != NULL) {
+ unsigned int n, num_rectangles;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rectangles; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+
+ shader.add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.x + rect.width, rect.y + rect.height);
+ }
+ } else {
+ shader.add_rectangle (&shader,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.x + extents->bounded.width,
+ extents->bounded.y + extents->bounded.height);
+ }
+
+ if (! extents->is_bounded)
+ status = i915_fixup_unbounded (dst, extents, clip);
+
+CLEANUP_DEVICE:
+ cairo_device_release (&device->intel.base.base);
+CLEANUP_SHADER:
+ i915_shader_fini (&shader);
+ return status;
+}
+
+cairo_int_status_t
+i915_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ i915_surface_t *surface = abstract_surface;
+ i915_surface_t *mask = NULL;
+ i915_device_t *device;
+ i915_shader_t shader;
+ cairo_composite_rectangles_t extents;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_bool_t overlap;
+ cairo_region_t *clip_region = NULL;
+ intel_bo_t *last_bo = NULL;
+ i915_emit_glyph_rectangle_func_t emit_func;
+ cairo_scaled_glyph_t *glyph_cache[64];
+ cairo_status_t status;
+ int mask_x = 0, mask_y = 0;
+ int i = 0;
+
+ *num_remaining = 0;
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ surface->intel.drm.width,
+ surface->intel.drm.height,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents.mask))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status))
+ return status;
+
+ have_clip = TRUE;
+ }
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+ return status;
+ }
+ }
+
+ if (i915_surface_needs_tiling (surface)) {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (overlap || ! extents.is_bounded) {
+ cairo_format_t format;
+
+ format = CAIRO_FORMAT_A8;
+ if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
+ format = CAIRO_FORMAT_ARGB32;
+
+ mask = (i915_surface_t *)
+ i915_surface_create_internal (&i915_device (surface)->intel.base,
+ format,
+ extents.bounded.width,
+ extents.bounded.height,
+ I915_TILING_DEFAULT,
+ TRUE);
+ if (unlikely (mask->intel.drm.base.status))
+ return mask->intel.drm.base.status;
+
+ status = i915_surface_clear (mask);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&mask->intel.drm.base);
+ return status;
+ }
+
+ i915_shader_init (&shader, mask, CAIRO_OPERATOR_ADD, 1.);
+
+ status = i915_shader_acquire_pattern (&shader, &shader.source,
+ &_cairo_pattern_white.base,
+ &extents.bounded);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&mask->intel.drm.base);
+ return status;
+ }
+
+ mask_x = -extents.bounded.x;
+ mask_y = -extents.bounded.y;
+ } else {
+ i915_shader_init (&shader, surface, op, 1.);
+
+ status = i915_shader_acquire_pattern (&shader, &shader.source,
+ source, &extents.bounded);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ i915_shader_set_clip (&shader, clip);
+ }
+ }
+
+ shader.mask.type.fragment = FS_TEXTURE;
+ shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
+ shader.mask.base.texfmt = TEXCOORDFMT_2D_16;
+ shader.mask.base.n_samplers = 1;
+ shader.mask.base.sampler[0] =
+ (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+ i915_texture_filter (CAIRO_FILTER_NEAREST);
+ shader.mask.base.sampler[1] =
+ SS3_NORMALIZED_COORDS |
+ i915_texture_extend (CAIRO_EXTEND_NONE);
+
+ switch (shader.source.type.vertex) {
+ case VS_ZERO:
+ emit_func = i915_emit_glyph_rectangle_zero;
+ break;
+ case VS_CONSTANT:
+ emit_func = i915_emit_glyph_rectangle_constant;
+ break;
+ default:
+ case VS_LINEAR:
+ case VS_TEXTURE:
+ case VS_TEXTURE_16:
+ emit_func = i915_emit_glyph_rectangle_general;
+ break;
+ }
+
+ status = cairo_device_acquire (surface->intel.drm.base.device);
+ if (unlikely (status))
+ goto CLEANUP_SHADER;
+
+ device = i915_device (surface);
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ if (scaled_font->surface_private == NULL) {
+ scaled_font->surface_private = device;
+ scaled_font->surface_backend = surface->intel.drm.base.backend;
+ cairo_list_add (&scaled_font->link, &device->intel.fonts);
+ }
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+
+ for (i = 0; i < num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+ int x, y, x1, x2, y1, y2;
+ int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
+ intel_glyph_t *glyph;
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
+ {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status))
+ goto FINISH;
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ if (unlikely (scaled_glyph->metrics.width == 0 ||
+ scaled_glyph->metrics.height == 0))
+ {
+ continue;
+ }
+
+ /* XXX glyph images are snapped to pixel locations */
+ x = _cairo_lround (glyphs[i].x);
+ y = _cairo_lround (glyphs[i].y);
+
+ x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+ y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+ x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
+ y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
+
+ if (x2 < extents.bounded.x ||
+ y2 < extents.bounded.y ||
+ x1 > extents.bounded.x + extents.bounded.width ||
+ y1 > extents.bounded.y + extents.bounded.height)
+ {
+ continue;
+ }
+
+ if (scaled_glyph->surface_private == NULL) {
+ status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) {
+ status = CAIRO_STATUS_SUCCESS;
+ continue;
+ }
+ if (unlikely (status))
+ goto FINISH;
+ }
+
+ glyph = intel_glyph_pin (scaled_glyph->surface_private);
+ if (glyph->cache->buffer.bo != last_bo) {
+ intel_buffer_cache_t *cache = glyph->cache;
+
+ shader.mask.base.bo = cache->buffer.bo;
+ shader.mask.base.offset[0] = cache->buffer.offset;
+ shader.mask.base.map[0] = cache->buffer.map0;
+ shader.mask.base.map[1] = cache->buffer.map1;
+ shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
+
+ status = i915_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto FINISH;
+
+ last_bo = cache->buffer.bo;
+ }
+
+ x2 = x1 + glyph->width;
+ y2 = y1 + glyph->height;
+
+ if (mask_x)
+ x1 += mask_x, x2 += mask_x;
+ if (mask_y)
+ y1 += mask_y, y2 += mask_y;
+
+ /* XXX clip glyph */
+ emit_func (device, &shader, x1, y1, x2, y2, glyph);
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ FINISH:
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ cairo_device_release (surface->intel.drm.base.device);
+ CLEANUP_SHADER:
+ i915_shader_fini (&shader);
+
+ if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) {
+ cairo_path_fixed_t path;
+
+ _cairo_path_fixed_init (&path);
+ status = _cairo_scaled_font_glyph_path (scaled_font,
+ glyphs + i, num_glyphs - i,
+ &path);
+ if (mask_x | mask_y) {
+ _cairo_path_fixed_translate (&path,
+ _cairo_fixed_from_int (mask_x),
+ _cairo_fixed_from_int (mask_y));
+ }
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = surface->intel.drm.base.backend->fill (shader.target,
+ shader.op,
+ mask != NULL ? &_cairo_pattern_white.base : source,
+ &path,
+ CAIRO_FILL_RULE_WINDING,
+ 0,
+ scaled_font->options.antialias,
+ clip);
+ }
+ _cairo_path_fixed_fini (&path);
+ }
+
+ if (mask != NULL) {
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = i915_surface_mask_internal (surface, op, source, mask,
+ clip, &extents);
+ }
+ cairo_surface_finish (&mask->intel.drm.base);
+ cairo_surface_destroy (&mask->intel.drm.base);
+ }
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
diff --git a/src/drm/cairo-drm-i915-private.h b/src/drm/cairo-drm-i915-private.h
new file mode 100755
index 000000000..c750cf4cf
--- /dev/null
+++ b/src/drm/cairo-drm-i915-private.h
@@ -0,0 +1,1270 @@
+/*
+ * Copyright © 2006, 2009 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_DRM_I915_PRIVATE_H
+#define CAIRO_DRM_I915_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-command-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+#include "cairo-freelist-private.h"
+
+#include <setjmp.h>
+
+#define I915_VERBOSE 1
+
+#define I915_MAX_TEX_INDIRECT 4
+#define I915_MAX_TEX_INSN 32
+#define I915_MAX_ALU_INSN 64
+#define I915_MAX_DECL_INSN 27
+#define I915_MAX_TEMPORARY 16
+
+/* Each instruction is 3 dwords long, though most don't require all
+ * this space. Maximum of 123 instructions. Smaller maxes per insn
+ * type.
+ */
+#define _3DSTATE_PIXEL_SHADER_PROGRAM (CMD_3D|(0x1d<<24)|(0x5<<16))
+
+#define REG_TYPE_R 0 /* temporary regs, no need to
+ * dcl, must be written before
+ * read -- Preserved between
+ * phases.
+ */
+#define REG_TYPE_T 1 /* Interpolated values, must be
+ * dcl'ed before use.
+ *
+ * 0..7: texture coord,
+ * 8: diffuse spec,
+ * 9: specular color,
+ * 10: fog parameter in w.
+ */
+#define REG_TYPE_CONST 2 /* Restriction: only one const
+ * can be referenced per
+ * instruction, though it may be
+ * selected for multiple inputs.
+ * Constants not initialized
+ * default to zero.
+ */
+#define REG_TYPE_S 3 /* sampler */
+#define REG_TYPE_OC 4 /* output color (rgba) */
+#define REG_TYPE_OD 5 /* output depth (w), xyz are
+ * temporaries. If not written,
+ * interpolated depth is used?
+ */
+#define REG_TYPE_U 6 /* unpreserved temporaries */
+#define REG_TYPE_MASK 0x7
+#define REG_TYPE_SHIFT 4
+#define REG_NR_MASK 0xf
+
+/* REG_TYPE_T:
+ */
+#define T_TEX0 0
+#define T_TEX1 1
+#define T_TEX2 2
+#define T_TEX3 3
+#define T_TEX4 4
+#define T_TEX5 5
+#define T_TEX6 6
+#define T_TEX7 7
+#define T_DIFFUSE 8
+#define T_SPECULAR 9
+#define T_FOG_W 10 /* interpolated fog is in W coord */
+
+/* Arithmetic instructions */
+
+/* .replicate_swizzle == selection and replication of a particular
+ * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww
+ */
+#define A0_NOP (0x0<<24) /* no operation */
+#define A0_ADD (0x1<<24) /* dst = src0 + src1 */
+#define A0_MOV (0x2<<24) /* dst = src0 */
+#define A0_MUL (0x3<<24) /* dst = src0 * src1 */
+#define A0_MAD (0x4<<24) /* dst = src0 * src1 + src2 */
+#define A0_DP2ADD (0x5<<24) /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */
+#define A0_DP3 (0x6<<24) /* dst.xyzw = src0.xyz dot src1.xyz */
+#define A0_DP4 (0x7<<24) /* dst.xyzw = src0.xyzw dot src1.xyzw */
+#define A0_FRC (0x8<<24) /* dst = src0 - floor(src0) */
+#define A0_RCP (0x9<<24) /* dst.xyzw = 1/(src0.replicate_swizzle) */
+#define A0_RSQ (0xa<<24) /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */
+#define A0_EXP (0xb<<24) /* dst.xyzw = exp2(src0.replicate_swizzle) */
+#define A0_LOG (0xc<<24) /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */
+#define A0_CMP (0xd<<24) /* dst = (src0 >= 0.0) ? src1 : src2 */
+#define A0_MIN (0xe<<24) /* dst = (src0 < src1) ? src0 : src1 */
+#define A0_MAX (0xf<<24) /* dst = (src0 >= src1) ? src0 : src1 */
+#define A0_FLR (0x10<<24) /* dst = floor(src0) */
+#define A0_MOD (0x11<<24) /* dst = src0 fmod 1.0 */
+#define A0_TRC (0x12<<24) /* dst = int(src0) */
+#define A0_SGE (0x13<<24) /* dst = src0 >= src1 ? 1.0 : 0.0 */
+#define A0_SLT (0x14<<24) /* dst = src0 < src1 ? 1.0 : 0.0 */
+#define A0_DEST_SATURATE (1<<22)
+#define A0_DEST_TYPE_SHIFT 19
+/* Allow: R, OC, OD, U */
+#define A0_DEST_NR_SHIFT 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define A0_DEST_CHANNEL_X (1<<10)
+#define A0_DEST_CHANNEL_Y (2<<10)
+#define A0_DEST_CHANNEL_Z (4<<10)
+#define A0_DEST_CHANNEL_W (8<<10)
+#define A0_DEST_CHANNEL_ALL (0xf<<10)
+#define A0_DEST_CHANNEL_SHIFT 10
+#define A0_SRC0_TYPE_SHIFT 7
+#define A0_SRC0_NR_SHIFT 2
+
+#define A0_DEST_CHANNEL_XY (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y)
+#define A0_DEST_CHANNEL_XYZ (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z)
+
+#define SRC_X 0
+#define SRC_Y 1
+#define SRC_Z 2
+#define SRC_W 3
+#define SRC_ZERO 4
+#define SRC_ONE 5
+
+#define A1_SRC0_CHANNEL_X_NEGATE (1<<31)
+#define A1_SRC0_CHANNEL_X_SHIFT 28
+#define A1_SRC0_CHANNEL_Y_NEGATE (1<<27)
+#define A1_SRC0_CHANNEL_Y_SHIFT 24
+#define A1_SRC0_CHANNEL_Z_NEGATE (1<<23)
+#define A1_SRC0_CHANNEL_Z_SHIFT 20
+#define A1_SRC0_CHANNEL_W_NEGATE (1<<19)
+#define A1_SRC0_CHANNEL_W_SHIFT 16
+#define A1_SRC1_TYPE_SHIFT 13
+#define A1_SRC1_NR_SHIFT 8
+#define A1_SRC1_CHANNEL_X_NEGATE (1<<7)
+#define A1_SRC1_CHANNEL_X_SHIFT 4
+#define A1_SRC1_CHANNEL_Y_NEGATE (1<<3)
+#define A1_SRC1_CHANNEL_Y_SHIFT 0
+
+#define A2_SRC1_CHANNEL_Z_NEGATE (1<<31)
+#define A2_SRC1_CHANNEL_Z_SHIFT 28
+#define A2_SRC1_CHANNEL_W_NEGATE (1<<27)
+#define A2_SRC1_CHANNEL_W_SHIFT 24
+#define A2_SRC2_TYPE_SHIFT 21
+#define A2_SRC2_NR_SHIFT 16
+#define A2_SRC2_CHANNEL_X_NEGATE (1<<15)
+#define A2_SRC2_CHANNEL_X_SHIFT 12
+#define A2_SRC2_CHANNEL_Y_NEGATE (1<<11)
+#define A2_SRC2_CHANNEL_Y_SHIFT 8
+#define A2_SRC2_CHANNEL_Z_NEGATE (1<<7)
+#define A2_SRC2_CHANNEL_Z_SHIFT 4
+#define A2_SRC2_CHANNEL_W_NEGATE (1<<3)
+#define A2_SRC2_CHANNEL_W_SHIFT 0
+
+/* Texture instructions */
+#define T0_TEXLD (0x15<<24) /* Sample texture using predeclared
+ * sampler and address, and output
+ * filtered texel data to destination
+ * register */
+#define T0_TEXLDP (0x16<<24) /* Same as texld but performs a
+ * perspective divide of the texture
+ * coordinate .xyz values by .w before
+ * sampling. */
+#define T0_TEXLDB (0x17<<24) /* Same as texld but biases the
+ * computed LOD by w. Only S4.6 two's
+ * comp is used. This implies that a
+ * float to fixed conversion is
+ * done. */
+#define T0_TEXKILL (0x18<<24) /* Does not perform a sampling
+ * operation. Simply kills the pixel
+ * if any channel of the address
+ * register is < 0.0. */
+#define T0_DEST_TYPE_SHIFT 19
+/* Allow: R, OC, OD, U */
+/* Note: U (unpreserved) regs do not retain their values between
+ * phases (cannot be used for feedback)
+ *
+ * Note: oC and OD registers can only be used as the destination of a
+ * texture instruction once per phase (this is an implementation
+ * restriction).
+ */
+#define T0_DEST_NR_SHIFT 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define T0_SAMPLER_NR_SHIFT 0 /* This field ignored for TEXKILL */
+#define T0_SAMPLER_NR_MASK (0xf<<0)
+
+#define T1_ADDRESS_REG_TYPE_SHIFT 24 /* Reg to use as texture coord */
+/* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */
+#define T1_ADDRESS_REG_NR_SHIFT 17
+#define T2_MBZ 0
+
+/* Declaration instructions */
+#define D0_DCL (0x19<<24) /* Declare a t (interpolated attrib)
+ * register or an s (sampler)
+ * register. */
+#define D0_SAMPLE_TYPE_SHIFT 22
+#define D0_SAMPLE_TYPE_2D (0x0<<22)
+#define D0_SAMPLE_TYPE_CUBE (0x1<<22)
+#define D0_SAMPLE_TYPE_VOLUME (0x2<<22)
+#define D0_SAMPLE_TYPE_MASK (0x3<<22)
+
+#define D0_TYPE_SHIFT 19
+/* Allow: T, S */
+#define D0_NR_SHIFT 14
+/* Allow T: 0..10, S: 0..15 */
+#define D0_CHANNEL_X (1<<10)
+#define D0_CHANNEL_Y (2<<10)
+#define D0_CHANNEL_Z (4<<10)
+#define D0_CHANNEL_W (8<<10)
+#define D0_CHANNEL_ALL (0xf<<10)
+#define D0_CHANNEL_NONE (0<<10)
+
+#define D0_CHANNEL_XY (D0_CHANNEL_X|D0_CHANNEL_Y)
+#define D0_CHANNEL_XYZ (D0_CHANNEL_XY|D0_CHANNEL_Z)
+
+/* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse
+ * or specular declarations.
+ *
+ * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw)
+ *
+ * Must be zero for S (sampler) dcls
+ */
+#define D1_MBZ 0
+#define D2_MBZ 0
+
+
+/* MASK_* are the unshifted bitmasks of the destination mask in arithmetic
+ * operations
+ */
+#define MASK_X 0x1
+#define MASK_Y 0x2
+#define MASK_Z 0x4
+#define MASK_W 0x8
+#define MASK_XYZ (MASK_X | MASK_Y | MASK_Z)
+#define MASK_XYZW (MASK_XYZ | MASK_W)
+#define MASK_SATURATE 0x10
+
+/* Temporary, undeclared regs. Preserved between phases */
+#define FS_R0 ((REG_TYPE_R << REG_TYPE_SHIFT) | 0)
+#define FS_R1 ((REG_TYPE_R << REG_TYPE_SHIFT) | 1)
+#define FS_R2 ((REG_TYPE_R << REG_TYPE_SHIFT) | 2)
+#define FS_R3 ((REG_TYPE_R << REG_TYPE_SHIFT) | 3)
+
+/* Texture coordinate regs. Must be declared. */
+#define FS_T0 ((REG_TYPE_T << REG_TYPE_SHIFT) | 0)
+#define FS_T1 ((REG_TYPE_T << REG_TYPE_SHIFT) | 1)
+#define FS_T2 ((REG_TYPE_T << REG_TYPE_SHIFT) | 2)
+#define FS_T3 ((REG_TYPE_T << REG_TYPE_SHIFT) | 3)
+#define FS_T4 ((REG_TYPE_T << REG_TYPE_SHIFT) | 4)
+#define FS_T5 ((REG_TYPE_T << REG_TYPE_SHIFT) | 5)
+#define FS_T6 ((REG_TYPE_T << REG_TYPE_SHIFT) | 6)
+#define FS_T7 ((REG_TYPE_T << REG_TYPE_SHIFT) | 7)
+#define FS_T8 ((REG_TYPE_T << REG_TYPE_SHIFT) | 8)
+#define FS_T9 ((REG_TYPE_T << REG_TYPE_SHIFT) | 9)
+#define FS_T10 ((REG_TYPE_T << REG_TYPE_SHIFT) | 10)
+
+/* Constant values */
+#define FS_C0 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 0)
+#define FS_C1 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 1)
+#define FS_C2 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 2)
+#define FS_C3 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 3)
+#define FS_C4 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 4)
+#define FS_C5 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 5)
+#define FS_C6 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 6)
+#define FS_C7 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 7)
+
+/* Sampler regs */
+#define FS_S0 ((REG_TYPE_S << REG_TYPE_SHIFT) | 0)
+#define FS_S1 ((REG_TYPE_S << REG_TYPE_SHIFT) | 1)
+#define FS_S2 ((REG_TYPE_S << REG_TYPE_SHIFT) | 2)
+#define FS_S3 ((REG_TYPE_S << REG_TYPE_SHIFT) | 3)
+
+/* Output color */
+#define FS_OC ((REG_TYPE_OC << REG_TYPE_SHIFT) | 0)
+
+/* Output depth */
+#define FS_OD ((REG_TYPE_OD << REG_TYPE_SHIFT) | 0)
+
+/* Unpreserved temporary regs */
+#define FS_U0 ((REG_TYPE_U << REG_TYPE_SHIFT) | 0)
+#define FS_U1 ((REG_TYPE_U << REG_TYPE_SHIFT) | 1)
+#define FS_U2 ((REG_TYPE_U << REG_TYPE_SHIFT) | 2)
+#define FS_U3 ((REG_TYPE_U << REG_TYPE_SHIFT) | 3)
+
+#define X_CHANNEL_SHIFT (REG_TYPE_SHIFT + 3)
+#define Y_CHANNEL_SHIFT (X_CHANNEL_SHIFT + 4)
+#define Z_CHANNEL_SHIFT (Y_CHANNEL_SHIFT + 4)
+#define W_CHANNEL_SHIFT (Z_CHANNEL_SHIFT + 4)
+
+#define REG_CHANNEL_MASK 0xf
+
+#define REG_NR(reg) ((reg) & REG_NR_MASK)
+#define REG_TYPE(reg) (((reg) >> REG_TYPE_SHIFT) & REG_TYPE_MASK)
+#define REG_X(reg) (((reg) >> X_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+#define REG_Y(reg) (((reg) >> Y_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+#define REG_Z(reg) (((reg) >> Z_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+#define REG_W(reg) (((reg) >> W_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+
+enum i915_fs_channel {
+ X_CHANNEL_VAL = 0,
+ Y_CHANNEL_VAL,
+ Z_CHANNEL_VAL,
+ W_CHANNEL_VAL,
+ ZERO_CHANNEL_VAL,
+ ONE_CHANNEL_VAL,
+
+ NEG_X_CHANNEL_VAL = X_CHANNEL_VAL | 0x8,
+ NEG_Y_CHANNEL_VAL = Y_CHANNEL_VAL | 0x8,
+ NEG_Z_CHANNEL_VAL = Z_CHANNEL_VAL | 0x8,
+ NEG_W_CHANNEL_VAL = W_CHANNEL_VAL | 0x8,
+ NEG_ONE_CHANNEL_VAL = ONE_CHANNEL_VAL | 0x8
+};
+
+#define i915_fs_operand(reg, x, y, z, w) \
+ (reg) | \
+ (x##_CHANNEL_VAL << X_CHANNEL_SHIFT) | \
+ (y##_CHANNEL_VAL << Y_CHANNEL_SHIFT) | \
+ (z##_CHANNEL_VAL << Z_CHANNEL_SHIFT) | \
+ (w##_CHANNEL_VAL << W_CHANNEL_SHIFT)
+
+/*
+ * Construct an operand description for using a register with no swizzling
+ */
+#define i915_fs_operand_reg(reg) \
+ i915_fs_operand(reg, X, Y, Z, W)
+
+#define i915_fs_operand_reg_negate(reg) \
+ i915_fs_operand(reg, NEG_X, NEG_Y, NEG_Z, NEG_W)
+
+/*
+ * Returns an operand containing (0.0, 0.0, 0.0, 0.0).
+ */
+#define i915_fs_operand_zero() i915_fs_operand(FS_R0, ZERO, ZERO, ZERO, ZERO)
+
+/*
+ * Returns an unused operand
+ */
+#define i915_fs_operand_none() i915_fs_operand_zero()
+
+/*
+ * Returns an operand containing (1.0, 1.0, 1.0, 1.0).
+ */
+#define i915_fs_operand_one() i915_fs_operand(FS_R0, ONE, ONE, ONE, ONE)
+
+#define i915_get_hardware_channel_val(val, shift, negate) \
+ (((val & 0x7) << shift) | ((val & 0x8) ? negate : 0))
+
+/*
+ * Outputs a fragment shader command to declare a sampler or texture register.
+ */
+#define i915_fs_dcl(reg) \
+do { \
+ OUT_DWORD (D0_DCL | \
+ (REG_TYPE(reg) << D0_TYPE_SHIFT) | \
+ (REG_NR(reg) << D0_NR_SHIFT) | \
+ ((REG_TYPE(reg) != REG_TYPE_S) ? D0_CHANNEL_ALL : 0)); \
+ OUT_DWORD (0); \
+ OUT_DWORD (0); \
+} while (0)
+
+#define i915_fs_texld(dest_reg, sampler_reg, address_reg) \
+do { \
+ OUT_DWORD (T0_TEXLD | \
+ (REG_TYPE(dest_reg) << T0_DEST_TYPE_SHIFT) | \
+ (REG_NR(dest_reg) << T0_DEST_NR_SHIFT) | \
+ (REG_NR(sampler_reg) << T0_SAMPLER_NR_SHIFT)); \
+ OUT_DWORD((REG_TYPE(address_reg) << T1_ADDRESS_REG_TYPE_SHIFT) | \
+ (REG_NR(address_reg) << T1_ADDRESS_REG_NR_SHIFT)); \
+ OUT_DWORD (0); \
+} while (0)
+
+#define i915_fs_arith_masked(op, dest_reg, dest_mask, operand0, operand1, operand2) \
+ _i915_fs_arith_masked(A0_##op, dest_reg, dest_mask, operand0, operand1, operand2)
+
+#define i915_fs_arith(op, dest_reg, operand0, operand1, operand2) \
+ _i915_fs_arith(A0_##op, dest_reg, operand0, operand1, operand2)
+
+#define _i915_fs_arith_masked(cmd, dest_reg, dest_mask, operand0, operand1, operand2) \
+do { \
+ /* Set up destination register and write mask */ \
+ OUT_DWORD (cmd | \
+ (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \
+ (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \
+ (((dest_mask) & ~MASK_SATURATE) << A0_DEST_CHANNEL_SHIFT) | \
+ (((dest_mask) & MASK_SATURATE) ? A0_DEST_SATURATE : 0) | \
+ /* Set up operand 0 */ \
+ (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \
+ (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \
+ OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \
+ A1_SRC0_CHANNEL_X_SHIFT, \
+ A1_SRC0_CHANNEL_X_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Y(operand0), \
+ A1_SRC0_CHANNEL_Y_SHIFT, \
+ A1_SRC0_CHANNEL_Y_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Z(operand0), \
+ A1_SRC0_CHANNEL_Z_SHIFT, \
+ A1_SRC0_CHANNEL_Z_NEGATE) | \
+ i915_get_hardware_channel_val(REG_W(operand0), \
+ A1_SRC0_CHANNEL_W_SHIFT, \
+ A1_SRC0_CHANNEL_W_NEGATE) | \
+ /* Set up operand 1 */ \
+ (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \
+ (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \
+ i915_get_hardware_channel_val(REG_X(operand1), \
+ A1_SRC1_CHANNEL_X_SHIFT, \
+ A1_SRC1_CHANNEL_X_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Y(operand1), \
+ A1_SRC1_CHANNEL_Y_SHIFT, \
+ A1_SRC1_CHANNEL_Y_NEGATE)); \
+ OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \
+ A2_SRC1_CHANNEL_Z_SHIFT, \
+ A2_SRC1_CHANNEL_Z_NEGATE) | \
+ i915_get_hardware_channel_val(REG_W(operand1), \
+ A2_SRC1_CHANNEL_W_SHIFT, \
+ A2_SRC1_CHANNEL_W_NEGATE) | \
+ /* Set up operand 2 */ \
+ (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \
+ (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \
+ i915_get_hardware_channel_val(REG_X(operand2), \
+ A2_SRC2_CHANNEL_X_SHIFT, \
+ A2_SRC2_CHANNEL_X_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Y(operand2), \
+ A2_SRC2_CHANNEL_Y_SHIFT, \
+ A2_SRC2_CHANNEL_Y_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Z(operand2), \
+ A2_SRC2_CHANNEL_Z_SHIFT, \
+ A2_SRC2_CHANNEL_Z_NEGATE) | \
+ i915_get_hardware_channel_val(REG_W(operand2), \
+ A2_SRC2_CHANNEL_W_SHIFT, \
+ A2_SRC2_CHANNEL_W_NEGATE)); \
+} while (0)
+
+#define _i915_fs_arith(cmd, dest_reg, operand0, operand1, operand2) do {\
+ /* Set up destination register and write mask */ \
+ OUT_DWORD (cmd | \
+ (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \
+ (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \
+ (A0_DEST_CHANNEL_ALL) | \
+ /* Set up operand 0 */ \
+ (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \
+ (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \
+ OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \
+ A1_SRC0_CHANNEL_X_SHIFT, \
+ A1_SRC0_CHANNEL_X_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Y(operand0), \
+ A1_SRC0_CHANNEL_Y_SHIFT, \
+ A1_SRC0_CHANNEL_Y_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Z(operand0), \
+ A1_SRC0_CHANNEL_Z_SHIFT, \
+ A1_SRC0_CHANNEL_Z_NEGATE) | \
+ i915_get_hardware_channel_val(REG_W(operand0), \
+ A1_SRC0_CHANNEL_W_SHIFT, \
+ A1_SRC0_CHANNEL_W_NEGATE) | \
+ /* Set up operand 1 */ \
+ (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \
+ (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \
+ i915_get_hardware_channel_val(REG_X(operand1), \
+ A1_SRC1_CHANNEL_X_SHIFT, \
+ A1_SRC1_CHANNEL_X_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Y(operand1), \
+ A1_SRC1_CHANNEL_Y_SHIFT, \
+ A1_SRC1_CHANNEL_Y_NEGATE)); \
+ OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \
+ A2_SRC1_CHANNEL_Z_SHIFT, \
+ A2_SRC1_CHANNEL_Z_NEGATE) | \
+ i915_get_hardware_channel_val(REG_W(operand1), \
+ A2_SRC1_CHANNEL_W_SHIFT, \
+ A2_SRC1_CHANNEL_W_NEGATE) | \
+ /* Set up operand 2 */ \
+ (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \
+ (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \
+ i915_get_hardware_channel_val(REG_X(operand2), \
+ A2_SRC2_CHANNEL_X_SHIFT, \
+ A2_SRC2_CHANNEL_X_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Y(operand2), \
+ A2_SRC2_CHANNEL_Y_SHIFT, \
+ A2_SRC2_CHANNEL_Y_NEGATE) | \
+ i915_get_hardware_channel_val(REG_Z(operand2), \
+ A2_SRC2_CHANNEL_Z_SHIFT, \
+ A2_SRC2_CHANNEL_Z_NEGATE) | \
+ i915_get_hardware_channel_val(REG_W(operand2), \
+ A2_SRC2_CHANNEL_W_SHIFT, \
+ A2_SRC2_CHANNEL_W_NEGATE)); \
+} while (0)
+
+#define i915_fs_mov(dest_reg, operand0) \
+ i915_fs_arith(MOV, dest_reg, \
+ operand0, \
+ i915_fs_operand_none(), \
+ i915_fs_operand_none())
+
+#define i915_fs_mov_masked(dest_reg, dest_mask, operand0) \
+ i915_fs_arith_masked (MOV, dest_reg, dest_mask, \
+ operand0, \
+ i915_fs_operand_none(), \
+ i915_fs_operand_none())
+
+
+#define i915_fs_frc(dest_reg, operand0) \
+ i915_fs_arith (FRC, dest_reg, \
+ operand0, \
+ i915_fs_operand_none(), \
+ i915_fs_operand_none())
+
+/* Add operand0 and operand1 and put the result in dest_reg */
+#define i915_fs_add(dest_reg, operand0, operand1) \
+ i915_fs_arith (ADD, dest_reg, \
+ operand0, operand1, \
+ i915_fs_operand_none())
+
+/* Multiply operand0 and operand1 and put the result in dest_reg */
+#define i915_fs_mul(dest_reg, operand0, operand1) \
+ i915_fs_arith (MUL, dest_reg, \
+ operand0, operand1, \
+ i915_fs_operand_none())
+
+/* Computes 1/sqrt(operand0.replicate_swizzle) puts the result in dest_reg */
+#define i915_fs_rsq(dest_reg, dest_mask, operand0) \
+do { \
+ if (dest_mask) { \
+ i915_fs_arith_masked (RSQ, dest_reg, dest_mask, \
+ operand0, \
+ i915_fs_operand_none (), \
+ i915_fs_operand_none ()); \
+ } else { \
+ i915_fs_arith (RSQ, dest_reg, \
+ operand0, \
+ i915_fs_operand_none (), \
+ i915_fs_operand_none ()); \
+ } \
+} while (0)
+
+/* Puts the minimum of operand0 and operand1 in dest_reg */
+#define i915_fs_min(dest_reg, operand0, operand1) \
+ i915_fs_arith (MIN, dest_reg, \
+ operand0, operand1, \
+ i915_fs_operand_none())
+
+/* Puts the maximum of operand0 and operand1 in dest_reg */
+#define i915_fs_max(dest_reg, operand0, operand1) \
+ i915_fs_arith (MAX, dest_reg, \
+ operand0, operand1, \
+ i915_fs_operand_none())
+
+#define i915_fs_cmp(dest_reg, operand0, operand1, operand2) \
+ i915_fs_arith (CMP, dest_reg, operand0, operand1, operand2)
+
+/* Perform operand0 * operand1 + operand2 and put the result in dest_reg */
+#define i915_fs_mad(dest_reg, dest_mask, op0, op1, op2) \
+do { \
+ if (dest_mask) { \
+ i915_fs_arith_masked (MAD, dest_reg, dest_mask, op0, op1, op2); \
+ } else { \
+ i915_fs_arith (MAD, dest_reg, op0, op1, op2); \
+ } \
+} while (0)
+
+#define i915_fs_dp2add(dest_reg, dest_mask, op0, op1, op2) \
+do { \
+ if (dest_mask) { \
+ i915_fs_arith_masked (DP2ADD, dest_reg, dest_mask, op0, op1, op2); \
+ } else { \
+ i915_fs_arith (DP2ADD, dest_reg, op0, op1, op2); \
+ } \
+} while (0)
+
+/*
+ * Perform a 3-component dot-product of operand0 and operand1 and put the
+ * resulting scalar in the channels of dest_reg specified by the dest_mask.
+ */
+#define i915_fs_dp3(dest_reg, dest_mask, op0, op1) \
+do { \
+ if (dest_mask) { \
+ i915_fs_arith_masked (DP3, dest_reg, dest_mask, \
+ op0, op1,\
+ i915_fs_operand_none()); \
+ } else { \
+ i915_fs_arith (DP3, dest_reg, op0, op1,\
+ i915_fs_operand_none()); \
+ } \
+} while (0)
+
+static inline uint32_t cairo_const
+i915_fs_operand_pure_alpha (int pure)
+{
+ if (pure & (1 << 3))
+ return i915_fs_operand_one ();
+ else
+ return i915_fs_operand_zero ();
+}
+
+#define I915_TILING_DEFAULT I915_TILING_Y
+#define I915_BO_CACHE_BUCKETS 13 /* cache surfaces up to 16 MiB */
+
+typedef struct i915_surface i915_surface_t;
+typedef struct i915_device i915_device_t;
+typedef struct i915_shader i915_shader_t;
+
+typedef void (*i915_add_rectangle_func_t) (const i915_shader_t *shader,
+ int x, int y,
+ int w, int h);
+
+#define IMAGE_CACHE_WIDTH 1024
+#define IMAGE_CACHE_HEIGHT 1024
+
+typedef struct i915_image_private {
+ cairo_rtree_node_t node;
+ intel_buffer_cache_t *container;
+} i915_image_private_t;
+
+#define I915_BATCH_SIZE (128*1024)
+#define I915_VBO_SIZE (512*1024)
+#define I915_MAX_RELOCS 2048
+
+enum {
+ I915_DEBUG_EXEC = 0x1,
+ I915_DEBUG_SYNC = 0x2,
+ I915_DEBUG_BATCH = 0x4,
+ I915_DEBUG_BUFFER = 0x8,
+ I915_DEBUG_BUFFER_CACHE = 0x10,
+ I915_DEBUG_BUFFER_ALLOC = 0x20,
+ I915_DEBUG_GLYPHS = 0x40,
+ I915_DEBUG_MAP = 0x80,
+ I915_DEBUG_THROTTLE = 0x100,
+};
+
+struct i915_device {
+ intel_device_t intel;
+
+ cairo_bool_t debug;
+
+ i915_shader_t *shader; /* note: only valid during geometry emission */
+
+ struct i915_batch {
+ intel_bo_t *target_bo[I915_MAX_RELOCS];
+ size_t gtt_avail_size;
+ size_t est_gtt_size;
+ size_t total_gtt_size;
+
+ uint16_t fences;
+ uint16_t fences_avail;
+ uint16_t reloc_count;
+ uint16_t exec_count;
+ uint16_t used;
+
+ struct drm_i915_gem_exec_object2 exec[I915_MAX_RELOCS];
+ struct drm_i915_gem_relocation_entry reloc[I915_MAX_RELOCS];
+ } batch;
+
+ uint32_t vbo;
+ uint32_t vbo_offset;
+ uint32_t vbo_used;
+ uint32_t vbo_max_index;
+ uint32_t vertex_index;
+ uint32_t vertex_count;
+ uint32_t floats_per_vertex;
+ uint32_t rectangle_size;
+ intel_bo_t *last_vbo;
+ uint32_t last_vbo_offset;
+ uint32_t last_vbo_space;
+
+ i915_surface_t *current_target;
+ uint32_t current_size;
+ uint32_t current_diffuse;
+ uint32_t current_colorbuf;
+ uint32_t *current_source;
+ uint32_t *current_mask;
+ uint32_t *current_clip;
+ uint32_t current_program;
+ uint32_t current_texcoords;
+ uint32_t current_blend;
+ uint32_t current_constants[8*4];
+ uint32_t current_n_constants;
+ uint32_t current_samplers[2*4];
+ uint32_t current_maps[4*4];
+ uint32_t current_n_samplers;
+ uint32_t current_n_maps;
+ uint32_t last_source_fragment;
+ uint32_t clear_alpha;
+
+ cairo_list_t image_caches[2];
+
+ uint32_t batch_header[13];
+ uint32_t batch_base[I915_BATCH_SIZE / sizeof (uint32_t)];
+ uint8_t vbo_base[I915_VBO_SIZE];
+};
+
+enum {
+ CURRENT_SOURCE = 0x1,
+ CURRENT_MASK = 0x2,
+ CURRENT_CLIP = 0x4
+};
+
+typedef enum {
+ VS_ZERO,
+ VS_CONSTANT,
+ VS_LINEAR,
+ VS_TEXTURE,
+ VS_TEXTURE_16,
+} i915_vertex_shader_t;
+
+typedef enum {
+ FS_ZERO,
+ FS_ONE,
+ FS_PURE,
+ FS_CONSTANT,
+ FS_DIFFUSE,
+ FS_LINEAR,
+ FS_RADIAL,
+ FS_TEXTURE,
+ FS_YUV,
+ FS_SPANS,
+} i915_fragment_shader_t;
+
+#define FS_DETAILS_SHIFT 4
+
+typedef enum {
+ PATTERN_BASE,
+ PATTERN_CONSTANT,
+ PATTERN_LINEAR,
+ PATTERN_RADIAL,
+ PATTERN_TEXTURE,
+} i915_shader_channel_t;
+
+struct i915_surface {
+ intel_surface_t intel;
+
+ uint32_t map0, map1;
+ uint32_t colorbuf;
+
+ cairo_bool_t deferred_clear;
+ uint32_t offset;
+ uint32_t is_current_texture;
+
+ i915_image_private_t *cache;
+
+ intel_bo_t *stencil;
+ uint32_t stencil_stride;
+ uint32_t stencil_offset;
+};
+
+typedef enum {
+ NONE = 0,
+ YUV_I420,
+ /* XXX */
+ YUV_YV12,
+ YUV_YUY2,
+ YUV_UYVY,
+} i915_packed_pixel_t;
+
+/* read-only container */
+#define I915_PACKED_PIXEL_SURFACE_TYPE 0x1000
+typedef struct i915_packed_pixel_surface {
+ cairo_surface_t base;
+
+ i915_packed_pixel_t pixel;
+
+ i915_device_t *device;
+ intel_bo_t *bo;
+ uint32_t is_current_texture;
+
+ uint32_t offset[4];
+ uint32_t stride[4];
+ uint32_t width[4];
+ uint32_t height[4];
+ uint32_t map0[4], map1[4];
+} i915_packed_pixel_surface_t;
+
+struct i915_shader {
+ i915_device_t *device;
+ i915_surface_t *target;
+
+ cairo_operator_t op;
+ uint32_t blend;
+ float opacity;
+ cairo_content_t content;
+
+ cairo_bool_t committed;
+ cairo_bool_t need_combine;
+
+ i915_add_rectangle_func_t add_rectangle;
+
+ union i915_shader_channel {
+ struct {
+ i915_vertex_shader_t vertex;
+ i915_fragment_shader_t fragment;
+ i915_shader_channel_t pattern;
+ } type;
+ struct i915_shader_base {
+ i915_vertex_shader_t vertex;
+ i915_fragment_shader_t fragment;
+ i915_shader_channel_t pattern;
+ uint32_t texfmt;
+ cairo_content_t content;
+ uint32_t mode;
+ intel_bo_t *bo;
+ uint32_t n_samplers;
+ uint32_t offset[4];
+ uint32_t map[2*4];
+ uint32_t sampler[2];
+ cairo_matrix_t matrix;
+ } base;
+ struct i915_shader_solid {
+ struct i915_shader_base base;
+ cairo_color_t color;
+ int pure;
+ } solid;
+ struct i915_shader_linear {
+ struct i915_shader_base base;
+ struct {
+ float red, green, blue, alpha;
+ } color0, color1;
+ float dx, dy, offset;
+ } linear;
+ struct i915_shader_radial {
+ struct i915_shader_base base;
+ float constants[8];
+ } radial;
+ struct i915_shader_surface {
+ struct i915_shader_base base;
+ i915_packed_pixel_t pixel;
+ } surface;
+ } source, mask, clip, dst;
+
+ jmp_buf unwind;
+};
+
+enum i915_shader_linear_mode {
+ /* XXX REFLECT */
+ LINEAR_TEXTURE,
+ LINEAR_NONE,
+ LINEAR_REPEAT,
+ LINEAR_PAD,
+};
+
+enum i915_shader_radial_mode {
+ RADIAL_ONE,
+ RADIAL_TWO
+};
+
+typedef cairo_status_t
+(*i915_spans_func_t) (void *closure,
+ cairo_span_renderer_t *renderer,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+i915_clip_and_composite_spans (i915_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_antialias_t antialias,
+ i915_spans_func_t draw_func,
+ void *draw_closure,
+ const cairo_composite_rectangles_t*extents,
+ cairo_clip_t *clip,
+ double opacity);
+
+cairo_private cairo_surface_t *
+i915_surface_create_internal (cairo_drm_device_t *base_dev,
+ cairo_format_t format,
+ int width, int height,
+ uint32_t tiling,
+ cairo_bool_t gpu_target);
+
+cairo_private i915_surface_t *
+i915_surface_create_from_cacheable_image_internal (i915_device_t *device,
+ cairo_image_surface_t *image);
+
+cairo_private void
+i915_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_int_status_t
+i915_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining);
+
+static inline int cairo_const
+i915_tiling_height (uint32_t tiling, int height)
+{
+ switch (tiling) {
+ default:
+ case I915_TILING_NONE: return (height + 1) & -2;
+ case I915_TILING_X: return (height + 7) & -8;
+ case I915_TILING_Y: return (height + 31) & -32;
+ }
+}
+
+static inline uint32_t cairo_const
+i915_tiling_stride (int format, uint32_t stride)
+{
+ uint32_t tile_width;
+
+ /* use 64B alignment so that the buffer may be used as a scanout */
+ if (format == I915_TILING_NONE)
+ return (stride + 63) & -64;
+
+ tile_width = 512;
+ /* XXX Currently the kernel enforces a tile_width of 512 for TILING_Y.
+
+ <jbarnes> the docs are a bit confused on that front
+ <jbarnes> once we enable it on 915 we'll find out what the tile width size should be in the fence setup
+ <jbarnes> it could be that 915 has y tiling but that the minimum width is 512 or something
+ <jbarnes> yeah it's probably 128 on 915 also
+ <jbarnes> it's just that we haven't tested
+ <jbarnes> but I wasn't thinking that the tile widths were the same
+ <jbarnes> only that in order to fence y tiles on 915 you needed pitch to be a multiple of 4 y tiles (or something like that)
+
+ tile_width = format == I915_TILING_Y ? 128 : 512;
+ */
+
+ /* needs a pot tile width */
+ while (tile_width < stride)
+ tile_width <<= 1;
+
+ return tile_width;
+}
+
+static inline uint32_t cairo_const
+i915_tiling_size (uint32_t tiling, uint32_t size)
+{
+ uint32_t fence;
+
+ if (tiling == I915_TILING_NONE)
+ return (size + 4095) & -4096;
+
+ fence = 1024 * 1024; /* 1 MiB */
+ while (fence < size)
+ fence <<= 1;
+
+ return fence;
+}
+
+static inline cairo_bool_t cairo_const
+i915_texture_filter_is_nearest (cairo_filter_t filter)
+{
+ switch (filter) {
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return FALSE;
+ default:
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ return TRUE;
+ }
+}
+
+static inline uint32_t cairo_const
+i915_texture_filter (cairo_filter_t filter)
+{
+ switch (filter) {
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return
+ (FILTER_LINEAR << SS2_MAG_FILTER_SHIFT) |
+ (FILTER_LINEAR << SS2_MIN_FILTER_SHIFT);
+ default:
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ return
+ (FILTER_NEAREST << SS2_MAG_FILTER_SHIFT) |
+ (FILTER_NEAREST << SS2_MIN_FILTER_SHIFT);
+ }
+}
+
+static inline uint32_t cairo_const
+i915_texture_extend (cairo_extend_t extend)
+{
+ switch (extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ return
+ (TEXCOORDMODE_CLAMP_BORDER << SS3_TCX_ADDR_MODE_SHIFT) |
+ (TEXCOORDMODE_CLAMP_BORDER << SS3_TCY_ADDR_MODE_SHIFT);
+ case CAIRO_EXTEND_REPEAT:
+ return
+ (TEXCOORDMODE_WRAP << SS3_TCX_ADDR_MODE_SHIFT) |
+ (TEXCOORDMODE_WRAP << SS3_TCY_ADDR_MODE_SHIFT);
+ case CAIRO_EXTEND_PAD:
+ return
+ (TEXCOORDMODE_CLAMP_EDGE << SS3_TCX_ADDR_MODE_SHIFT) |
+ (TEXCOORDMODE_CLAMP_EDGE << SS3_TCY_ADDR_MODE_SHIFT);
+ case CAIRO_EXTEND_REFLECT:
+ return
+ (TEXCOORDMODE_MIRROR << SS3_TCX_ADDR_MODE_SHIFT) |
+ (TEXCOORDMODE_MIRROR << SS3_TCY_ADDR_MODE_SHIFT);
+ }
+}
+
+static inline uint32_t cairo_const
+BUF_tiling (uint32_t tiling)
+{
+ switch (tiling) {
+ default:
+ case I915_TILING_NONE: return 0;
+ case I915_TILING_X: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_X;
+ case I915_TILING_Y: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_Y;
+ }
+}
+
+#define OUT_DWORD(dword) i915_batch_emit_dword (device, dword)
+#define OUT_RELOC(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, FALSE)
+#define OUT_RELOC_FENCED(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, TRUE)
+
+#define FS_LOCALS \
+ uint32_t *_shader_start
+
+#define FS_BEGIN() \
+do { \
+ _shader_start = BATCH_PTR (device); \
+ OUT_DWORD (_3DSTATE_PIXEL_SHADER_PROGRAM); \
+} while (0)
+
+#define FS_END() \
+do { \
+ *_shader_start |= BATCH_PTR (device) - _shader_start - 2; \
+} while (0);
+
+static inline int32_t
+i915_batch_space (i915_device_t *device)
+{
+ /* leave room for RECTLIST(4) + MI_BUFFER_END + MI_NOOP */
+ return sizeof (device->batch_base) - (device->batch.used << 2) - 32;
+}
+
+static inline cairo_bool_t
+i915_check_aperture_size (const i915_device_t *device, int relocs, size_t est_size, size_t size)
+{
+ return device->batch.reloc_count + relocs < I915_MAX_RELOCS - 2 &&
+ device->batch.est_gtt_size + est_size <= device->batch.gtt_avail_size &&
+ device->batch.total_gtt_size + size <= device->intel.gtt_avail_size;
+}
+
+static inline cairo_bool_t
+i915_check_aperture (const i915_device_t *device, intel_bo_t **bo_array, int count)
+{
+ uint32_t relocs = 0, est_size = 0, size = 0;
+
+ while (count--) {
+ const intel_bo_t *bo = *bo_array++;
+ if (bo->exec == NULL) {
+ relocs++;
+ size += bo->base.size;
+ if (!bo->busy)
+ est_size += bo->base.size;
+ }
+ }
+
+ return i915_check_aperture_size (device, relocs, est_size, size);
+}
+
+static inline cairo_bool_t
+i915_check_aperture_and_fences (const i915_device_t *device, intel_bo_t **bo_array, int count)
+{
+ uint32_t relocs = 0, est_size = 0, size = 0;
+ uint32_t fences = 0;
+
+ while (count--) {
+ const intel_bo_t *bo = *bo_array++;
+ if (bo->exec == NULL) {
+ relocs++;
+ size += bo->base.size;
+ if (!bo->busy)
+ est_size += bo->base.size;
+ if (bo->tiling != I915_TILING_NONE)
+ fences++;
+ } else if (bo->tiling != I915_TILING_NONE) {
+ if ((bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0)
+ fences++;
+ }
+ }
+
+ return i915_check_aperture_size (device, relocs, est_size, size) &&
+ device->batch.fences + fences <= device->batch.fences_avail;
+}
+
+#define BATCH_PTR(device) &(device)->batch_base[(device)->batch.used]
+static inline void
+i915_batch_emit_dword (i915_device_t *device, uint32_t dword)
+{
+ device->batch_base[device->batch.used++] = dword;
+}
+
+cairo_private void
+i915_batch_add_reloc (i915_device_t *device, uint32_t pos,
+ intel_bo_t *bo,
+ uint32_t offset,
+ uint32_t read_domains,
+ uint32_t write_domain,
+ cairo_bool_t needs_fence);
+
+static inline void
+i915_batch_fill_reloc (i915_device_t *device, uint32_t pos,
+ intel_bo_t *bo,
+ uint32_t offset,
+ uint32_t read_domains,
+ uint32_t write_domain)
+{
+ i915_batch_add_reloc (device, pos,
+ bo, offset,
+ read_domains, write_domain,
+ FALSE);
+ device->batch_base[pos] = bo->offset + offset;
+}
+
+static inline void
+i915_batch_emit_reloc (i915_device_t *device,
+ intel_bo_t *bo,
+ uint32_t offset,
+ uint32_t read_domains,
+ uint32_t write_domain,
+ cairo_bool_t needs_fence)
+{
+ i915_batch_add_reloc (device, device->batch.used,
+ bo, offset,
+ read_domains, write_domain,
+ needs_fence);
+ i915_batch_emit_dword (device, bo->offset + offset);
+}
+
+cairo_private void
+i915_vbo_flush (i915_device_t *device);
+
+cairo_private void
+i915_vbo_finish (i915_device_t *device);
+
+cairo_private cairo_status_t
+i915_batch_flush (i915_device_t *device);
+
+static inline float *
+i915_add_rectangle (i915_device_t *device)
+{
+ float *vertices;
+ uint32_t size;
+
+ assert (device->floats_per_vertex);
+ assert (device->rectangle_size == 3*device->floats_per_vertex*sizeof(float));
+
+ size = device->rectangle_size;
+ if (unlikely (device->vbo_offset + size > I915_VBO_SIZE))
+ i915_vbo_finish (device);
+
+ vertices = (float *) (device->vbo_base + device->vbo_offset);
+ device->vbo_used = device->vbo_offset += size;
+ device->vertex_count += 3;
+ return vertices;
+}
+
+static inline i915_device_t *
+i915_device (i915_surface_t *surface)
+{
+ return (i915_device_t *) surface->intel.drm.base.device;
+}
+
+cairo_private cairo_status_t
+i915_surface_clear (i915_surface_t *dst);
+
+cairo_private void
+i915_set_dst (i915_device_t *device, i915_surface_t *dst);
+
+cairo_private void
+i915_shader_init (i915_shader_t *shader,
+ i915_surface_t *dst,
+ cairo_operator_t op,
+ double opacity);
+
+cairo_private cairo_status_t
+i915_shader_acquire_pattern (i915_shader_t *shader,
+ union i915_shader_channel *src,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private void
+i915_shader_set_clip (i915_shader_t *shader,
+ cairo_clip_t *clip);
+
+cairo_private int
+i915_shader_num_texcoords (const i915_shader_t *shader);
+
+static inline double cairo_const
+i915_shader_linear_texcoord (const struct i915_shader_linear *l,
+ double src_x, double src_y)
+{
+ return l->dx * src_x + l->dy * src_y + l->offset;
+}
+
+cairo_private cairo_status_t
+i915_shader_commit (i915_shader_t *shader,
+ i915_device_t *device);
+
+cairo_private void
+i915_shader_fini (i915_shader_t *shader);
+
+cairo_private cairo_status_t
+i915_fixup_unbounded (i915_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip);
+
+static inline cairo_bool_t
+i915_surface_needs_tiling (i915_surface_t *dst)
+{
+ return dst->intel.drm.width > 2048 || dst->intel.drm.height > 2048;
+}
+
+cairo_private cairo_status_t
+i915_surface_copy_subimage (i915_device_t *device,
+ i915_surface_t *src,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t flush,
+ i915_surface_t **clone_out);
+
+static inline uint32_t
+pack_float (float f)
+{
+ union {
+ float f;
+ uint32_t ui;
+ } t;
+ t.f = f;
+ return t.ui;
+}
+
+static inline cairo_status_t
+i915_surface_fallback_flush (i915_surface_t *surface)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->intel.drm.fallback != NULL))
+ return intel_surface_flush (&surface->intel, 0);
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (unlikely (surface->deferred_clear))
+ status = i915_surface_clear (surface);
+
+ return status;
+}
+
+#endif /* CAIRO_DRM_I915_PRIVATE_H */
diff --git a/src/drm/cairo-drm-i915-shader.c b/src/drm/cairo-drm-i915-shader.c
new file mode 100755
index 000000000..a1911d0a4
--- /dev/null
+++ b/src/drm/cairo-drm-i915-shader.c
@@ -0,0 +1,2893 @@
+/* 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-error-private.h"
+#include "cairo-drm-i915-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-private.h"
+
+#if 0
+static cairo_status_t
+i915_packed_pixel_surface_finish (void *abstract_surface)
+{
+ i915_packed_pixel_surface_t *surface = abstract_surface;
+ i915_device_t *device;
+
+ device = i915_device_acquire (&surface->device->intel.base);
+
+ intel_bo_destroy (&device->intel, surface->bo);
+
+ if (surface->is_current_texture) {
+ if (surface->is_current_texture & CURRENT_SOURCE)
+ device->current_source = NULL;
+ if (surface->is_current_texture & CURRENT_MASK)
+ device->current_mask = NULL;
+ device->current_n_samplers = 0;
+ }
+
+ i915_device_release (device);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t i915_packed_pixel_surface_backend = {
+ I915_PACKED_PIXEL_SURFACE_TYPE,
+ i915_packed_pixel_surface_finish,
+};
+
+static cairo_surface_t *
+i915_packed_pixel_surface_create (i915_device_t *device,
+ i915_packed_pixel_t pixel,
+ const uint8_t *data,
+ uint32_t length,
+ uint32_t width, uint32_t height)
+{
+ i915_packed_pixel_surface_t *surface;
+ cairo_content_t content;
+ uint32_t tiling, size;
+ uint32_t stride, half_stride;
+ uint32_t i;
+
+ if (width > 2048 || height > 2048)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ surface = malloc (sizeof (i915_packed_pixel_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ tiling = I915_TILING_NONE; /* XXX */
+ half_stride = stride = i915_tiling_stride (tiling, width/2);
+ if (stride < width)
+ stride *= 2 ;
+ height = i915_tiling_height (tiling, height);
+
+ switch (surface->pixel = pixel) {
+ case YUV_I420:
+ content = CAIRO_CONTENT_COLOR;
+
+ surface->offset[0] = 0;
+ surface->width[0] = width;
+ surface->height[0] = height;
+ surface->stride[0] = stride;
+ surface->map0[0] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling);
+ surface->map0[0] |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+ ((width - 1) << MS3_WIDTH_SHIFT);
+ surface->map1[0] = (stride / 4 - 1) << MS4_PITCH_SHIFT;
+
+ surface->offset[1] = stride * height;
+ surface->width[1] = width / 2;
+ surface->height[1] = height / 2;
+ surface->stride[1] = half_stride;
+ surface->map0[1] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling);
+ surface->map0[1] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) |
+ ((width/2 - 1) << MS3_WIDTH_SHIFT);
+ surface->map1[1] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT;
+
+ if (width < half_stride) {
+ surface->offset[2] = stride * height + half_stride / 2;
+ size = stride * height + half_stride * height / 2;
+ } else {
+ surface->offset[2] = stride * height + half_stride * height / 2;
+ size = stride * height + half_stride * height;
+ }
+ surface->width[2] = width / 2;
+ surface->height[2] = height / 2;
+ surface->stride[2] = half_stride;
+ surface->map0[2] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling);
+ surface->map0[2] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) |
+ ((width/2 - 1) << MS3_WIDTH_SHIFT);
+ surface->map1[2] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT;
+ break;
+
+ case NONE:
+ case YUV_YV12:
+ case YUV_YUY2:
+ case YUV_UYVY:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+
+ _cairo_surface_init (&surface->base,
+ &i915_packed_pixel_surface_backend,
+ content);
+
+ surface->bo = intel_bo_create (&device->intel, size, FALSE);
+ assert (surface->bo->tiling == I915_TILING_NONE);
+ if (unlikely (surface->bo == NULL)) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ if (tiling == I915_TILING_NONE) {
+ intel_bo_t *bo = surface->bo;
+ uint32_t dst;
+ int uv;
+
+ dst = surface->offset[0];
+ if (width == stride) {
+ size = stride * height;
+ intel_bo_write (&device->intel, bo, dst, size, data);
+ data += size;
+ } else {
+ for (i = 0; i < height; i++) {
+ intel_bo_write (&device->intel, bo, dst, width, data);
+ dst += stride;
+ data += width;
+ }
+ }
+
+ for (uv = 1; uv <= 2; uv++) {
+ dst = surface->offset[uv];
+ if (width / 2 == half_stride) {
+ size = half_stride * height / 2;
+ intel_bo_write (&device->intel, bo, dst, size, data);
+ data += size;
+ } else {
+ size = width / 2;
+ for (i = 0; i < height / 2; i++) {
+ intel_bo_write (&device->intel, bo, dst, size, data);
+ dst += half_stride;
+ data += size;
+ }
+ }
+ }
+ } else {
+ uint8_t *dst, *base;
+
+ base = intel_bo_map (&device->intel, surface->bo);
+
+ dst = base + surface->offset[0];
+ if (width == stride) {
+ size = stride * height;
+ memcpy (dst, data, size);
+ data += size;
+ } else {
+ for (i = 0; i < height; i++) {
+ memcpy (dst, data, width);
+ dst += stride;
+ data += width;
+ }
+ }
+
+ dst = base + surface->offset[1];
+ if (width / 2 == half_stride) {
+ size = half_stride * height / 2;
+ memcpy (dst, data, size);
+ data += size;
+ } else {
+ size = width / 2;
+ for (i = 0; i < height / 2; i++) {
+ memcpy (dst, data, size);
+ dst += half_stride;
+ data += size;
+ }
+ }
+
+ dst = base + surface->offset[2];
+ if (width / 2 == half_stride) {
+ size = half_stride * height / 2;
+ memcpy (dst, data, size);
+ data += size;
+ } else {
+ size = width / 2;
+ for (i = 0; i < height / 2; i++) {
+ memcpy (dst, data, size);
+ dst += half_stride;
+ data += size;
+ }
+ }
+ }
+
+ surface->device = device;
+ surface->is_current_texture = 0;
+
+ return &surface->base;
+}
+
+static cairo_int_status_t
+i915_clone_yuv (i915_surface_t *surface,
+ cairo_surface_t *source,
+ int width, int height,
+ cairo_surface_t **clone_out)
+{
+ const uint8_t *mime_data = NULL;
+ unsigned int mime_data_length;
+ cairo_surface_t *clone;
+
+ cairo_surface_get_mime_data (source, "video/x-raw-yuv/i420",
+ &mime_data, &mime_data_length);
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ clone =
+ i915_packed_pixel_surface_create ((i915_device_t *) surface->base.device,
+ YUV_I420,
+ mime_data, mime_data_length,
+ width, height);
+ if (clone == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (unlikely (clone->status))
+ return clone->status;
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+/* Max instruction count: 4 */
+static void
+i915_shader_linear_color (i915_device_t *device,
+ enum i915_shader_linear_mode mode,
+ int in, int c0, int c1, int out)
+{
+ int tmp = FS_U0;
+
+ switch (mode) {
+ case LINEAR_TEXTURE:
+ ASSERT_NOT_REACHED;
+ case LINEAR_NONE:
+ tmp = in;
+ break;
+
+ case LINEAR_REPEAT:
+ i915_fs_frc (tmp, i915_fs_operand (in, X, X, X, X));
+ break;
+#if 0
+ case LINEAR_REFLECT:
+ /* XXX needs an extra constant: C2 [0.5, 2.0, x, x] */
+ i915_fs_mul (tmp, in, 0.5);
+ i915_fs_frc (tmp, i915_fs_operand_reg (tmp));
+ i915_fs_mul (tmp, tmp, 2.0);
+ i915_fs_add (tmp, i915_fs_operand_one (),
+ i915_fs_operand_reg_negate (tmp));
+ i915_fs_cmp (tmp,
+ i915_fs_operand_reg (tmp),
+ i915_fs_operand_reg (tmp),
+ i915_fs_operand_reg_negate (tmp));
+ i915_fs_add (tmp, i915_fs_operand_one (),
+ i915_fs_operand_reg_negate (tmp));
+#endif
+ case LINEAR_PAD:
+ i915_fs_max (tmp,
+ i915_fs_operand_zero (),
+ i915_fs_operand (in, X, X, X, X));
+ i915_fs_min (tmp,
+ i915_fs_operand_one (),
+ i915_fs_operand_reg (tmp));
+ break;
+ }
+
+ /* interpolate */
+ i915_fs_mad (out, 0,
+ i915_fs_operand (tmp, NEG_X, NEG_X, NEG_X, NEG_X),
+ i915_fs_operand_reg (c0),
+ i915_fs_operand_reg (c0));
+ i915_fs_mad (out, 0,
+ i915_fs_operand (tmp, X, X, X, X),
+ i915_fs_operand_reg (c1),
+ i915_fs_operand_reg (out));
+}
+
+static void
+i915_shader_radial_init (struct i915_shader_radial *r,
+ const cairo_radial_pattern_t *radial)
+{
+ double dx, dy, dr, r1;
+
+ dx = radial->cd2.center.x - radial->cd1.center.x;
+ dy = radial->cd2.center.y - radial->cd1.center.y;
+ dr = radial->cd2.radius - radial->cd1.radius;
+
+ r1 = radial->cd1.radius;
+
+ if (radial->cd2.center.x == radial->cd1.center.x &&
+ radial->cd2.center.y == radial->cd1.center.y)
+ {
+ /* XXX dr == 0, meaningless with anything other than PAD */
+ r->constants[0] = radial->cd1.center.x / dr;
+ r->constants[1] = radial->cd1.center.y / dr;
+ r->constants[2] = 1. / dr;
+ r->constants[3] = -r1 / dr;
+
+ r->constants[4] = 0;
+ r->constants[5] = 0;
+ r->constants[6] = 0;
+ r->constants[7] = 0;
+
+ r->base.mode = RADIAL_ONE;
+ } else {
+ r->constants[0] = -radial->cd1.center.x;
+ r->constants[1] = -radial->cd1.center.y;
+ r->constants[2] = r1;
+ r->constants[3] = -4 * (dx*dx + dy*dy - dr*dr);
+
+ r->constants[4] = -2 * dx;
+ r->constants[5] = -2 * dy;
+ r->constants[6] = -2 * r1 * dr;
+ r->constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr));
+
+ r->base.mode = RADIAL_TWO;
+ }
+
+ r->base.matrix = radial->base.base.matrix;
+}
+
+/* Max instruction count: 10 */
+static void
+i915_shader_radial_coord (i915_device_t *device,
+ enum i915_shader_radial_mode mode,
+ int in, int g0, int g1, int out)
+{
+ switch (mode) {
+ case RADIAL_ONE:
+ /*
+ pdx = (x - c1x) / dr, pdy = (y - c1y) / dr;
+ r² = pdx*pdx + pdy*pdy
+ t = r²/sqrt(r²) - r1/dr;
+ */
+ i915_fs_mad (FS_U0, MASK_X | MASK_Y,
+ i915_fs_operand (in, X, Y, ZERO, ZERO),
+ i915_fs_operand (g0, Z, Z, ZERO, ZERO),
+ i915_fs_operand (g0, NEG_X, NEG_Y, ZERO, ZERO));
+ i915_fs_dp2add (FS_U0, MASK_X,
+ i915_fs_operand (FS_U0, X, Y, ZERO, ZERO),
+ i915_fs_operand (FS_U0, X, Y, ZERO, ZERO),
+ i915_fs_operand_zero ());
+ i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U0, X, X, X, X));
+ i915_fs_mad (out, MASK_X,
+ i915_fs_operand (FS_U0, X, ZERO, ZERO, ZERO),
+ i915_fs_operand (out, X, ZERO, ZERO, ZERO),
+ i915_fs_operand (g0, W, ZERO, ZERO, ZERO));
+ break;
+
+ case RADIAL_TWO:
+ /*
+ pdx = x - c1x, pdy = y - c1y;
+ A = dx² + dy² - dr²
+ B = -2*(pdx*dx + pdy*dy + r1*dr);
+ C = pdx² + pdy² - r1²;
+ det = B*B - 4*A*C;
+ t = (-B + sqrt (det)) / (2 * A)
+ */
+
+ /* u0.x = pdx, u0.y = pdy, u[0].z = r1; */
+ i915_fs_add (FS_U0,
+ i915_fs_operand (in, X, Y, ZERO, ZERO),
+ i915_fs_operand (g0, X, Y, Z, ZERO));
+ /* u0.x = pdx, u0.y = pdy, u[0].z = r1, u[0].w = B; */
+ i915_fs_dp3 (FS_U0, MASK_W,
+ i915_fs_operand (FS_U0, X, Y, ONE, ZERO),
+ i915_fs_operand (g1, X, Y, Z, ZERO));
+ /* u1.x = pdx² + pdy² - r1²; [C] */
+ i915_fs_dp3 (FS_U1, MASK_X,
+ i915_fs_operand (FS_U0, X, Y, Z, ZERO),
+ i915_fs_operand (FS_U0, X, Y, NEG_Z, ZERO));
+ /* u1.x = C, u1.y = B, u1.z=-4*A; */
+ i915_fs_mov_masked (FS_U1, MASK_Y, i915_fs_operand (FS_U0, W, W, W, W));
+ i915_fs_mov_masked (FS_U1, MASK_Z, i915_fs_operand (g0, W, W, W, W));
+ /* u1.x = B² - 4*A*C */
+ i915_fs_dp2add (FS_U1, MASK_X,
+ i915_fs_operand (FS_U1, X, Y, ZERO, ZERO),
+ i915_fs_operand (FS_U1, Z, Y, ZERO, ZERO),
+ i915_fs_operand_zero ());
+ /* out.x = -B + sqrt (B² - 4*A*C),
+ * out.y = -B - sqrt (B² - 4*A*C),
+ */
+ i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U1, X, X, X, X));
+ i915_fs_mad (out, MASK_X | MASK_Y,
+ i915_fs_operand (out, X, X, ZERO, ZERO),
+ i915_fs_operand (FS_U1, X, NEG_X, ZERO, ZERO),
+ i915_fs_operand (FS_U0, NEG_W, NEG_W, ZERO, ZERO));
+ /* out.x = (-B + sqrt (B² - 4*A*C)) / (2 * A),
+ * out.y = (-B - sqrt (B² - 4*A*C)) / (2 * A)
+ */
+ i915_fs_mul (out,
+ i915_fs_operand (out, X, Y, ZERO, ZERO),
+ i915_fs_operand (g1, W, W, ZERO, ZERO));
+ /* if (A > 0)
+ * out = (-B + sqrt (B² - 4*A*C)) / (2 * A),
+ * else
+ * out = (-B - sqrt (B² - 4*A*C)) / (2 * A)
+ */
+ i915_fs_cmp (out,
+ i915_fs_operand (g1, W, ZERO, ZERO, ZERO),
+ i915_fs_operand (out, X, ZERO, ZERO, ZERO),
+ i915_fs_operand (out, Y, ZERO, ZERO, ZERO));
+ break;
+ }
+}
+
+/* Max instruction count: 7 */
+static inline void
+i915_shader_yuv_color (i915_device_t *device,
+ int y, int u, int v,
+ int c0, int c1, int c2,
+ int out)
+{
+ i915_fs_mov_masked (FS_U0, MASK_X, i915_fs_operand_reg (y));
+ i915_fs_mov_masked (FS_U0, MASK_Y, i915_fs_operand_reg (u));
+ i915_fs_mov_masked (FS_U0, MASK_Z, i915_fs_operand_reg (v));
+
+ i915_fs_add (FS_U0,
+ i915_fs_operand_reg (FS_U0),
+ i915_fs_operand_reg (c0));
+ i915_fs_dp3 (out, MASK_X,
+ i915_fs_operand_reg (FS_U0),
+ i915_fs_operand (c1, X, ZERO, Y, ZERO));
+ i915_fs_dp3 (out, MASK_Z,
+ i915_fs_operand_reg (FS_U0),
+ i915_fs_operand (c1, Z, W, ZERO, ZERO));
+ i915_fs_dp3 (out, MASK_Y,
+ i915_fs_operand_reg (FS_U0),
+ i915_fs_operand_reg (c2));
+}
+
+static inline uint32_t
+i915_shader_channel_key (const union i915_shader_channel *channel)
+{
+ return (channel->type.fragment & 0x0f) | (channel->base.mode << FS_DETAILS_SHIFT);
+}
+
+static uint32_t
+i915_shader_channel_get_num_tex_coords (const union i915_shader_channel *channel)
+{
+ switch (channel->type.fragment) {
+ default:
+ case FS_ZERO:
+ case FS_ONE:
+ case FS_CONSTANT:
+ case FS_PURE:
+ case FS_DIFFUSE:
+ return 0;
+
+ case FS_LINEAR:
+ case FS_RADIAL:
+ case FS_TEXTURE:
+ case FS_SPANS:
+ case FS_YUV:
+ return 1;
+ }
+}
+
+static uint32_t
+i915_shader_get_num_tex_coords (const i915_shader_t *shader)
+{
+ uint32_t num_tex_coords;
+
+ num_tex_coords = 0;
+
+ num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->source);
+ num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->mask);
+ num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->clip);
+ num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->dst);
+
+ return num_tex_coords;
+}
+
+#define i915_fs_operand_impure(reg, channel, pure) \
+ (reg | \
+ (((pure & (1 << 0)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \
+ (((pure & (1 << 1)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \
+ (((pure & (1 << 2)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \
+ (((pure & (1 << 3)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT))
+
+#define i915_fs_operand_pure(pure) \
+ (FS_R0 | \
+ (((pure & (1 << 0)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \
+ (((pure & (1 << 1)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \
+ (((pure & (1 << 2)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \
+ (((pure & (1 << 3)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT))
+
+static void
+i915_set_shader_program (i915_device_t *device,
+ const i915_shader_t *shader)
+{
+ uint32_t num_tex_coords;
+ uint32_t num_samplers;
+ uint32_t n;
+ uint32_t texture_offset = 0;
+ uint32_t constant_offset = 0;
+ uint32_t sampler_offset = 0;
+ uint32_t source_reg;
+ uint32_t source_pure;
+ uint32_t mask_reg;
+ uint32_t out_reg;
+ uint32_t dest_reg;
+ FS_LOCALS;
+
+ n = (i915_shader_channel_key (&shader->source) << 0) |
+ (i915_shader_channel_key (&shader->mask) << 8) |
+ (i915_shader_channel_key (&shader->clip) << 16) |
+ (shader->op << 24) |
+ ((shader->opacity < 1.) << 30) |
+ (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31);
+ if (n == device->current_program)
+ return;
+ device->current_program = n;
+
+ FS_BEGIN ();
+
+ if (shader->source.type.fragment == FS_ZERO) {
+ if (shader->clip.type.fragment == FS_TEXTURE) {
+ /* XXX need_combine */
+ assert (shader->mask.type.fragment == (i915_fragment_shader_t) -1);
+ i915_fs_dcl (FS_T0);
+ i915_fs_texld (FS_U0, FS_S0, FS_T0);
+ if ((shader->content & CAIRO_CONTENT_COLOR) == 0)
+ i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, W, W, W, W));
+ else
+ i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, ZERO, ZERO, ZERO, W));
+ } else {
+ i915_fs_mov (FS_OC, i915_fs_operand_zero ());
+ }
+
+ FS_END ();
+ return;
+ }
+
+ num_tex_coords = i915_shader_get_num_tex_coords (shader);
+ for (n = 0; n < num_tex_coords; n++)
+ i915_fs_dcl (FS_T0 + n);
+
+ num_samplers =
+ shader->source.base.n_samplers +
+ shader->mask.base.n_samplers +
+ shader->clip.base.n_samplers +
+ shader->dst.base.n_samplers;
+ for (n = 0; n < num_samplers; n++)
+ i915_fs_dcl (FS_S0 + n);
+
+ source_reg = ~0;
+ source_pure = 0;
+ out_reg = FS_R0;
+ if (! shader->need_combine &&
+ shader->mask.type.fragment == (i915_fragment_shader_t) -1 &&
+ shader->clip.type.fragment != FS_TEXTURE &&
+ shader->content != CAIRO_CONTENT_ALPHA)
+ {
+ out_reg = FS_OC;
+ }
+
+ switch (shader->source.type.fragment) {
+ default:
+ case FS_ZERO:
+ case FS_SPANS:
+ ASSERT_NOT_REACHED;
+
+ case FS_PURE:
+ source_pure = shader->source.solid.pure;
+ case FS_ONE:
+ break;
+
+ case FS_CONSTANT:
+ source_reg = FS_C0;
+ constant_offset += 1;
+ break;
+
+ case FS_DIFFUSE:
+ i915_fs_dcl (FS_T8);
+ source_reg = FS_T8;
+ break;
+
+ case FS_LINEAR:
+ i915_shader_linear_color (device, shader->source.base.mode,
+ FS_T0, /* input */
+ FS_C0, FS_C1, /* colour ramp */
+ FS_U3); /* unpremultiplied output */
+ /* XXX can we defer premultiplication? */
+ i915_fs_mul (out_reg,
+ i915_fs_operand_reg (FS_U3),
+ i915_fs_operand (FS_U3, W, W, W, ONE));
+
+ constant_offset += 2;
+ texture_offset += 1;
+ source_reg = out_reg;
+ break;
+
+ case FS_RADIAL:
+ i915_shader_radial_coord (device, shader->source.base.mode,
+ FS_T0, /* input */
+ FS_C0, FS_C1, /* gradient constants */
+ FS_R0); /* coordinate */
+
+ i915_fs_texld (out_reg, FS_S0, FS_R0);
+ constant_offset += 2;
+ texture_offset += 1;
+ sampler_offset += 1;
+ source_reg = out_reg;
+ break;
+
+ case FS_TEXTURE:
+ i915_fs_texld (out_reg, FS_S0, FS_T0);
+ texture_offset += 1;
+ sampler_offset += 1;
+ source_reg = out_reg;
+ break;
+
+ case FS_YUV:
+ /* Load samplers to temporaries. */
+ i915_fs_texld (FS_R0, FS_S0, FS_T0);
+ i915_fs_texld (FS_R1, FS_S1, FS_T0);
+ i915_fs_texld (FS_R2, FS_S2, FS_T0);
+
+ i915_shader_yuv_color (device,
+ FS_R0, FS_R1, FS_R2, /* y, u, v */
+ FS_C0, FS_C1, FS_C2, /* coefficients */
+ out_reg);
+
+ constant_offset += 3;
+ texture_offset += 1;
+ sampler_offset += 3;
+ source_reg = out_reg;
+ break;
+ }
+
+ mask_reg = ~0;
+ switch (shader->mask.type.fragment) {
+ case FS_PURE:
+ case FS_ZERO:
+ case FS_YUV:
+ case FS_DIFFUSE:
+ ASSERT_NOT_REACHED;
+ case FS_ONE:
+ default:
+ break;
+
+ case FS_SPANS:
+ mask_reg = FS_T0 + texture_offset;
+ texture_offset += 1;
+ break;
+
+ case FS_CONSTANT:
+ mask_reg = FS_C0 + constant_offset;
+ constant_offset += 1;
+ break;
+
+ case FS_LINEAR:
+ i915_shader_linear_color (device, shader->mask.base.mode,
+ FS_T0 + texture_offset, /* input */
+ FS_C0 + constant_offset,
+ FS_C0 + constant_offset + 1, /* colour ramp */
+ FS_R1); /* unpremultiplied output */
+ constant_offset += 2;
+ texture_offset += 1;
+ mask_reg = FS_R1;
+ break;
+
+ case FS_RADIAL:
+ i915_shader_radial_coord (device, shader->mask.base.mode,
+ FS_T0 + texture_offset, /* input */
+ FS_C0 + constant_offset,
+ FS_C0 + constant_offset + 1, /* gradient constants */
+ FS_R1); /* coordinate */
+
+ i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_R1);
+ constant_offset += 2;
+ texture_offset += 1;
+ sampler_offset += 1;
+ mask_reg = FS_R1;
+ break;
+
+ case FS_TEXTURE:
+ i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+ texture_offset += 1;
+ sampler_offset += 1;
+ mask_reg = FS_R1;
+ break;
+ }
+
+ if (mask_reg != ~0U) {
+ if (! shader->need_combine &&
+ shader->clip.type.fragment != FS_TEXTURE &&
+ (shader->content != CAIRO_CONTENT_ALPHA || source_reg == ~0U))
+ {
+ out_reg = FS_OC;
+ }
+ if (source_reg == ~0U) {
+ if (source_pure) {
+ if (shader->mask.type.fragment == FS_SPANS) {
+ if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+ if (source_pure & (1 << 3))
+ i915_fs_mov (out_reg, i915_fs_operand (mask_reg, X, X, X, X));
+ else
+ i915_fs_mov (out_reg, i915_fs_operand_zero ());
+ } else {
+ i915_fs_mov (out_reg,
+ i915_fs_operand_impure (mask_reg, X, source_pure));
+ }
+ } else {
+ /* XXX ComponentAlpha
+ i915_fs_mov (out_reg,
+ i915_fs_operand_pure (mask_reg,
+ shader->source.solid.pure));
+ */
+ if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+ if (source_pure & (1 << 3))
+ i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W));
+ else
+ i915_fs_mov (out_reg, i915_fs_operand_zero ());
+ } else {
+ i915_fs_mov (out_reg,
+ i915_fs_operand_impure (mask_reg, W, source_pure));
+ }
+ }
+ source_reg = out_reg;
+ } else if (shader->mask.type.fragment == FS_SPANS) {
+ i915_fs_mov (out_reg,
+ i915_fs_operand (mask_reg, X, X, X, X));
+ source_reg = out_reg;
+ } else {
+ source_reg = mask_reg;
+ }
+ } else {
+ if (shader->mask.type.fragment == FS_SPANS) {
+ if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+ i915_fs_mul (out_reg,
+ i915_fs_operand (source_reg, W, W, W, W),
+ i915_fs_operand (mask_reg, X, X, X, X));
+ } else {
+ i915_fs_mul (out_reg,
+ i915_fs_operand_reg (source_reg),
+ i915_fs_operand (mask_reg, X, X, X, X));
+ }
+ } else {
+ /* XXX ComponentAlpha
+ i915_fs_mul (FS_R0,
+ i915_fs_operand_reg (source_reg),
+ i915_fs_operand_reg (mask_reg));
+ */
+ if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+ i915_fs_mul (out_reg,
+ i915_fs_operand (source_reg, W, W, W, W),
+ i915_fs_operand (mask_reg, W, W, W, W));
+ } else {
+ i915_fs_mul (out_reg,
+ i915_fs_operand_reg (source_reg),
+ i915_fs_operand (mask_reg, W, W, W, W));
+ }
+ }
+
+ source_reg = out_reg;
+ }
+ }
+
+ if (shader->opacity < 1.) {
+ i915_fs_mul (source_reg,
+ i915_fs_operand_reg (source_reg),
+ i915_fs_operand_reg (FS_C0 + constant_offset));
+ constant_offset++;
+ }
+
+ /* need to preserve order of src, mask, clip, dst */
+ mask_reg = ~0;
+ if (shader->clip.type.fragment == FS_TEXTURE) {
+ i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+ texture_offset += 1;
+ sampler_offset += 1;
+ mask_reg = FS_R1;
+ }
+
+ if (shader->need_combine) {
+ assert (shader->dst.type.fragment == FS_TEXTURE);
+
+ i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+ texture_offset += 1;
+ sampler_offset += 1;
+ dest_reg = FS_R2;
+
+ switch (shader->op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ ASSERT_NOT_REACHED;
+
+ case CAIRO_OPERATOR_OVER:
+ if (source_reg == ~0U) {
+ /* XXX shader->source.type.fragment == FS_PURE */
+ dest_reg = FS_OC;
+ } else {
+ i915_fs_add (FS_U0,
+ i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W),
+ i915_fs_operand_one ());
+ i915_fs_mul (FS_U0,
+ i915_fs_operand_reg (FS_U0),
+ dest_reg);
+ i915_fs_add (FS_R3,
+ i915_fs_operand_reg (source_reg),
+ i915_fs_operand_reg (FS_U0));
+ source_reg = FS_R3;
+ }
+ break;
+
+ case CAIRO_OPERATOR_IN:
+ if (source_reg == ~0U) {
+ /* XXX shader->source.type.fragment == FS_PURE */
+ source_reg = dest_reg;
+ } else {
+ i915_fs_mul (FS_R3,
+ i915_fs_operand_reg (source_reg),
+ dest_reg);
+ source_reg = FS_R3;
+ }
+ break;
+
+ case CAIRO_OPERATOR_OUT:
+ if (source_reg == ~0U) {
+ /* XXX shader->source.type.fragment == FS_PURE */
+ i915_fs_mov (FS_R3, i915_fs_operand_zero ());
+ source_reg = FS_R3;
+ } else {
+ i915_fs_add (FS_U0,
+ i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W),
+ i915_fs_operand_one ());
+ i915_fs_mul (FS_R3,
+ i915_fs_operand_reg (FS_U0),
+ dest_reg);
+ source_reg = FS_R3;
+ }
+ break;
+
+ case CAIRO_OPERATOR_ATOP:
+
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ }
+
+ if (shader->clip.type.fragment == FS_TEXTURE) {
+ assert (mask_reg != ~0U);
+
+ if (! shader->need_combine) {
+ /* (source IN clip) */
+ if (source_reg == ~0U) {
+ if (source_pure == 0) {
+ source_reg = mask_reg;
+ } else {
+ out_reg = FS_OC;
+ if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+ if (source_pure & (1 << 3))
+ i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W));
+ else
+ i915_fs_mov (out_reg, i915_fs_operand_zero ());
+ } else {
+ i915_fs_mov (out_reg,
+ i915_fs_operand_impure (mask_reg, W, source_pure));
+ }
+ source_reg = out_reg;
+ }
+ } else if (mask_reg) {
+ out_reg = FS_OC;
+ if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+ i915_fs_mul (out_reg,
+ i915_fs_operand (source_reg, W, W, W, W),
+ i915_fs_operand (mask_reg, W, W, W, W));
+ } else {
+ i915_fs_mul (out_reg,
+ i915_fs_operand_reg (source_reg),
+ i915_fs_operand (mask_reg, W, W, W, W));
+ }
+
+ source_reg = out_reg;
+ }
+ } else {
+ /* (source OP dest) LERP_clip dest */
+ if (source_reg == ~0U) {
+ if (source_pure == 0) {
+ i915_fs_mov (FS_R3,
+ i915_fs_operand (mask_reg, W, W, W, W));
+ } else {
+ i915_fs_mov (FS_R3,
+ i915_fs_operand_impure (mask_reg, W, source_pure));
+ }
+ } else {
+ i915_fs_mul (FS_R3,
+ i915_fs_operand_reg (source_reg),
+ i915_fs_operand (mask_reg, W, W, W, W));
+ }
+
+ i915_fs_add (mask_reg,
+ i915_fs_operand_one (),
+ i915_fs_operand (mask_reg, NEG_W, NEG_W, NEG_W, NEG_W));
+
+ if (dest_reg != FS_OC) {
+ if (dest_reg == ~0U) {
+ assert (shader->dst.type.fragment == FS_TEXTURE);
+
+ i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+ texture_offset += 1;
+ sampler_offset += 1;
+ dest_reg = FS_R2;
+ }
+
+ i915_fs_mul (FS_U1,
+ i915_fs_operand_reg (dest_reg),
+ i915_fs_operand_reg (mask_reg));
+ mask_reg = FS_U1;
+ }
+
+ source_reg = FS_OC;
+ if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+ i915_fs_add (source_reg,
+ i915_fs_operand (FS_R3, W, W, W, W),
+ i915_fs_operand (mask_reg, W, W, W, W));
+ } else {
+ i915_fs_add (source_reg,
+ i915_fs_operand_reg (FS_R3),
+ i915_fs_operand_reg (mask_reg));
+ }
+ }
+ }
+
+ if (source_reg != FS_OC) {
+ if (source_reg == ~0U) {
+ if (source_pure) {
+ if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+ if (source_pure & (1 << 3))
+ i915_fs_mov (FS_OC, i915_fs_operand_one ());
+ else
+ i915_fs_mov (FS_OC, i915_fs_operand_zero ());
+ } else
+ i915_fs_mov (FS_OC, i915_fs_operand_pure (source_pure));
+ } else {
+ i915_fs_mov (FS_OC, i915_fs_operand_one ());
+ }
+ } else if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+ i915_fs_mov (FS_OC, i915_fs_operand (source_reg, W, W, W, W));
+ } else {
+ i915_fs_mov (FS_OC, i915_fs_operand_reg (source_reg));
+ }
+ }
+
+ FS_END ();
+}
+
+static cairo_bool_t
+i915_shader_linear_init (struct i915_shader_linear *l,
+ const cairo_linear_pattern_t *linear)
+{
+ double x0, y0, sf;
+ double dx, dy, offset;
+
+ dx = linear->pd2.x - linear->pd1.x;
+ dy = linear->pd2.y - linear->pd1.y;
+ sf = dx * dx + dy * dy;
+ if (sf <= 1e-5)
+ return FALSE;
+
+ dx /= sf;
+ dy /= sf;
+
+ x0 = linear->pd1.x;
+ y0 = linear->pd1.y;
+ offset = dx*x0 + dy*y0;
+
+ if (_cairo_matrix_is_identity (&linear->base.base.matrix)) {
+ l->dx = dx;
+ l->dy = dy;
+ l->offset = -offset;
+ } else {
+ cairo_matrix_t m;
+
+ cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0);
+ cairo_matrix_multiply (&m, &linear->base.base.matrix, &m);
+ l->dx = m.xx;
+ l->dy = m.xy;
+ l->offset = m.x0;
+ }
+
+ return TRUE;
+}
+
+static cairo_bool_t
+i915_shader_linear_contains_rectangle (struct i915_shader_linear *l,
+ const cairo_rectangle_int_t *extents)
+{
+ double v;
+
+ v = i915_shader_linear_texcoord (l,
+ extents->x,
+ extents->y);
+ if (v < 0.)
+ return FALSE;
+ if (v > 1.)
+ return FALSE;
+
+ v = i915_shader_linear_texcoord (l,
+ extents->x + extents->width,
+ extents->y);
+ if (v < 0.)
+ return FALSE;
+ if (v > 1.)
+ return FALSE;
+
+ v = i915_shader_linear_texcoord (l,
+ extents->x,
+ extents->y + extents->height);
+ if (v < 0.)
+ return FALSE;
+ if (v > 1.)
+ return FALSE;
+
+ v = i915_shader_linear_texcoord (l,
+ extents->x + extents->width,
+ extents->y + extents->height);
+ if (v < 0.)
+ return FALSE;
+ if (v > 1.)
+ return FALSE;
+
+ return TRUE;
+}
+
+#define is_pure(C,mask) (((mask) == 0) || (C) <= 0x00ff || (C) >= 0xff00)
+#define is_one(C,mask) (((mask) != 0) && (C) >= 0xff00)
+#define is_zero(C,mask) (((mask) != 0) && (C) <= 0x00ff)
+
+static cairo_status_t
+i915_shader_acquire_solid (i915_shader_t *shader,
+ union i915_shader_channel *src,
+ const cairo_solid_pattern_t *solid,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_content_t content;
+
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+ src->solid.color = solid->color;
+ if (content == 0 || solid->color.alpha_short <= 0x00ff)
+ {
+ src->base.content = CAIRO_CONTENT_ALPHA;
+ src->type.fragment = FS_ZERO;
+ }
+ else if ((((content & CAIRO_CONTENT_COLOR) == 0) ||
+ (solid->color.red_short >= 0xff00 &&
+ solid->color.green_short >= 0xff00 &&
+ solid->color.blue_short >= 0xff00)) &&
+ ((content & CAIRO_CONTENT_ALPHA) == 0 ||
+ solid->color.alpha_short >= 0xff00))
+ {
+ src->base.content = CAIRO_CONTENT_ALPHA;
+ src->type.fragment = FS_ONE;
+ }
+ else if (is_pure (solid->color.red_short, content & CAIRO_CONTENT_COLOR) &&
+ is_pure (solid->color.green_short, content & CAIRO_CONTENT_COLOR) &&
+ is_pure (solid->color.blue_short, content & CAIRO_CONTENT_COLOR) &&
+ is_pure (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA))
+ {
+ src->solid.pure = 0;
+ src->solid.pure |= is_one (solid->color.red_short, content & CAIRO_CONTENT_COLOR) << 0;
+ src->solid.pure |= is_one (solid->color.green_short, content & CAIRO_CONTENT_COLOR) << 1;
+ src->solid.pure |= is_one (solid->color.blue_short, content & CAIRO_CONTENT_COLOR) << 2;
+ src->solid.pure |= (! is_zero (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA)) << 3;
+
+ if (src->solid.pure == 0) {
+ src->base.content = CAIRO_CONTENT_ALPHA;
+ src->type.fragment = FS_ZERO;
+ } else if (src->solid.pure == 0x7) {
+ src->base.content = CAIRO_CONTENT_ALPHA;
+ src->type.fragment = FS_ONE;
+ } else {
+ src->base.content = content;
+ src->type.fragment = FS_PURE;
+ src->base.mode = src->solid.pure;
+ }
+ }
+ else
+ {
+ src->base.content = content;
+ src->type.fragment = src == &shader->source ? FS_DIFFUSE : FS_CONSTANT;
+ }
+ src->type.vertex = src->type.fragment == FS_ZERO ? VS_ZERO : VS_CONSTANT;
+ src->type.pattern = PATTERN_CONSTANT;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_shader_acquire_linear (i915_shader_t *shader,
+ union i915_shader_channel *src,
+ const cairo_linear_pattern_t *linear,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t mode = LINEAR_TEXTURE;
+ cairo_status_t status;
+
+ if (i915_shader_linear_init (&src->linear, linear) &&
+ linear->base.n_stops == 2 &&
+ linear->base.stops[0].offset == 0.0 &&
+ linear->base.stops[1].offset == 1.0)
+ {
+ if (i915_shader_linear_contains_rectangle (&src->linear,
+ extents))
+ {
+ /* XXX can also lerp if contained within offset range */
+ mode = LINEAR_NONE;
+ }
+ else switch (linear->base.base.extend) {
+ case CAIRO_EXTEND_REPEAT:
+ mode = LINEAR_REPEAT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ mode = LINEAR_PAD;
+ break;
+ case CAIRO_EXTEND_NONE:
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ }
+
+ src->type.vertex = VS_LINEAR;
+ src->type.pattern = PATTERN_LINEAR;
+ src->base.texfmt = TEXCOORDFMT_1D;
+ src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+ src->base.mode = mode;
+ if (mode == LINEAR_TEXTURE) {
+ intel_buffer_t buffer;
+
+ status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device,
+ &linear->base, &buffer);
+ if (unlikely (status))
+ return status;
+
+ src->type.fragment = FS_TEXTURE;
+ src->base.bo = intel_bo_reference (buffer.bo);
+ src->base.n_samplers = 1;
+ src->base.offset[0] = buffer.offset;
+ src->base.map[0] = buffer.map0;
+ src->base.map[1] = buffer.map1;
+ src->base.sampler[0] =
+ (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+ i915_texture_filter (CAIRO_FILTER_BILINEAR);
+ src->base.sampler[1] =
+ SS3_NORMALIZED_COORDS |
+ i915_texture_extend (linear->base.base.extend);
+ } else {
+ src->type.fragment = FS_LINEAR;
+ src->linear.color0.red = linear->base.stops[0].color.red;
+ src->linear.color0.green = linear->base.stops[0].color.green;
+ src->linear.color0.blue = linear->base.stops[0].color.blue;
+ src->linear.color0.alpha = linear->base.stops[0].color.alpha;
+
+ src->linear.color1.red = linear->base.stops[1].color.red;
+ src->linear.color1.green = linear->base.stops[1].color.green;
+ src->linear.color1.blue = linear->base.stops[1].color.blue;
+ src->linear.color1.alpha = linear->base.stops[1].color.alpha;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_shader_acquire_radial (i915_shader_t *shader,
+ union i915_shader_channel *src,
+ const cairo_radial_pattern_t *radial,
+ const cairo_rectangle_int_t *extents)
+{
+ intel_buffer_t buffer;
+ cairo_status_t status;
+
+ status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device,
+ &radial->base, &buffer);
+ if (unlikely (status))
+ return status;
+
+ i915_shader_radial_init (&src->radial, radial);
+
+ src->type.vertex = VS_TEXTURE;
+ src->type.fragment = FS_RADIAL;
+ src->type.pattern = PATTERN_RADIAL;
+ src->base.texfmt = TEXCOORDFMT_2D;
+
+ src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+ src->base.bo = intel_bo_reference (buffer.bo);
+ src->base.n_samplers = 1;
+ src->base.offset[0] = buffer.offset;
+ src->base.map[0] = buffer.map0;
+ src->base.map[1] = buffer.map1;
+ src->base.sampler[0] =
+ (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+ i915_texture_filter (CAIRO_FILTER_BILINEAR);
+ src->base.sampler[1] =
+ SS3_NORMALIZED_COORDS |
+ i915_texture_extend (radial->base.base.extend);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_surface_clone (i915_device_t *device,
+ cairo_image_surface_t *image,
+ i915_surface_t **clone_out)
+{
+ i915_surface_t *clone;
+ cairo_status_t status;
+
+#if 0
+ clone =
+ i915_surface_create_from_cacheable_image_internal (device, image);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+#else
+ cairo_format_t format;
+
+ format = image->format;
+ if (format == CAIRO_FORMAT_A1)
+ format = CAIRO_FORMAT_A8;
+
+ clone = (i915_surface_t *)
+ i915_surface_create_internal (&device->intel.base,
+ format,
+ image->width,
+ image->height,
+ I915_TILING_DEFAULT,
+ FALSE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ status = intel_bo_put_image (&device->intel,
+ to_intel_bo (clone->intel.drm.bo),
+ image,
+ 0, 0,
+ image->width, image->height,
+ 0, 0);
+
+ if (unlikely (status))
+ return status;
+#endif
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_surface_clone_subimage (i915_device_t *device,
+ cairo_image_surface_t *image,
+ const cairo_rectangle_int_t *extents,
+ i915_surface_t **clone_out)
+{
+ i915_surface_t *clone;
+ cairo_status_t status;
+ cairo_format_t format;
+
+ format = image->format;
+ if (format == CAIRO_FORMAT_A1)
+ format = CAIRO_FORMAT_A8;
+
+ clone = (i915_surface_t *)
+ i915_surface_create_internal (&device->intel.base,
+ format,
+ extents->width,
+ extents->height,
+ I915_TILING_NONE,
+ FALSE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ status = intel_bo_put_image (&device->intel,
+ to_intel_bo (clone->intel.drm.bo),
+ image,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ 0, 0);
+
+ if (unlikely (status))
+ return status;
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_surface_render_pattern (i915_device_t *device,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ i915_surface_t **clone_out)
+{
+ i915_surface_t *clone;
+ cairo_surface_t *image;
+ cairo_status_t status;
+ void *ptr;
+
+ clone = (i915_surface_t *)
+ i915_surface_create_internal (&device->intel.base,
+ _cairo_format_from_content (pattern->surface->content),
+ extents->width,
+ extents->height,
+ I915_TILING_NONE,
+ FALSE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ ptr = intel_bo_map (&device->intel,
+ to_intel_bo (clone->intel.drm.bo));
+ if (unlikely (ptr == NULL)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ image = cairo_image_surface_create_for_data (ptr,
+ clone->intel.drm.format,
+ clone->intel.drm.width,
+ clone->intel.drm.height,
+ clone->intel.drm.stride);
+ if (unlikely (image->status)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return image->status;
+ }
+
+ status = _cairo_surface_offset_paint (image,
+ extents->x, extents->y,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern->base,
+ NULL);
+ cairo_surface_destroy (image);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return status;
+ }
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_shader_acquire_solid_surface (i915_shader_t *shader,
+ union i915_shader_channel *src,
+ cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *pixel;
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+ uint32_t argb;
+
+ status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ /* extract the pixel as argb32 */
+ pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+ _cairo_pattern_init_for_surface (&pattern, &image->base);
+ cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ _cairo_surface_release_source_image (surface, image, image_extra);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (pixel);
+ return status;
+ }
+
+ image = (cairo_image_surface_t *) pixel;
+ argb = *(uint32_t *) image->data;
+ cairo_surface_destroy (pixel);
+
+ if (argb >> 24 == 0) {
+ _cairo_color_init_rgba (&src->solid.color, 0, 0, 0, 0);
+ } else {
+ uint8_t alpha = argb >> 24;
+
+ _cairo_color_init_rgba (&src->solid.color,
+ ((((argb >> 16) & 0xff) * 255 + alpha / 2) / alpha) / 255.,
+ ((((argb >> 8) & 0xff) * 255 + alpha / 2) / alpha) / 255.,
+ ((((argb >> 0) & 0xff) * 255 + alpha / 2) / alpha) / 255.,
+ alpha / 255.);
+ }
+
+ src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+ src->type.fragment = FS_CONSTANT;
+ src->type.vertex = VS_CONSTANT;
+ src->type.pattern = PATTERN_CONSTANT;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_filter_t
+sampled_area (const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_rectangle_int_t *sample)
+{
+ cairo_rectangle_int_t surface_extents;
+ 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;
+
+ if (_cairo_matrix_is_translation (&pattern->base.matrix)) {
+ x1 += pattern->base.matrix.x0; x2 += pattern->base.matrix.x0;
+ y1 += pattern->base.matrix.y0; y2 += pattern->base.matrix.y0;
+ } else {
+ _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;
+
+ if (_cairo_surface_get_extents (pattern->surface, &surface_extents))
+ _cairo_rectangle_intersect (sample, &surface_extents);
+
+ return filter;
+}
+
+static cairo_status_t
+i915_shader_acquire_surface (i915_shader_t *shader,
+ union i915_shader_channel *src,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ int surface_width, surface_height;
+ cairo_surface_t *surface, *drm;
+ cairo_extend_t extend;
+ cairo_filter_t filter;
+ cairo_matrix_t m;
+ int src_x = 0, src_y = 0;
+ cairo_surface_t *free_me = NULL;
+ cairo_status_t status;
+ cairo_rectangle_int_t sample;
+
+ assert (src->type.fragment == (i915_fragment_shader_t) -1);
+ drm = surface = pattern->surface;
+
+ extend = pattern->base.extend;
+ src->base.matrix = pattern->base.matrix;
+ filter = sampled_area (pattern, extents, &sample);
+
+ if (surface->type == CAIRO_SURFACE_TYPE_DRM) {
+ if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ drm = ((cairo_surface_subsurface_t *) surface)->target;
+ } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+ drm = ((cairo_surface_snapshot_t *) surface)->target;
+ }
+ }
+
+ if (drm->type == CAIRO_SURFACE_TYPE_DRM) {
+ i915_surface_t *s = (i915_surface_t *) drm;
+
+ if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ if (s->intel.drm.base.device == shader->target->intel.drm.base.device &&
+ s != shader->target)
+ {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface;
+ int x;
+
+ status = i915_surface_fallback_flush (s);
+ if (unlikely (status))
+ return status;
+
+ /* XXX blt subimage and cache snapshot */
+
+ if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) {
+ /* XXX pipelined flush of RENDER/TEXTURE cache */
+ }
+
+ src->type.fragment = FS_TEXTURE;
+ src->surface.pixel = NONE;
+ surface_width = sub->extents.width;
+ surface_height = sub->extents.height;
+
+ src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo));
+ src->base.n_samplers = 1;
+
+ x = sub->extents.x;
+ if (s->intel.drm.format != CAIRO_FORMAT_A8)
+ x *= 4;
+
+ /* XXX tiling restrictions upon offset? */
+ src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x;
+ src->base.map[0] = s->map0;
+ src->base.map[0] &= ~((2047 << MS3_HEIGHT_SHIFT) | (2047 << MS3_WIDTH_SHIFT));
+ src->base.map[0] |=
+ ((sub->extents.height - 1) << MS3_HEIGHT_SHIFT) |
+ ((sub->extents.width - 1) << MS3_WIDTH_SHIFT);
+ src->base.map[1] = (s->intel.drm.stride / 4 - 1) << MS4_PITCH_SHIFT;
+ }
+ } else {
+ /* XXX if s == shader->dst allow if FILTER_NEAREST, EXTEND_NONE? */
+ if (s->intel.drm.base.device == shader->target->intel.drm.base.device) {
+ status = i915_surface_fallback_flush (s);
+ if (unlikely (status))
+ return status;
+
+ if (s == shader->target || i915_surface_needs_tiling (s)) {
+ status = i915_surface_copy_subimage (i915_device (shader->target),
+ s, &sample, TRUE, &s);
+ if (unlikely (status))
+ return status;
+
+ free_me = drm = &s->intel.drm.base;
+ }
+
+ src->type.fragment = FS_TEXTURE;
+ src->surface.pixel = NONE;
+
+ surface_width = s->intel.drm.width;
+ surface_height = s->intel.drm.height;
+
+ src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo));
+ src->base.n_samplers = 1;
+ src->base.offset[0] = s->offset;
+ src->base.map[0] = s->map0;
+ src->base.map[1] = s->map1;
+ }
+ }
+ }
+
+ if (src->type.fragment == (i915_fragment_shader_t) -1) {
+ i915_surface_t *s;
+
+ if (extents->width == 1 && extents->height == 1) {
+ return i915_shader_acquire_solid_surface (shader, src,
+ surface, extents);
+ }
+
+ s = (i915_surface_t *)
+ _cairo_surface_has_snapshot (surface,
+ shader->target->intel.drm.base.backend);
+ if (s == NULL) {
+ cairo_status_t status;
+
+#if 0
+ /* XXX hackity hack hack */
+ status = i915_clone_yuv (surface, src,
+ image->width, image->height,
+ clone_out);
+#endif
+
+ if (sample.width > 2048 || sample.height > 2048) {
+ status = i915_surface_render_pattern (i915_device (shader->target),
+ pattern, extents,
+ &s);
+ if (unlikely (status))
+ return status;
+
+ extend = CAIRO_EXTEND_NONE;
+ filter = CAIRO_FILTER_NEAREST;
+ cairo_matrix_init_translate (&src->base.matrix,
+ -extents->x, -extents->y);
+ } else {
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ if (image->width < 2048 &&
+ image->height < 2048 &&
+ sample.width >= image->width / 4 &&
+ sample.height >= image->height /4)
+ {
+
+ status = i915_surface_clone (i915_device (shader->target),
+ image, &s);
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_surface_attach_snapshot (surface,
+ &s->intel.drm.base,
+ intel_surface_detach_snapshot);
+
+ status = intel_snapshot_cache_insert (&i915_device (shader->target)->intel,
+ &s->intel);
+ if (unlikely (status)) {
+ cairo_surface_finish (&s->intel.drm.base);
+ cairo_surface_destroy (&s->intel.drm.base);
+ }
+ }
+ }
+ else
+ {
+ status = i915_surface_clone_subimage (i915_device (shader->target),
+ image, &sample, &s);
+ src_x = -extents->x;
+ src_y = -extents->y;
+ }
+
+ _cairo_surface_release_source_image (surface, image, image_extra);
+ if (unlikely (status))
+ return status;
+ }
+
+ free_me = &s->intel.drm.base;
+ }
+
+ src->type.fragment = FS_TEXTURE;
+ src->surface.pixel = NONE;
+
+ src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo));
+ src->base.n_samplers = 1;
+ src->base.offset[0] = s->offset;
+ src->base.map[0] = s->map0;
+ src->base.map[1] = s->map1;
+
+ drm = &s->intel.drm.base;
+
+ surface_width = s->intel.drm.width;
+ surface_height = s->intel.drm.height;
+ }
+
+ /* XXX transform nx1 or 1xn surfaces to 1D */
+
+ src->type.pattern = PATTERN_TEXTURE;
+ if (extend != CAIRO_EXTEND_NONE &&
+ sample.x >= 0 && sample.y >= 0 &&
+ sample.x + sample.width <= surface_width &&
+ sample.y + sample.height <= surface_height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ }
+ if (extend == CAIRO_EXTEND_NONE) {
+ src->type.vertex = VS_TEXTURE_16;
+ src->base.texfmt = TEXCOORDFMT_2D_16;
+ } else {
+ src->type.vertex = VS_TEXTURE;
+ src->base.texfmt = TEXCOORDFMT_2D;
+ }
+ src->base.content = drm->content;
+
+ src->base.sampler[0] =
+ (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+ i915_texture_filter (filter);
+ src->base.sampler[1] =
+ SS3_NORMALIZED_COORDS |
+ i915_texture_extend (extend);
+
+ /* tweak the src matrix to map from dst to texture coordinates */
+ if (src_x | src_y)
+ cairo_matrix_translate (&src->base.matrix, src_x, src_x);
+ cairo_matrix_init_scale (&m, 1. / surface_width, 1. / surface_height);
+ cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m);
+
+ if (free_me != NULL)
+ cairo_surface_destroy (free_me);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+i915_shader_acquire_pattern (i915_shader_t *shader,
+ union i915_shader_channel *src,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return i915_shader_acquire_solid (shader, src,
+ (cairo_solid_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return i915_shader_acquire_linear (shader, src,
+ (cairo_linear_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return i915_shader_acquire_radial (shader, src,
+ (cairo_radial_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return i915_shader_acquire_surface (shader, src,
+ (cairo_surface_pattern_t *) pattern,
+ extents);
+
+ default:
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+ }
+}
+
+static uint32_t
+i915_get_blend (cairo_operator_t op,
+ i915_surface_t *dst)
+{
+#define SBLEND(X) ((BLENDFACT_##X) << S6_CBUF_SRC_BLEND_FACT_SHIFT)
+#define DBLEND(X) ((BLENDFACT_##X) << S6_CBUF_DST_BLEND_FACT_SHIFT)
+ static const struct blendinfo {
+ cairo_bool_t dst_alpha;
+ uint32_t src_blend;
+ uint32_t dst_blend;
+ enum {
+ BOUNDED,
+ SIMPLE,
+ XRENDER,
+ } kind;
+ } i915_blend_op[] = {
+ {0, SBLEND (ZERO), DBLEND (ZERO), BOUNDED}, /* Clear */
+ {0, SBLEND (ONE), DBLEND (ZERO), BOUNDED}, /* Src */
+
+ {0, SBLEND (ONE), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Over */
+ {1, SBLEND (DST_ALPHA), DBLEND (ZERO), XRENDER}, /* In */
+ {1, SBLEND (INV_DST_ALPHA), DBLEND (ZERO), XRENDER}, /* Out */
+ {1, SBLEND (DST_ALPHA), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Atop */
+
+ {0, SBLEND (ZERO), DBLEND (ONE), SIMPLE}, /* Dst */
+ {1, SBLEND (INV_DST_ALPHA), DBLEND (ONE), SIMPLE}, /* OverReverse */
+ {0, SBLEND (ZERO), DBLEND (SRC_ALPHA), XRENDER}, /* InReverse */
+ {0, SBLEND (ZERO), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* OutReverse */
+ {1, SBLEND (INV_DST_ALPHA), DBLEND (SRC_ALPHA), XRENDER}, /* AtopReverse */
+
+ {1, SBLEND (INV_DST_ALPHA), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Xor */
+ {0, SBLEND (ONE), DBLEND (ONE), SIMPLE}, /* Add */
+ //{0, 0, SBLEND (SRC_ALPHA_SATURATE), DBLEND (ONE), SIMPLE}, /* XXX Saturate */
+ };
+ uint32_t sblend, dblend;
+
+ if (op >= ARRAY_LENGTH (i915_blend_op))
+ return 0;
+
+ if (i915_blend_op[op].kind == BOUNDED)
+ return 0;
+
+ sblend = i915_blend_op[op].src_blend;
+ dblend = i915_blend_op[op].dst_blend;
+
+ /* If there's no dst alpha channel, adjust the blend op so that we'll treat
+ * it as always 1.
+ */
+ if ((dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA) == 0 &&
+ i915_blend_op[op].dst_alpha)
+ {
+ if (sblend == SBLEND (DST_ALPHA))
+ sblend = SBLEND (ONE);
+ else if (sblend == SBLEND (INV_DST_ALPHA))
+ sblend = SBLEND (ZERO);
+ }
+
+ /* i915 engine reads 8bit color buffer into green channel in cases
+ like color buffer blending etc., and also writes back green channel.
+ So with dst_alpha blend we should use color factor. See spec on
+ "8-bit rendering" */
+ if (dst->intel.drm.format == CAIRO_FORMAT_A8 && i915_blend_op[op].dst_alpha) {
+ if (sblend == SBLEND (DST_ALPHA))
+ sblend = SBLEND (DST_COLR);
+ else if (sblend == SBLEND (INV_DST_ALPHA))
+ sblend = SBLEND (INV_DST_COLR);
+ }
+
+ return sblend | dblend;
+#undef SBLEND
+#undef DBLEND
+}
+
+static void
+i915_shader_channel_init (union i915_shader_channel *channel)
+{
+ channel->type.vertex = (i915_vertex_shader_t) -1;
+ channel->type.fragment = (i915_fragment_shader_t) -1;
+ channel->type.pattern = (i915_shader_channel_t) -1;
+ channel->base.texfmt = TEXCOORDFMT_NOT_PRESENT;
+ channel->base.bo = NULL;
+ channel->base.n_samplers = 0;
+ channel->base.mode = 0;
+}
+
+static void
+i915_shader_channel_fini (i915_device_t *device,
+ union i915_shader_channel *channel)
+{
+ switch (channel->type.pattern) {
+ case PATTERN_TEXTURE:
+ case PATTERN_BASE:
+ case PATTERN_LINEAR:
+ case PATTERN_RADIAL:
+ if (channel->base.bo != NULL)
+ intel_bo_destroy (&device->intel, channel->base.bo);
+ break;
+
+ default:
+ case PATTERN_CONSTANT:
+ break;
+ }
+}
+
+static void
+i915_shader_channel_reset (i915_device_t *device,
+ union i915_shader_channel *channel)
+{
+ i915_shader_channel_fini (device, channel);
+ i915_shader_channel_init (channel);
+}
+
+void
+i915_shader_init (i915_shader_t *shader,
+ i915_surface_t *dst,
+ cairo_operator_t op,
+ double opacity)
+{
+ shader->committed = FALSE;
+ shader->device = i915_device (dst);
+ shader->target = dst;
+ shader->op = op;
+ shader->opacity = opacity;
+
+ shader->blend = i915_get_blend (op, dst);
+ shader->need_combine = FALSE;
+
+ shader->content = dst->intel.drm.base.content;
+
+ i915_shader_channel_init (&shader->source);
+ i915_shader_channel_init (&shader->mask);
+ i915_shader_channel_init (&shader->clip);
+ i915_shader_channel_init (&shader->dst);
+}
+
+static void
+i915_set_shader_samplers (i915_device_t *device,
+ const i915_shader_t *shader)
+{
+ uint32_t n_samplers, n_maps, n;
+ uint32_t samplers[2*4];
+ uint32_t maps[4*4];
+ uint32_t mask, s, m;
+
+ n_maps =
+ shader->source.base.n_samplers +
+ shader->mask.base.n_samplers +
+ shader->clip.base.n_samplers +
+ shader->dst.base.n_samplers;
+ assert (n_maps <= 4);
+
+ if (n_maps == 0)
+ return;
+
+ n_samplers =
+ !! shader->source.base.bo +
+ !! shader->mask.base.bo +
+ !! shader->clip.base.bo +
+ !! shader->dst.base.bo;
+
+ mask = (1 << n_maps) - 1;
+
+ /* We check for repeated setting of sample state mainly to catch
+ * continuation of text strings across multiple show-glyphs.
+ */
+ s = m = 0;
+ if (shader->source.base.bo != NULL) {
+ samplers[s++] = shader->source.base.sampler[0];
+ samplers[s++] = shader->source.base.sampler[1];
+ maps[m++] = shader->source.base.bo->base.handle;
+ for (n = 0; n < shader->source.base.n_samplers; n++) {
+ maps[m++] = shader->source.base.offset[n];
+ maps[m++] = shader->source.base.map[2*n+0];
+ maps[m++] = shader->source.base.map[2*n+1];
+ }
+ }
+ if (shader->mask.base.bo != NULL) {
+ samplers[s++] = shader->mask.base.sampler[0];
+ samplers[s++] = shader->mask.base.sampler[1];
+ maps[m++] = shader->mask.base.bo->base.handle;
+ for (n = 0; n < shader->mask.base.n_samplers; n++) {
+ maps[m++] = shader->mask.base.offset[n];
+ maps[m++] = shader->mask.base.map[2*n+0];
+ maps[m++] = shader->mask.base.map[2*n+1];
+ }
+ }
+ if (shader->clip.base.bo != NULL) {
+ samplers[s++] = shader->clip.base.sampler[0];
+ samplers[s++] = shader->clip.base.sampler[1];
+ maps[m++] = shader->clip.base.bo->base.handle;
+ for (n = 0; n < shader->clip.base.n_samplers; n++) {
+ maps[m++] = shader->clip.base.offset[n];
+ maps[m++] = shader->clip.base.map[2*n+0];
+ maps[m++] = shader->clip.base.map[2*n+1];
+ }
+ }
+ if (shader->dst.base.bo != NULL) {
+ samplers[s++] = shader->dst.base.sampler[0];
+ samplers[s++] = shader->dst.base.sampler[1];
+ maps[m++] = shader->dst.base.bo->base.handle;
+ for (n = 0; n < shader->dst.base.n_samplers; n++) {
+ maps[m++] = shader->dst.base.offset[n];
+ maps[m++] = shader->dst.base.map[2*n+0];
+ maps[m++] = shader->dst.base.map[2*n+1];
+ }
+ }
+
+ if (n_maps > device->current_n_maps ||
+ memcmp (device->current_maps,
+ maps,
+ m * sizeof (uint32_t)))
+ {
+ memcpy (device->current_maps, maps, m * sizeof (uint32_t));
+ device->current_n_maps = n_maps;
+
+ if (device->current_source != NULL)
+ *device->current_source = 0;
+ if (device->current_mask != NULL)
+ *device->current_mask = 0;
+ if (device->current_clip != NULL)
+ *device->current_clip = 0;
+
+#if 0
+ if (shader->source.type.pattern == PATTERN_TEXTURE) {
+ switch ((int) shader->source.surface.surface->type) {
+ case CAIRO_SURFACE_TYPE_DRM:
+ {
+ i915_surface_t *surface =
+ (i915_surface_t *) shader->source.surface.surface;
+ device->current_source = &surface->is_current_texture;
+ surface->is_current_texture |= CURRENT_SOURCE;
+ break;
+ }
+
+ case I915_PACKED_PIXEL_SURFACE_TYPE:
+ {
+ i915_packed_pixel_surface_t *surface =
+ (i915_packed_pixel_surface_t *) shader->source.surface.surface;
+ device->current_source = &surface->is_current_texture;
+ surface->is_current_texture |= CURRENT_SOURCE;
+ break;
+ }
+
+ default:
+ device->current_source = NULL;
+ break;
+ }
+ } else
+ device->current_source = NULL;
+
+ if (shader->mask.type.pattern == PATTERN_TEXTURE) {
+ switch ((int) shader->mask.surface.surface->type) {
+ case CAIRO_SURFACE_TYPE_DRM:
+ {
+ i915_surface_t *surface =
+ (i915_surface_t *) shader->mask.surface.surface;
+ device->current_mask = &surface->is_current_texture;
+ surface->is_current_texture |= CURRENT_MASK;
+ break;
+ }
+
+ case I915_PACKED_PIXEL_SURFACE_TYPE:
+ {
+ i915_packed_pixel_surface_t *surface =
+ (i915_packed_pixel_surface_t *) shader->mask.surface.surface;
+ device->current_mask = &surface->is_current_texture;
+ surface->is_current_texture |= CURRENT_MASK;
+ break;
+ }
+
+ default:
+ device->current_mask = NULL;
+ break;
+ }
+ } else
+ device->current_mask = NULL;
+#endif
+
+ OUT_DWORD (_3DSTATE_MAP_STATE | (3 * n_maps));
+ OUT_DWORD (mask);
+ for (n = 0; n < shader->source.base.n_samplers; n++) {
+ i915_batch_emit_reloc (device, shader->source.base.bo,
+ shader->source.base.offset[n],
+ I915_GEM_DOMAIN_SAMPLER, 0,
+ FALSE);
+ OUT_DWORD (shader->source.base.map[2*n+0]);
+ OUT_DWORD (shader->source.base.map[2*n+1]);
+ }
+ for (n = 0; n < shader->mask.base.n_samplers; n++) {
+ i915_batch_emit_reloc (device, shader->mask.base.bo,
+ shader->mask.base.offset[n],
+ I915_GEM_DOMAIN_SAMPLER, 0,
+ FALSE);
+ OUT_DWORD (shader->mask.base.map[2*n+0]);
+ OUT_DWORD (shader->mask.base.map[2*n+1]);
+ }
+ for (n = 0; n < shader->clip.base.n_samplers; n++) {
+ i915_batch_emit_reloc (device, shader->clip.base.bo,
+ shader->clip.base.offset[n],
+ I915_GEM_DOMAIN_SAMPLER, 0,
+ FALSE);
+ OUT_DWORD (shader->clip.base.map[2*n+0]);
+ OUT_DWORD (shader->clip.base.map[2*n+1]);
+ }
+ for (n = 0; n < shader->dst.base.n_samplers; n++) {
+ i915_batch_emit_reloc (device, shader->dst.base.bo,
+ shader->dst.base.offset[n],
+ I915_GEM_DOMAIN_SAMPLER, 0,
+ FALSE);
+ OUT_DWORD (shader->dst.base.map[2*n+0]);
+ OUT_DWORD (shader->dst.base.map[2*n+1]);
+ }
+ }
+
+ if (n_samplers > device->current_n_samplers ||
+ memcmp (device->current_samplers,
+ samplers,
+ s * sizeof (uint32_t)))
+ {
+ device->current_n_samplers = s;
+ memcpy (device->current_samplers, samplers, s * sizeof (uint32_t));
+
+ OUT_DWORD (_3DSTATE_SAMPLER_STATE | (3 * n_maps));
+ OUT_DWORD (mask);
+ s = 0;
+ for (n = 0; n < shader->source.base.n_samplers; n++) {
+ OUT_DWORD (shader->source.base.sampler[0]);
+ OUT_DWORD (shader->source.base.sampler[1] |
+ (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+ OUT_DWORD (0x0);
+ s++;
+ }
+ for (n = 0; n < shader->mask.base.n_samplers; n++) {
+ OUT_DWORD (shader->mask.base.sampler[0]);
+ OUT_DWORD (shader->mask.base.sampler[1] |
+ (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+ OUT_DWORD (0x0);
+ s++;
+ }
+ for (n = 0; n < shader->clip.base.n_samplers; n++) {
+ OUT_DWORD (shader->clip.base.sampler[0]);
+ OUT_DWORD (shader->clip.base.sampler[1] |
+ (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+ OUT_DWORD (0x0);
+ s++;
+ }
+ for (n = 0; n < shader->dst.base.n_samplers; n++) {
+ OUT_DWORD (shader->dst.base.sampler[0]);
+ OUT_DWORD (shader->dst.base.sampler[1] |
+ (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+ OUT_DWORD (0x0);
+ s++;
+ }
+ }
+}
+
+static uint32_t
+i915_shader_get_texcoords (const i915_shader_t *shader)
+{
+ uint32_t texcoords;
+ uint32_t tu;
+
+ texcoords = S2_TEXCOORD_NONE;
+ tu = 0;
+ if (shader->source.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+ texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+ texcoords |= S2_TEXCOORD_FMT (tu, shader->source.base.texfmt);
+ tu++;
+ }
+ if (shader->mask.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+ texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+ texcoords |= S2_TEXCOORD_FMT (tu, shader->mask.base.texfmt);
+ tu++;
+ }
+ if (shader->clip.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+ texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+ texcoords |= S2_TEXCOORD_FMT (tu, shader->clip.base.texfmt);
+ tu++;
+ }
+ if (shader->dst.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+ texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+ texcoords |= S2_TEXCOORD_FMT (tu, shader->dst.base.texfmt);
+ tu++;
+ }
+
+ return texcoords;
+}
+
+static void
+i915_set_shader_mode (i915_device_t *device,
+ const i915_shader_t *shader)
+{
+ uint32_t texcoords;
+ uint32_t mask, cnt;
+
+ texcoords = i915_shader_get_texcoords (shader);
+
+ mask = cnt = 0;
+
+ if (device->current_texcoords != texcoords)
+ mask |= I1_LOAD_S (2), cnt++;
+
+ if (device->current_blend != shader->blend)
+ mask |= I1_LOAD_S (6), cnt++;
+
+ if (cnt == 0)
+ return;
+
+ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | mask | (cnt-1));
+
+ if (device->current_texcoords != texcoords) {
+ OUT_DWORD (texcoords);
+ device->current_texcoords = texcoords;
+ }
+
+ if (device->current_blend != shader->blend) {
+ if (shader->blend) {
+ OUT_DWORD (S6_CBUF_BLEND_ENABLE | S6_COLOR_WRITE_ENABLE |
+ (BLENDFUNC_ADD << S6_CBUF_BLEND_FUNC_SHIFT) |
+ shader->blend);
+ } else {
+ OUT_DWORD (S6_COLOR_WRITE_ENABLE);
+ }
+
+ device->current_blend = shader->blend;
+ }
+}
+
+static void
+i915_set_constants (i915_device_t *device,
+ const uint32_t *constants,
+ uint32_t n_constants)
+{
+ uint32_t n;
+
+ OUT_DWORD (_3DSTATE_PIXEL_SHADER_CONSTANTS | n_constants);
+ OUT_DWORD ((1 << (n_constants >> 2)) - 1);
+
+ for (n = 0; n < n_constants; n++)
+ OUT_DWORD (constants[n]);
+
+ device->current_n_constants = n_constants;
+ memcpy (device->current_constants, constants, n_constants*4);
+}
+
+static uint32_t
+pack_constants (const union i915_shader_channel *channel,
+ uint32_t *constants)
+{
+ uint32_t count = 0, n;
+
+ switch (channel->type.fragment) {
+ case FS_ZERO:
+ case FS_ONE:
+ case FS_PURE:
+ case FS_DIFFUSE:
+ break;
+
+ case FS_CONSTANT:
+ constants[count++] = pack_float (channel->solid.color.red);
+ constants[count++] = pack_float (channel->solid.color.green);
+ constants[count++] = pack_float (channel->solid.color.blue);
+ constants[count++] = pack_float (channel->solid.color.alpha);
+ break;
+
+ case FS_LINEAR:
+ constants[count++] = pack_float (channel->linear.color0.red);
+ constants[count++] = pack_float (channel->linear.color0.green);
+ constants[count++] = pack_float (channel->linear.color0.blue);
+ constants[count++] = pack_float (channel->linear.color0.alpha);
+
+ constants[count++] = pack_float (channel->linear.color1.red);
+ constants[count++] = pack_float (channel->linear.color1.green);
+ constants[count++] = pack_float (channel->linear.color1.blue);
+ constants[count++] = pack_float (channel->linear.color1.alpha);
+ break;
+
+ case FS_RADIAL:
+ for (n = 0; n < ARRAY_LENGTH (channel->radial.constants); n++)
+ constants[count++] = pack_float (channel->radial.constants[n]);
+ break;
+
+ case FS_TEXTURE:
+ case FS_YUV:
+ case FS_SPANS:
+ break;
+ }
+
+ return count;
+}
+
+static void
+i915_set_shader_constants (i915_device_t *device,
+ const i915_shader_t *shader)
+{
+ uint32_t constants[4*4*3+4];
+ unsigned n_constants;
+
+ n_constants = 0;
+ if (shader->source.type.fragment == FS_DIFFUSE) {
+ uint32_t diffuse;
+
+ diffuse =
+ ((shader->source.solid.color.alpha_short >> 8) << 24) |
+ ((shader->source.solid.color.red_short >> 8) << 16) |
+ ((shader->source.solid.color.green_short >> 8) << 8) |
+ ((shader->source.solid.color.blue_short >> 8) << 0);
+
+ if (diffuse != device->current_diffuse) {
+ OUT_DWORD (_3DSTATE_DFLT_DIFFUSE_CMD);
+ OUT_DWORD (diffuse);
+ device->current_diffuse = diffuse;
+ }
+ } else {
+ n_constants += pack_constants (&shader->source, constants + n_constants);
+ }
+ n_constants += pack_constants (&shader->mask, constants + n_constants);
+
+ if (shader->opacity < 1.) {
+ constants[n_constants+0] =
+ constants[n_constants+1] =
+ constants[n_constants+2] =
+ constants[n_constants+3] = pack_float (shader->opacity);
+ n_constants += 4;
+ }
+
+ if (n_constants != 0 &&
+ (device->current_n_constants != n_constants ||
+ memcmp (device->current_constants, constants, n_constants*4)))
+ {
+ i915_set_constants (device, constants, n_constants);
+ }
+}
+
+static cairo_bool_t
+i915_shader_needs_update (const i915_shader_t *shader,
+ const i915_device_t *device)
+{
+ uint32_t count, n;
+ uint32_t buf[64];
+
+ if (device->current_target != shader->target)
+ return TRUE;
+
+ count =
+ !! shader->source.base.bo +
+ !! shader->mask.base.bo +
+ !! shader->clip.base.bo +
+ !! shader->dst.base.bo;
+ if (count > device->current_n_samplers)
+ return TRUE;
+
+ count =
+ shader->source.base.n_samplers +
+ shader->mask.base.n_samplers +
+ shader->clip.base.n_samplers +
+ shader->dst.base.n_samplers;
+ if (count > device->current_n_maps)
+ return TRUE;
+
+ if (count) {
+ count = 0;
+ if (shader->source.base.bo != NULL) {
+ buf[count++] = shader->source.base.sampler[0];
+ buf[count++] = shader->source.base.sampler[1];
+ }
+ if (shader->mask.base.bo != NULL) {
+ buf[count++] = shader->mask.base.sampler[0];
+ buf[count++] = shader->mask.base.sampler[1];
+ }
+ if (shader->clip.base.bo != NULL) {
+ buf[count++] = shader->clip.base.sampler[0];
+ buf[count++] = shader->clip.base.sampler[1];
+ }
+ if (shader->dst.base.bo != NULL) {
+ buf[count++] = shader->dst.base.sampler[0];
+ buf[count++] = shader->dst.base.sampler[1];
+ }
+ if (memcmp (device->current_samplers, buf, count * sizeof (uint32_t)))
+ return TRUE;
+
+ count = 0;
+ if (shader->source.base.bo != NULL) {
+ buf[count++] = shader->source.base.bo->base.handle;
+ for (n = 0; n < shader->source.base.n_samplers; n++) {
+ buf[count++] = shader->source.base.offset[n];
+ buf[count++] = shader->source.base.map[2*n+0];
+ buf[count++] = shader->source.base.map[2*n+1];
+ }
+ }
+ if (shader->mask.base.bo != NULL) {
+ buf[count++] = shader->mask.base.bo->base.handle;
+ for (n = 0; n < shader->mask.base.n_samplers; n++) {
+ buf[count++] = shader->mask.base.offset[n];
+ buf[count++] = shader->mask.base.map[2*n+0];
+ buf[count++] = shader->mask.base.map[2*n+1];
+ }
+ }
+ if (shader->clip.base.bo != NULL) {
+ buf[count++] = shader->clip.base.bo->base.handle;
+ for (n = 0; n < shader->clip.base.n_samplers; n++) {
+ buf[count++] = shader->clip.base.offset[n];
+ buf[count++] = shader->clip.base.map[2*n+0];
+ buf[count++] = shader->clip.base.map[2*n+1];
+ }
+ }
+ if (shader->dst.base.bo != NULL) {
+ buf[count++] = shader->dst.base.bo->base.handle;
+ for (n = 0; n < shader->dst.base.n_samplers; n++) {
+ buf[count++] = shader->dst.base.offset[n];
+ buf[count++] = shader->dst.base.map[2*n+0];
+ buf[count++] = shader->dst.base.map[2*n+1];
+ }
+ }
+ if (memcmp (device->current_maps, buf, count * sizeof (uint32_t)))
+ return TRUE;
+ }
+
+ if (i915_shader_get_texcoords (shader) != device->current_texcoords)
+ return TRUE;
+ if (device->current_blend != shader->blend)
+ return TRUE;
+
+ count = 0;
+ if (shader->source.type.fragment == FS_DIFFUSE) {
+ uint32_t diffuse;
+
+ diffuse =
+ ((shader->source.solid.color.alpha_short >> 8) << 24) |
+ ((shader->source.solid.color.red_short >> 8) << 16) |
+ ((shader->source.solid.color.green_short >> 8) << 8) |
+ ((shader->source.solid.color.blue_short >> 8) << 0);
+
+ if (diffuse != device->current_diffuse)
+ return TRUE;
+ } else {
+ count += pack_constants (&shader->source, buf + count);
+ }
+ count += pack_constants (&shader->mask, buf + count);
+
+ if (count &&
+ (device->current_n_constants != count ||
+ memcmp (device->current_constants, buf, count*4)))
+ {
+ return TRUE;
+ }
+
+ n = (i915_shader_channel_key (&shader->source) << 0) |
+ (i915_shader_channel_key (&shader->mask) << 8) |
+ (i915_shader_channel_key (&shader->clip) << 16) |
+ (shader->op << 24) |
+ ((shader->opacity < 1.) << 30) |
+ (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31);
+ return n != device->current_program;
+}
+
+void
+i915_set_dst (i915_device_t *device, i915_surface_t *dst)
+{
+ uint32_t size;
+
+ if (device->current_target != dst) {
+ intel_bo_t *bo;
+
+ bo = to_intel_bo (dst->intel.drm.bo);
+ assert (bo != NULL);
+
+ OUT_DWORD (_3DSTATE_BUF_INFO_CMD);
+ OUT_DWORD (BUF_3D_ID_COLOR_BACK |
+ BUF_tiling (bo->tiling) |
+ BUF_3D_PITCH (dst->intel.drm.stride));
+ OUT_RELOC (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ device->current_target = dst;
+ }
+
+ if (dst->colorbuf != device->current_colorbuf) {
+ OUT_DWORD (_3DSTATE_DST_BUF_VARS_CMD);
+ OUT_DWORD (dst->colorbuf);
+ device->current_colorbuf = dst->colorbuf;
+ }
+
+ size = DRAW_YMAX (dst->intel.drm.height) | DRAW_XMAX (dst->intel.drm.width);
+ if (size != device->current_size) {
+ OUT_DWORD (_3DSTATE_DRAW_RECT_CMD);
+ OUT_DWORD (0); /* dither */
+ OUT_DWORD (0); /* top-left */
+ OUT_DWORD (size);
+ OUT_DWORD (0); /* origin */
+ device->current_size = size;
+ }
+}
+
+static void
+i915_set_shader_target (i915_device_t *device,
+ const i915_shader_t *shader)
+{
+ i915_set_dst (device, shader->target);
+}
+
+int
+i915_shader_num_texcoords (const i915_shader_t *shader)
+{
+ int cnt = 0;
+
+ switch (shader->source.base.texfmt) {
+ default:
+ ASSERT_NOT_REACHED;
+ case TEXCOORDFMT_NOT_PRESENT: break;
+ case TEXCOORDFMT_2D: cnt += 2; break;
+ case TEXCOORDFMT_3D: cnt += 3; break;
+ case TEXCOORDFMT_4D: cnt += 4; break;
+ case TEXCOORDFMT_1D: cnt += 1; break;
+ case TEXCOORDFMT_2D_16: cnt += 1; break;
+ }
+
+ switch (shader->mask.base.texfmt) {
+ default:
+ ASSERT_NOT_REACHED;
+ case TEXCOORDFMT_NOT_PRESENT: break;
+ case TEXCOORDFMT_2D: cnt += 2; break;
+ case TEXCOORDFMT_3D: cnt += 3; break;
+ case TEXCOORDFMT_4D: cnt += 4; break;
+ case TEXCOORDFMT_1D: cnt += 1; break;
+ case TEXCOORDFMT_2D_16: cnt += 1; break;
+ }
+
+ switch (shader->clip.base.texfmt) {
+ default:
+ ASSERT_NOT_REACHED;
+ case TEXCOORDFMT_NOT_PRESENT: break;
+ case TEXCOORDFMT_2D: cnt += 2; break;
+ case TEXCOORDFMT_3D: cnt += 3; break;
+ case TEXCOORDFMT_4D: cnt += 4; break;
+ case TEXCOORDFMT_1D: cnt += 1; break;
+ case TEXCOORDFMT_2D_16: cnt += 1; break;
+ }
+
+ switch (shader->dst.base.texfmt) {
+ default:
+ ASSERT_NOT_REACHED;
+ case TEXCOORDFMT_NOT_PRESENT: break;
+ case TEXCOORDFMT_2D: cnt += 2; break;
+ case TEXCOORDFMT_3D: cnt += 3; break;
+ case TEXCOORDFMT_4D: cnt += 4; break;
+ case TEXCOORDFMT_1D: cnt += 1; break;
+ case TEXCOORDFMT_2D_16: cnt += 1; break;
+ }
+
+ return cnt;
+}
+
+void
+i915_shader_fini (i915_shader_t *shader)
+{
+ i915_device_t *device = i915_device (shader->target);
+
+ i915_shader_channel_fini (device, &shader->source);
+ i915_shader_channel_fini (device, &shader->mask);
+ i915_shader_channel_fini (device, &shader->clip);
+}
+
+void
+i915_shader_set_clip (i915_shader_t *shader,
+ cairo_clip_t *clip)
+{
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+ union i915_shader_channel *channel;
+ i915_surface_t *s;
+
+ clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y);
+ assert (clip_surface->status == CAIRO_STATUS_SUCCESS);
+ assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM);
+
+ channel = &shader->clip;
+ channel->type.vertex = VS_TEXTURE_16;
+ channel->base.texfmt = TEXCOORDFMT_2D_16;
+ channel->base.content = CAIRO_CONTENT_ALPHA;
+
+ channel->type.fragment = FS_TEXTURE;
+ channel->surface.pixel = NONE;
+
+ s = (i915_surface_t *) clip_surface;
+ channel->base.bo = to_intel_bo (s->intel.drm.bo);
+ channel->base.n_samplers = 1;
+ channel->base.offset[0] = s->offset;
+ channel->base.map[0] = s->map0;
+ channel->base.map[1] = s->map1;
+
+ channel->base.sampler[0] =
+ (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+ i915_texture_filter (CAIRO_FILTER_NEAREST);
+ channel->base.sampler[1] =
+ SS3_NORMALIZED_COORDS |
+ i915_texture_extend (CAIRO_EXTEND_NONE);
+
+ cairo_matrix_init_scale (&shader->clip.base.matrix,
+ 1. / s->intel.drm.width,
+ 1. / s->intel.drm.height);
+ cairo_matrix_translate (&shader->clip.base.matrix,
+ -clip_x, -clip_y);
+}
+
+static cairo_status_t
+i915_shader_check_aperture (i915_shader_t *shader,
+ i915_device_t *device)
+{
+ cairo_status_t status;
+ intel_bo_t *bo_array[4];
+ uint32_t n = 0;
+
+ if (shader->target != device->current_target)
+ bo_array[n++] = to_intel_bo (shader->target->intel.drm.bo);
+
+ if (shader->source.base.bo != NULL)
+ bo_array[n++] = shader->source.base.bo;
+
+ if (shader->mask.base.bo != NULL)
+ bo_array[n++] = shader->mask.base.bo;
+
+ if (shader->clip.base.bo != NULL)
+ bo_array[n++] = shader->clip.base.bo;
+
+ if (n == 0 || i915_check_aperture (device, bo_array, n))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = i915_batch_flush (device);
+ if (unlikely (status))
+ return status;
+
+ assert (i915_check_aperture (device, bo_array, n));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+i915_shader_combine_mask (i915_shader_t *shader, i915_device_t *device)
+{
+ if (shader->mask.type.fragment == (i915_fragment_shader_t) -1 ||
+ shader->mask.type.fragment == FS_CONSTANT)
+ {
+ return;
+ }
+
+ if (shader->mask.type.fragment == FS_PURE) {
+ if (shader->mask.solid.pure & (1<<3)) {
+ shader->mask.type.fragment = FS_ONE;
+ } else {
+ shader->mask.type.fragment = FS_ZERO;
+ }
+ }
+
+ if (shader->mask.type.fragment == FS_ONE ||
+ (shader->mask.base.content & CAIRO_CONTENT_ALPHA) == 0)
+ {
+ i915_shader_channel_reset (device, &shader->mask);
+ }
+
+ if (shader->mask.type.fragment == FS_ZERO) {
+ i915_shader_channel_fini (device, &shader->source);
+
+ shader->source.type.fragment = FS_ZERO;
+ shader->source.type.vertex = VS_ZERO;
+ shader->source.base.texfmt = TEXCOORDFMT_NOT_PRESENT;
+ shader->source.base.mode = 0;
+ shader->source.base.n_samplers = 0;
+ }
+
+ if (shader->source.type.fragment == FS_ZERO) {
+ i915_shader_channel_reset (device, &shader->mask);
+ i915_shader_channel_reset (device, &shader->clip);
+ }
+}
+
+static void
+i915_shader_setup_dst (i915_shader_t *shader)
+{
+ union i915_shader_channel *channel;
+ i915_surface_t *s;
+
+ /* We need to manual blending if we have a clip surface and an unbounded op,
+ * or an extended blend mode.
+ */
+ if (shader->need_combine ||
+ (shader->op < CAIRO_OPERATOR_SATURATE &&
+ (shader->clip.type.fragment == (i915_fragment_shader_t) -1 ||
+ _cairo_operator_bounded_by_mask (shader->op))))
+ {
+ return;
+ }
+
+ shader->need_combine = TRUE;
+
+ channel = &shader->dst;
+ channel->type.vertex = VS_TEXTURE_16;
+ channel->base.texfmt = TEXCOORDFMT_2D_16;
+ channel->base.content = shader->content;
+
+ channel->type.fragment = FS_TEXTURE;
+ channel->surface.pixel = NONE;
+
+ s = shader->target;
+ channel->base.bo = to_intel_bo (s->intel.drm.bo);
+ channel->base.n_samplers = 1;
+ channel->base.offset[0] = s->offset;
+ channel->base.map[0] = s->map0;
+ channel->base.map[1] = s->map1;
+
+ channel->base.sampler[0] =
+ (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+ i915_texture_filter (CAIRO_FILTER_NEAREST);
+ channel->base.sampler[1] =
+ SS3_NORMALIZED_COORDS |
+ i915_texture_extend (CAIRO_EXTEND_NONE);
+
+ cairo_matrix_init_scale (&shader->dst.base.matrix,
+ 1. / s->intel.drm.width,
+ 1. / s->intel.drm.height);
+}
+
+static void
+i915_shader_combine_source (i915_shader_t *shader,
+ i915_device_t *device)
+{
+ if (device->last_source_fragment == shader->source.type.fragment)
+ return;
+
+ if (device->last_source_fragment == FS_DIFFUSE) {
+ switch (shader->source.type.fragment) {
+ case FS_ONE:
+ case FS_PURE:
+ case FS_CONSTANT:
+ case FS_DIFFUSE:
+ shader->source.type.fragment = FS_DIFFUSE;
+ shader->source.base.mode = 0;
+ break;
+ case FS_ZERO:
+ case FS_LINEAR:
+ case FS_RADIAL:
+ case FS_TEXTURE:
+ case FS_YUV:
+ case FS_SPANS:
+ default:
+ break;
+ }
+ }
+
+ device->last_source_fragment = shader->source.type.fragment;
+}
+
+static inline float *
+i915_composite_vertex (float *v,
+ const i915_shader_t *shader,
+ double x, double y)
+{
+ double s, t;
+
+ /* Each vertex is:
+ * 2 vertex coordinates
+ * [0-2] source texture coordinates
+ * [0-2] mask texture coordinates
+ */
+
+ *v++ = x; *v++ = y;
+ switch (shader->source.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *v++ = i915_shader_linear_texcoord (&shader->source.linear, x, y);
+ break;
+ case VS_TEXTURE:
+ s = x, t = y;
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = s; *v++ = t;
+ break;
+ case VS_TEXTURE_16:
+ s = x, t = y;
+ cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+ *v++ = texcoord_2d_16 (s, t);
+ break;
+ }
+ switch (shader->mask.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *v++ = i915_shader_linear_texcoord (&shader->mask.linear, x, y);
+ break;
+ case VS_TEXTURE:
+ s = x, t = y;
+ cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t);
+ *v++ = s; *v++ = t;
+ break;
+ case VS_TEXTURE_16:
+ s = x, t = y;
+ cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t);
+ *v++ = texcoord_2d_16 (s, t);
+ break;
+ }
+
+ return v;
+}
+
+static inline void
+i915_shader_add_rectangle_general (const i915_shader_t *shader,
+ int x, int y,
+ int w, int h)
+{
+ float *vertices;
+
+ vertices = i915_add_rectangle (shader->device);
+ vertices = i915_composite_vertex (vertices, shader, x + w, y + h);
+ vertices = i915_composite_vertex (vertices, shader, x, y + h);
+ vertices = i915_composite_vertex (vertices, shader, x, y);
+ /* XXX overflow! */
+}
+
+void
+i915_vbo_flush (i915_device_t *device)
+{
+ assert (device->floats_per_vertex);
+ assert (device->vertex_count);
+
+ if (device->vbo == 0) {
+ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+ I1_LOAD_S (0) |
+ I1_LOAD_S (1) |
+ 1);
+ device->vbo = device->batch.used++;
+ device->vbo_max_index = device->batch.used;
+ OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+ (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+ }
+
+ OUT_DWORD (PRIM3D_RECTLIST |
+ PRIM3D_INDIRECT_SEQUENTIAL |
+ device->vertex_count);
+ OUT_DWORD (device->vertex_index);
+
+ device->vertex_index += device->vertex_count;
+ device->vertex_count = 0;
+}
+
+cairo_status_t
+i915_shader_commit (i915_shader_t *shader,
+ i915_device_t *device)
+{
+ unsigned floats_per_vertex;
+ cairo_status_t status;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+
+ if (! shader->committed) {
+ device->shader = shader;
+
+ i915_shader_combine_mask (shader, device);
+ i915_shader_combine_source (shader, device);
+ i915_shader_setup_dst (shader);
+
+ shader->add_rectangle = i915_shader_add_rectangle_general;
+
+ if ((status = setjmp (shader->unwind)))
+ return status;
+
+ shader->committed = TRUE;
+ }
+
+ if (i915_shader_needs_update (shader, device)) {
+ if (i915_batch_space (device) < 256) {
+ status = i915_batch_flush (device);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (device->vertex_count)
+ i915_vbo_flush (device);
+
+ status = i915_shader_check_aperture (shader, device);
+ if (unlikely (status))
+ return status;
+
+ update_shader:
+ i915_set_shader_target (device, shader);
+ i915_set_shader_mode (device, shader);
+ i915_set_shader_samplers (device, shader);
+ i915_set_shader_constants (device, shader);
+ i915_set_shader_program (device, shader);
+ }
+
+ floats_per_vertex = 2 + i915_shader_num_texcoords (shader);
+ if (device->floats_per_vertex == floats_per_vertex)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (i915_batch_space (device) < 8) {
+ status = i915_batch_flush (device);
+ if (unlikely (status))
+ return status;
+
+ goto update_shader;
+ }
+
+ if (device->vertex_count)
+ i915_vbo_flush (device);
+
+ if (device->vbo) {
+ device->batch_base[device->vbo_max_index] |= device->vertex_index;
+ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (1) | 0);
+ device->vbo_max_index = device->batch.used;
+ OUT_DWORD ((floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+ (floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+ }
+
+ device->floats_per_vertex = floats_per_vertex;
+ device->rectangle_size = floats_per_vertex * 3 * sizeof (float);
+ device->vertex_index =
+ (device->vbo_used + 4*floats_per_vertex - 1) / (4 * floats_per_vertex);
+ device->vbo_offset = 4 * device->vertex_index * floats_per_vertex;
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/drm/cairo-drm-i915-spans.c b/src/drm/cairo-drm-i915-spans.c
new file mode 100755
index 000000000..b3f4e0afd
--- /dev/null
+++ b/src/drm/cairo-drm-i915-spans.c
@@ -0,0 +1,799 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-drm-i915-private.h"
+
+/* Operates in either immediate or retained mode.
+ * When given a clip region we record the sequence of vbo and then
+ * replay them for each clip rectangle, otherwise we simply emit
+ * the vbo straight into the command stream.
+ */
+
+typedef struct _i915_spans i915_spans_t;
+
+typedef float *
+(*i915_get_rectangle_func_t) (i915_spans_t *spans);
+
+typedef void
+(*i915_span_func_t) (i915_spans_t *spans,
+ int x0, int x1, int y0, int y1,
+ int alpha);
+
+struct _i915_spans {
+ cairo_span_renderer_t renderer;
+
+ i915_device_t *device;
+
+ int xmin, xmax;
+ cairo_bool_t is_bounded;
+ const cairo_rectangle_int_t *extents;
+
+ i915_get_rectangle_func_t get_rectangle;
+ i915_span_func_t span;
+ i915_shader_t shader;
+
+ cairo_region_t *clip_region;
+ cairo_bool_t need_clip_surface;
+
+ struct vbo {
+ struct vbo *next;
+ intel_bo_t *bo;
+ unsigned int count;
+ } head, *tail;
+
+ unsigned int vbo_offset;
+ float *vbo_base;
+};
+
+static float *
+i915_emit_rectangle (i915_spans_t *spans)
+{
+ return i915_add_rectangle (spans->device);
+}
+
+static float *
+i915_accumulate_rectangle (i915_spans_t *spans)
+{
+ float *vertices;
+ uint32_t size;
+
+ size = spans->device->rectangle_size;
+ if (unlikely (spans->vbo_offset + size > I915_VBO_SIZE)) {
+ struct vbo *vbo;
+
+ vbo = malloc (sizeof (struct vbo));
+ if (unlikely (vbo == NULL)) {
+ /* throw error! */
+ }
+
+ spans->tail->next = vbo;
+ spans->tail = vbo;
+
+ vbo->next = NULL;
+ vbo->bo = intel_bo_create (&spans->device->intel,
+ I915_VBO_SIZE, I915_VBO_SIZE,
+ FALSE, I915_TILING_NONE, 0);
+ vbo->count = 0;
+
+ spans->vbo_offset = 0;
+ spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo);
+ }
+
+ vertices = spans->vbo_base + spans->vbo_offset;
+ spans->vbo_offset += size;
+ spans->tail->count += 3;
+
+ return vertices;
+}
+
+static void
+i915_span_zero (i915_spans_t *spans,
+ int x0, int x1, int y0, int y1,
+ int alpha)
+{
+ float *vertices;
+
+ vertices = spans->get_rectangle (spans);
+
+ *vertices++ = x1;
+ *vertices++ = y1;
+
+ *vertices++ = x0;
+ *vertices++ = y1;
+
+ *vertices++ = x0;
+ *vertices++ = y0;
+}
+
+static void
+i915_span_constant (i915_spans_t *spans,
+ int x0, int x1, int y0, int y1,
+ int alpha)
+{
+ float *vertices;
+ float a = alpha / 255.;
+
+ vertices = spans->get_rectangle (spans);
+
+ *vertices++ = x1;
+ *vertices++ = y1;
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y1;
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y0;
+ *vertices++ = a;
+}
+
+static void
+i915_span_linear (i915_spans_t *spans,
+ int x0, int x1, int y0, int y1,
+ int alpha)
+{
+ float *vertices;
+ float a = alpha / 255.;
+ double s, t;
+
+ vertices = spans->get_rectangle (spans);
+
+ *vertices++ = x1;
+ *vertices++ = y1;
+ s = x0, t = y0;
+ *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y1;
+ s = x1, t = y0;
+ *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y0;
+ s = x1, t = y1;
+ *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+ *vertices++ = a;
+}
+
+static void
+i915_span_texture (i915_spans_t *spans,
+ int x0, int x1, int y0, int y1,
+ int alpha)
+{
+ float *vertices;
+ float a = alpha / 255.;
+ double s, t;
+
+ vertices = spans->get_rectangle (spans);
+
+ *vertices++ = x1;
+ *vertices++ = y1;
+ s = x0, t = y0;
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = s; *vertices++ = t;
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y1;
+ s = x1, t = y0;
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = s; *vertices++ = t;
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y0;
+ s = x1, t = y1;
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = s; *vertices++ = t;
+ *vertices++ = a;
+}
+
+static void
+i915_span_texture16 (i915_spans_t *spans,
+ int x0, int x1, int y0, int y1, int alpha)
+{
+ float *vertices;
+ float a = alpha / 255.;
+ double s, t;
+
+ vertices = spans->get_rectangle (spans);
+
+ *vertices++ = x1;
+ *vertices++ = y1;
+ s = x0, t = y0;
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y1;
+ s = x1, t = y0;
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y0;
+ s = x1, t = y1;
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ *vertices++ = a;
+}
+
+static void
+i915_span_generic (i915_spans_t *spans,
+ int x0, int x1, int y0, int y1, int alpha)
+{
+ double s, t;
+ float *vertices;
+ float a = alpha / 255.;
+
+ /* Each vertex is:
+ * 2 vertex coordinates
+ * [0-2] source texture coordinates
+ * 1 alpha value.
+ * [0,2] clip mask coordinates
+ */
+
+ vertices = spans->get_rectangle (spans);
+
+ /* bottom right */
+ *vertices++ = x1; *vertices++ = y1;
+ s = x1, t = y1;
+ switch (spans->shader.source.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+ break;
+ case VS_TEXTURE:
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = s; *vertices++ = t;
+ break;
+ case VS_TEXTURE_16:
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ break;
+ }
+ *vertices++ = a;
+ if (spans->need_clip_surface) {
+ s = x1, t = y1;
+ cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ }
+ if (spans->shader.need_combine) {
+ s = x1, t = y1;
+ cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ }
+
+ /* bottom left */
+ *vertices++ = x0; *vertices++ = y1;
+ s = x0, t = y1;
+ switch (spans->shader.source.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+ break;
+ case VS_TEXTURE:
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = s; *vertices++ = t;
+ break;
+ case VS_TEXTURE_16:
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ break;
+ }
+ *vertices++ = a;
+ if (spans->need_clip_surface) {
+ s = x0, t = y1;
+ cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ }
+ if (spans->shader.need_combine) {
+ s = x0, t = y1;
+ cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ }
+
+ /* top left */
+ *vertices++ = x0; *vertices++ = y0;
+ s = x0, t = y0;
+ switch (spans->shader.source.type.vertex) {
+ case VS_ZERO:
+ case VS_CONSTANT:
+ break;
+ case VS_LINEAR:
+ *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+ break;
+ case VS_TEXTURE:
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = s; *vertices++ = t;
+ break;
+ case VS_TEXTURE_16:
+ cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ break;
+ }
+ *vertices++ = a;
+ if (spans->need_clip_surface) {
+ s = x0, t = y0;
+ cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ }
+ if (spans->shader.need_combine) {
+ s = x0, t = y0;
+ cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t);
+ *vertices++ = texcoord_2d_16 (s, t);
+ }
+}
+
+static cairo_status_t
+i915_zero_spans_mono (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i915_spans_t *spans = abstract_renderer;
+ int x0, x1;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ while (num_spans && half[0].coverage < 128)
+ half++, num_spans--;
+ if (num_spans == 0)
+ break;
+
+ x0 = x1 = half[0].x;
+ while (num_spans--) {
+ half++;
+
+ x1 = half[0].x;
+ if (half[0].coverage < 128)
+ break;
+ }
+
+ i915_span_zero (spans,
+ x0, x1,
+ y, y + height,
+ 0);
+ } while (num_spans);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_zero_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i915_spans_t *spans = abstract_renderer;
+ int x0, x1;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ while (num_spans && half[0].coverage == 0)
+ half++, num_spans--;
+ if (num_spans == 0)
+ break;
+
+ x0 = x1 = half[0].x;
+ while (num_spans--) {
+ half++;
+
+ x1 = half[0].x;
+ if (half[0].coverage == 0)
+ break;
+ }
+
+ i915_span_zero (spans,
+ x0, x1,
+ y, y + height,
+ 0);
+ } while (num_spans);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_bounded_spans_mono (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i915_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (half[0].coverage >= 128) {
+ spans->span (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ 255);
+ }
+ half++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i915_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (half[0].coverage) {
+ spans->span (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ half[0].coverage);
+ }
+ half++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i915_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0) {
+ spans->span (spans,
+ spans->xmin, spans->xmax,
+ y, y + height,
+ 0);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (half[0].x != spans->xmin) {
+ spans->span (spans,
+ spans->xmin, half[0].x,
+ y, y + height,
+ 0);
+ }
+
+ do {
+ spans->span (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ half[0].coverage);
+ half++;
+ } while (--num_spans > 1);
+
+ if (half[0].x != spans->xmax) {
+ spans->span (spans,
+ half[0].x, spans->xmax,
+ y, y + height,
+ 0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_unbounded_spans_mono (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i915_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0) {
+ spans->span (spans,
+ spans->xmin, spans->xmax,
+ y, y + height,
+ 0);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (half[0].x != spans->xmin) {
+ spans->span (spans,
+ spans->xmin, half[0].x,
+ y, y + height,
+ 0);
+ }
+
+ do {
+ int alpha = 0;
+ if (half[0].coverage >= 128)
+ alpha = 255;
+ spans->span (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ alpha);
+ half++;
+ } while (--num_spans > 1);
+
+ if (half[0].x != spans->xmax) {
+ spans->span (spans,
+ half[0].x, spans->xmax,
+ y, y + height,
+ 0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_spans_init (i915_spans_t *spans,
+ i915_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ double opacity,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_status_t status;
+
+ spans->device = (i915_device_t *) dst->intel.drm.base.device;
+
+ spans->is_bounded = extents->is_bounded;
+ if (extents->is_bounded) {
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ spans->renderer.render_rows = i915_bounded_spans_mono;
+ else
+ spans->renderer.render_rows = i915_bounded_spans;
+
+ spans->extents = &extents->bounded;
+ } else {
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ spans->renderer.render_rows = i915_unbounded_spans_mono;
+ else
+ spans->renderer.render_rows = i915_unbounded_spans;
+
+ spans->extents = &extents->unbounded;
+ }
+ spans->xmin = spans->extents->x;
+ spans->xmax = spans->extents->x + spans->extents->width;
+
+ spans->clip_region = NULL;
+ spans->need_clip_surface = FALSE;
+ if (clip != NULL) {
+ cairo_region_t *clip_region = NULL;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+
+ spans->clip_region = clip_region;
+ spans->need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ spans->head.next = NULL;
+ spans->head.bo = NULL;
+ spans->head.count = 0;
+ spans->tail = &spans->head;
+
+ if (spans->clip_region == NULL) {
+ spans->get_rectangle = i915_emit_rectangle;
+ } else {
+ assert (! extents->is_bounded);
+ spans->get_rectangle = i915_accumulate_rectangle;
+ spans->head.bo = intel_bo_create (&spans->device->intel,
+ I915_VBO_SIZE, I915_VBO_SIZE,
+ FALSE, I915_TILING_NONE, 0);
+ if (unlikely (spans->head.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo);
+ }
+ spans->vbo_offset = 0;
+
+ i915_shader_init (&spans->shader, dst, op, opacity);
+ if (spans->need_clip_surface)
+ i915_shader_set_clip (&spans->shader, clip);
+
+ status = i915_shader_acquire_pattern (&spans->shader, &spans->shader.source,
+ pattern, &extents->bounded);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+i915_spans_fini (i915_spans_t *spans)
+{
+ i915_shader_fini (&spans->shader);
+
+ if (spans->head.bo != NULL) {
+ struct vbo *vbo, *next;
+
+ intel_bo_destroy (&spans->device->intel, spans->head.bo);
+ for (vbo = spans->head.next; vbo != NULL; vbo = next) {
+ next = vbo->next;
+ intel_bo_destroy (&spans->device->intel, vbo->bo);
+ free (vbo);
+ }
+ }
+}
+
+cairo_status_t
+i915_clip_and_composite_spans (i915_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_antialias_t antialias,
+ i915_spans_func_t draw_func,
+ void *draw_closure,
+ const cairo_composite_rectangles_t*extents,
+ cairo_clip_t *clip,
+ double opacity)
+{
+ i915_spans_t spans;
+ i915_device_t *device;
+ cairo_status_t status;
+ struct vbo *vbo;
+
+ if (i915_surface_needs_tiling (dst)) {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ pattern = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ status = i915_spans_init (&spans, dst, op, pattern, antialias, clip, opacity, extents);
+ if (unlikely (status))
+ return status;
+
+ spans.shader.mask.base.texfmt = TEXCOORDFMT_1D;
+ spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA;
+ spans.shader.mask.type.fragment = FS_SPANS;
+
+ status = cairo_device_acquire (dst->intel.drm.base.device);
+ if (unlikely (status))
+ goto CLEANUP_SPANS;
+
+ if (dst->deferred_clear) {
+ status = i915_surface_clear (dst);
+ if (unlikely (status))
+ goto CLEANUP_SPANS;
+ }
+
+ device = i915_device (dst);
+ status = i915_shader_commit (&spans.shader, device);
+ if (unlikely (status))
+ goto CLEANUP_DEVICE;
+
+ if (! spans.shader.need_combine && ! spans.need_clip_surface) {
+ switch (spans.shader.source.type.vertex) {
+ case VS_ZERO:
+ spans.span = i915_span_zero;
+ if (extents->is_bounded) {
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ spans.renderer.render_rows = i915_zero_spans_mono;
+ else
+ spans.renderer.render_rows = i915_zero_spans;
+ }
+ break;
+ case VS_CONSTANT:
+ spans.span = i915_span_constant;
+ break;
+ case VS_LINEAR:
+ spans.span = i915_span_linear;
+ break;
+ case VS_TEXTURE:
+ spans.span = i915_span_texture;
+ break;
+ case VS_TEXTURE_16:
+ spans.span = i915_span_texture16;
+ break;
+ default:
+ spans.span = i915_span_generic;
+ break;
+ }
+ } else {
+ spans.span = i915_span_generic;
+ }
+
+ status = draw_func (draw_closure, &spans.renderer, spans.extents);
+ if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS) {
+ i915_vbo_finish (device);
+
+ OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | ENABLE_SCISSOR_RECT);
+ for (vbo = &spans.head; vbo != NULL; vbo = vbo->next) {
+ int i, num_rectangles;
+
+ /* XXX require_space & batch_flush */
+
+ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1);
+ i915_batch_emit_reloc (device, vbo->bo, 0,
+ I915_GEM_DOMAIN_VERTEX, 0,
+ FALSE);
+ OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+ (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT) |
+ vbo->count);
+
+ num_rectangles = cairo_region_num_rectangles (spans.clip_region);
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (spans.clip_region, i, &rect);
+
+ OUT_DWORD (_3DSTATE_SCISSOR_RECT_0_CMD);
+ OUT_DWORD (SCISSOR_RECT_0_XMIN (rect.x) |
+ SCISSOR_RECT_0_YMIN (rect.y));
+ OUT_DWORD (SCISSOR_RECT_0_XMAX (rect.x + rect.width) |
+ SCISSOR_RECT_0_YMAX (rect.y + rect.height));
+
+ OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | vbo->count);
+ OUT_DWORD (0);
+ }
+ }
+ OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT);
+ }
+
+CLEANUP_DEVICE:
+ cairo_device_release (dst->intel.drm.base.device);
+CLEANUP_SPANS:
+ i915_spans_fini (&spans);
+
+ return status;
+}
diff --git a/src/drm/cairo-drm-i915-surface.c b/src/drm/cairo-drm-i915-surface.c
new file mode 100755
index 000000000..c1a04529f
--- /dev/null
+++ b/src/drm/cairo-drm-i915-surface.c
@@ -0,0 +1,2942 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * **************************************************************************
+ * This work was initially based upon xf86-video-intel/src/i915_render.c:
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Wang Zhenyu <zhenyu.z.wang@intel.com>
+ * Eric Anholt <eric@anholt.net>
+ *
+ * **************************************************************************
+ * and also upon libdrm/intel/intel_bufmgr_gem.c:
+ * Copyright © 2007 Red Hat Inc.
+ * Copyright © 2007 Intel Corporation
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ * Keith Whitwell <keithw-at-tungstengraphics-dot-com>
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ */
+
+/* XXX
+ *
+ * - Per thread context? Would it actually avoid many locks?
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-command-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+#include "cairo-drm-i915-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-cache-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+static const uint32_t i915_batch_setup[] = {
+ /* Disable line anti-aliasing */
+ _3DSTATE_AA_CMD,
+
+ /* Disable independent alpha blend */
+ _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD |
+ IAB_MODIFY_ENABLE |
+ IAB_MODIFY_FUNC | (BLENDFUNC_ADD << IAB_FUNC_SHIFT) |
+ IAB_MODIFY_SRC_FACTOR | (BLENDFACT_ONE << IAB_SRC_FACTOR_SHIFT) |
+ IAB_MODIFY_DST_FACTOR | (BLENDFACT_ZERO << IAB_DST_FACTOR_SHIFT),
+
+ /* Disable texture crossbar */
+ _3DSTATE_COORD_SET_BINDINGS |
+ CSB_TCB (0, 0) |
+ CSB_TCB (1, 1) |
+ CSB_TCB (2, 2) |
+ CSB_TCB (3, 3) |
+ CSB_TCB (4, 4) |
+ CSB_TCB (5, 5) |
+ CSB_TCB (6, 6) |
+ CSB_TCB (7, 7),
+
+ _3DSTATE_MODES_4_CMD | ENABLE_LOGIC_OP_FUNC | LOGIC_OP_FUNC (LOGICOP_COPY),
+
+ _3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+ I1_LOAD_S (2) |
+ I1_LOAD_S (3) |
+ I1_LOAD_S (4) |
+ I1_LOAD_S (5) |
+ I1_LOAD_S (6) |
+ 4,
+ S2_TEXCOORD_NONE,
+ 0, /* Disable texture coordinate wrap-shortest */
+ (1 << S4_POINT_WIDTH_SHIFT) |
+ S4_LINE_WIDTH_ONE |
+ S4_FLATSHADE_ALPHA |
+ S4_FLATSHADE_FOG |
+ S4_FLATSHADE_SPECULAR |
+ S4_FLATSHADE_COLOR |
+ S4_CULLMODE_NONE |
+ S4_VFMT_XY,
+ 0, /* Disable stencil buffer */
+ S6_COLOR_WRITE_ENABLE,
+
+ _3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT,
+
+ /* disable indirect state */
+ _3DSTATE_LOAD_INDIRECT,
+ 0,
+};
+
+static const cairo_surface_backend_t i915_surface_backend;
+
+static cairo_surface_t *
+i915_surface_create_from_cacheable_image (cairo_drm_device_t *base_dev,
+ cairo_surface_t *source);
+
+static cairo_status_t
+i915_bo_exec (i915_device_t *device, intel_bo_t *bo, uint32_t offset)
+{
+ struct drm_i915_gem_execbuffer2 execbuf;
+ int ret, cnt, i;
+
+ /* Add the batch buffer to the validation list. */
+ cnt = device->batch.exec_count;
+ if (cnt > 0 && bo->base.handle == device->batch.exec[cnt-1].handle)
+ i = cnt - 1;
+ else
+ i = device->batch.exec_count++;
+ device->batch.exec[i].handle = bo->base.handle;
+ device->batch.exec[i].relocation_count = device->batch.reloc_count;
+ device->batch.exec[i].relocs_ptr = (uintptr_t) device->batch.reloc;
+ device->batch.exec[i].alignment = 0;
+ device->batch.exec[i].offset = 0;
+ device->batch.exec[i].flags = 0;
+ device->batch.exec[i].rsvd1 = 0;
+ device->batch.exec[i].rsvd2 = 0;
+
+ execbuf.buffers_ptr = (uintptr_t) device->batch.exec;
+ execbuf.buffer_count = device->batch.exec_count;
+ execbuf.batch_start_offset = offset;
+ execbuf.batch_len = (device->batch.used << 2) + sizeof (device->batch_header);
+ execbuf.DR1 = 0;
+ execbuf.DR4 = 0;
+ execbuf.num_cliprects = 0;
+ execbuf.cliprects_ptr = 0;
+ execbuf.flags = 0;
+ execbuf.rsvd1 = 0;
+ execbuf.rsvd2 = 0;
+
+ do {
+ ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
+ } while (ret != 0 && errno == EINTR);
+
+ if (device->debug & I915_DEBUG_SYNC && ret == 0)
+ ret = ! intel_bo_wait (&device->intel, bo);
+
+ if (0 && ret) {
+ int n, m;
+
+ fprintf (stderr, "Batch submission failed: %d\n", errno);
+ fprintf (stderr, " relocation entries: %d/%d\n",
+ device->batch.reloc_count, I915_MAX_RELOCS);
+ fprintf (stderr, " gtt size: (%zd/%zd), (%zd/%zd)\n",
+ device->batch.est_gtt_size, device->batch.gtt_avail_size,
+ device->batch.total_gtt_size, device->intel.gtt_avail_size);
+
+ fprintf (stderr, " buffers:\n");
+ for (n = 0; n < device->batch.exec_count; n++) {
+ fprintf (stderr, " exec[%d] = %d, %d/%d bytes, gtt = %qx\n",
+ n,
+ device->batch.exec[n].handle,
+ n == device->batch.exec_count - 1 ? bo->base.size : device->batch.target_bo[n]->base.size,
+ n == device->batch.exec_count - 1 ? bo->full_size : device->batch.target_bo[n]->full_size,
+ device->batch.exec[n].offset);
+ }
+ for (n = 0; n < device->batch.reloc_count; n++) {
+ for (m = 0; m < device->batch.exec_count; m++)
+ if (device->batch.exec[m].handle == device->batch.reloc[n].target_handle)
+ break;
+
+ fprintf (stderr, " reloc[%d] = %d @ %qx -> %qx + %qx\n", n,
+ device->batch.reloc[n].target_handle,
+ device->batch.reloc[n].offset,
+ (unsigned long long) device->batch.exec[m].offset,
+ (unsigned long long) device->batch.reloc[n].delta);
+
+ device->batch_base[(device->batch.reloc[n].offset - sizeof (device->batch_header)) / 4] =
+ device->batch.exec[m].offset + device->batch.reloc[n].delta;
+ }
+
+ intel_dump_batchbuffer (device->batch_header,
+ execbuf.batch_len,
+ device->intel.base.chip_id);
+ }
+ assert (ret == 0);
+
+ VG (VALGRIND_MAKE_MEM_DEFINED (device->batch.exec, sizeof (device->batch.exec[0]) * i));
+
+ bo->offset = device->batch.exec[i].offset;
+ bo->busy = TRUE;
+ if (bo->virtual)
+ intel_bo_unmap (bo);
+ bo->cpu = FALSE;
+
+ while (cnt--) {
+ intel_bo_t *bo = device->batch.target_bo[cnt];
+
+ bo->offset = device->batch.exec[cnt].offset;
+ bo->exec = NULL;
+ bo->busy = TRUE;
+ bo->batch_read_domains = 0;
+ bo->batch_write_domain = 0;
+ cairo_list_del (&bo->cache_list);
+
+ if (bo->virtual)
+ intel_bo_unmap (bo);
+ bo->cpu = FALSE;
+
+ intel_bo_destroy (&device->intel, bo);
+ }
+ assert (cairo_list_is_empty (&device->intel.bo_in_flight));
+
+ device->batch.exec_count = 0;
+ device->batch.reloc_count = 0;
+ device->batch.fences = 0;
+
+ device->batch.est_gtt_size = I915_BATCH_SIZE;
+ device->batch.total_gtt_size = I915_BATCH_SIZE;
+
+ return ret == 0 ? CAIRO_STATUS_SUCCESS : _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+void
+i915_batch_add_reloc (i915_device_t *device,
+ uint32_t pos,
+ intel_bo_t *bo,
+ uint32_t offset,
+ uint32_t read_domains,
+ uint32_t write_domain,
+ cairo_bool_t needs_fence)
+{
+ int index;
+
+ assert (offset < bo->base.size);
+
+ if (bo->exec == NULL) {
+ device->batch.total_gtt_size += bo->base.size;
+
+ if (! bo->busy)
+ device->batch.est_gtt_size += bo->base.size;
+
+ assert (device->batch.exec_count < ARRAY_LENGTH (device->batch.exec));
+
+ index = device->batch.exec_count++;
+ device->batch.exec[index].handle = bo->base.handle;
+ device->batch.exec[index].relocation_count = 0;
+ device->batch.exec[index].relocs_ptr = 0;
+ device->batch.exec[index].alignment = 0;
+ device->batch.exec[index].offset = 0;
+ device->batch.exec[index].flags = 0;
+ device->batch.exec[index].rsvd1 = 0;
+ device->batch.exec[index].rsvd2 = 0;
+
+ device->batch.target_bo[index] = intel_bo_reference (bo);
+
+ bo->exec = &device->batch.exec[index];
+ }
+
+ if (bo->tiling != I915_TILING_NONE) {
+ uint32_t alignment;
+
+#if 0
+ /* We presume that we will want to use a fence with X tiled objects... */
+ if (needs_fence || bo->tiling == I915_TILING_X)
+ alignment = bo->full_size;
+ else
+ alignment = 2*((bo->stride + 4095) & -4096);
+#else
+ alignment = bo->full_size;
+#endif
+ if (bo->exec->alignment < alignment)
+ bo->exec->alignment = alignment;
+
+ if (needs_fence && (bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) {
+ bo->exec->flags |= EXEC_OBJECT_NEEDS_FENCE;
+ device->batch.fences++;
+
+ intel_bo_set_tiling (&device->intel, bo);
+ }
+ }
+
+ assert (device->batch.reloc_count < ARRAY_LENGTH (device->batch.reloc));
+
+ index = device->batch.reloc_count++;
+ device->batch.reloc[index].offset = (pos << 2) + sizeof (device->batch_header);
+ device->batch.reloc[index].delta = offset;
+ device->batch.reloc[index].target_handle = bo->base.handle;
+ device->batch.reloc[index].read_domains = read_domains;
+ device->batch.reloc[index].write_domain = write_domain;
+ device->batch.reloc[index].presumed_offset = bo->offset;
+
+ assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain);
+ bo->batch_read_domains |= read_domains;
+ bo->batch_write_domain |= write_domain;
+}
+
+void
+i915_vbo_finish (i915_device_t *device)
+{
+ intel_bo_t *vbo;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+ assert (device->vbo_used);
+
+ if (device->vertex_count) {
+ if (device->vbo == 0) {
+ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+ I1_LOAD_S (0) |
+ I1_LOAD_S (1) |
+ 1);
+ device->vbo = device->batch.used++;
+ device->vbo_max_index = device->batch.used;
+ OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+ (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+ }
+
+ OUT_DWORD (PRIM3D_RECTLIST |
+ PRIM3D_INDIRECT_SEQUENTIAL |
+ device->vertex_count);
+ OUT_DWORD (device->vertex_index);
+ }
+
+ if (device->last_vbo != NULL) {
+ intel_bo_in_flight_add (&device->intel, device->last_vbo);
+ intel_bo_destroy (&device->intel, device->last_vbo);
+ }
+
+ device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count;
+
+ /* will include a few bytes of inter-array padding */
+ vbo = intel_bo_create (&device->intel,
+ device->vbo_used, device->vbo_used,
+ FALSE, I915_TILING_NONE, 0);
+ i915_batch_fill_reloc (device, device->vbo, vbo, 0,
+ I915_GEM_DOMAIN_VERTEX, 0);
+ intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base);
+ device->last_vbo = vbo;
+ device->last_vbo_offset = (device->vbo_used+7)&-8;
+ device->last_vbo_space = vbo->base.size - device->last_vbo_offset;
+
+ device->vbo = 0;
+
+ device->vbo_used = device->vbo_offset = 0;
+ device->vertex_index = device->vertex_count = 0;
+
+ if (! i915_check_aperture_size (device, 1, I915_VBO_SIZE, I915_VBO_SIZE)) {
+ cairo_status_t status;
+
+ status = i915_batch_flush (device);
+ if (unlikely (status))
+ longjmp (device->shader->unwind, status);
+
+ status = i915_shader_commit (device->shader, device);
+ if (unlikely (status))
+ longjmp (device->shader->unwind, status);
+ }
+}
+
+/* XXX improve state tracker/difference and flush state on vertex emission */
+static void
+i915_device_reset (i915_device_t *device)
+{
+ if (device->current_source != NULL)
+ *device->current_source = 0;
+ if (device->current_mask != NULL)
+ *device->current_mask = 0;
+ if (device->current_clip != NULL)
+ *device->current_clip = 0;
+
+ device->current_target = NULL;
+ device->current_size = 0;
+ device->current_source = NULL;
+ device->current_mask = NULL;
+ device->current_clip = NULL;
+ device->current_texcoords = ~0;
+ device->current_blend = 0;
+ device->current_n_constants = 0;
+ device->current_n_samplers = 0;
+ device->current_n_maps = 0;
+ device->current_colorbuf = 0;
+ device->current_diffuse = 0;
+ device->current_program = ~0;
+ device->clear_alpha = ~0;
+
+ device->last_source_fragment = ~0;
+}
+
+static void
+i915_batch_cleanup (i915_device_t *device)
+{
+ int i;
+
+ for (i = 0; i < device->batch.exec_count; i++) {
+ intel_bo_t *bo = device->batch.target_bo[i];
+
+ bo->exec = NULL;
+ bo->batch_read_domains = 0;
+ bo->batch_write_domain = 0;
+ cairo_list_del (&bo->cache_list);
+
+ intel_bo_destroy (&device->intel, bo);
+ }
+
+ device->batch.exec_count = 0;
+ device->batch.reloc_count = 0;
+}
+
+static void
+i915_batch_vbo_finish (i915_device_t *device)
+{
+ assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+
+ if (device->vbo || i915_batch_space (device) < (int32_t) device->vbo_used) {
+ intel_bo_t *vbo;
+
+ if (device->vertex_count) {
+ if (device->vbo == 0) {
+ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+ I1_LOAD_S (0) |
+ I1_LOAD_S (1) |
+ 1);
+ device->vbo = device->batch.used++;
+ device->vbo_max_index = device->batch.used;
+ OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+ (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+ }
+
+ OUT_DWORD (PRIM3D_RECTLIST |
+ PRIM3D_INDIRECT_SEQUENTIAL |
+ device->vertex_count);
+ OUT_DWORD (device->vertex_index);
+ }
+
+ if (device->last_vbo != NULL)
+ intel_bo_destroy (&device->intel, device->last_vbo);
+
+ device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count;
+
+ /* will include a few bytes of inter-array padding */
+ vbo = intel_bo_create (&device->intel,
+ device->vbo_used, device->vbo_used,
+ FALSE, I915_TILING_NONE, 0);
+ i915_batch_fill_reloc (device, device->vbo,
+ vbo, 0,
+ I915_GEM_DOMAIN_VERTEX, 0);
+ intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base);
+ device->last_vbo = vbo;
+ device->last_vbo_offset = (device->vbo_used+7)&-8;
+ device->last_vbo_space = vbo->base.size - device->last_vbo_offset;
+
+ device->vbo = 0;
+ }
+ else
+ {
+ /* Only a single rectlist in this batch, and no active vertex buffer. */
+ OUT_DWORD (PRIM3D_RECTLIST | (device->vbo_used / 4 - 1));
+
+ memcpy (BATCH_PTR (device), device->vbo_base, device->vbo_used);
+ device->batch.used += device->vbo_used >> 2;
+ }
+
+ device->vbo_used = device->vbo_offset = 0;
+ device->vertex_index = device->vertex_count = 0;
+}
+
+cairo_status_t
+i915_batch_flush (i915_device_t *device)
+{
+ intel_bo_t *batch;
+ cairo_status_t status;
+ uint32_t length, offset;
+ int n;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+
+ if (device->vbo_used)
+ i915_batch_vbo_finish (device);
+
+ if (device->batch.used == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ i915_batch_emit_dword (device, MI_BATCH_BUFFER_END);
+ if ((device->batch.used & 1) != ((sizeof (device->batch_header)>>2) & 1))
+ i915_batch_emit_dword (device, MI_NOOP);
+
+ length = (device->batch.used << 2) + sizeof (device->batch_header);
+
+ /* NB: it is faster to copy the data then map/unmap the batch,
+ * presumably because we frequently only use a small part of the buffer.
+ */
+ batch = NULL;
+ if (device->last_vbo) {
+ if (length <= device->last_vbo_space) {
+ batch = device->last_vbo;
+ offset = device->last_vbo_offset;
+
+ /* fixup the relocations */
+ for (n = 0; n < device->batch.reloc_count; n++)
+ device->batch.reloc[n].offset += offset;
+ } else
+ intel_bo_destroy (&device->intel, device->last_vbo);
+ device->last_vbo = NULL;
+ }
+ if (batch == NULL) {
+ batch = intel_bo_create (&device->intel,
+ length, length,
+ FALSE, I915_TILING_NONE, 0);
+ if (unlikely (batch == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ i915_batch_cleanup (device);
+ goto BAIL;
+ }
+
+ offset = 0;
+ }
+ intel_bo_write (&device->intel, batch, offset, length, device->batch_header);
+ status = i915_bo_exec (device, batch, offset);
+ intel_bo_destroy (&device->intel, batch);
+
+BAIL:
+ device->batch.used = 0;
+
+ intel_glyph_cache_unpin (&device->intel);
+ intel_snapshot_cache_thaw (&device->intel);
+
+ i915_device_reset (device);
+
+ return status;
+}
+
+#if 0
+static float *
+i915_add_rectangles (i915_device_t *device, int num_rects, int *count)
+{
+ float *vertices;
+ uint32_t size;
+ int cnt;
+
+ assert (device->floats_per_vertex);
+
+ size = device->rectangle_size;
+ if (unlikely (device->vbo_offset + size > I915_VBO_SIZE))
+ i915_vbo_finish (device);
+
+ vertices = (float *) (device->vbo_base + device->vbo_offset);
+ cnt = (I915_VBO_SIZE - device->vbo_offset) / size;
+ if (cnt > num_rects)
+ cnt = num_rects;
+ device->vbo_used = device->vbo_offset += size * cnt;
+ device->vertex_count += 3 * cnt;
+ *count = cnt;
+ return vertices;
+}
+#endif
+
+static cairo_surface_t *
+i915_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width, int height)
+{
+ i915_surface_t *other;
+ cairo_format_t format;
+ uint32_t tiling = I915_TILING_DEFAULT;
+
+ other = abstract_other;
+ if (content == other->intel.drm.base.content)
+ format = other->intel.drm.format;
+ else
+ format = _cairo_format_from_content (content);
+
+ if (width * _cairo_format_bits_per_pixel (format) > 8 * 32*1024 || height > 64*1024)
+ return NULL;
+
+ /* we presume that a similar surface will be used for blitting */
+ if (i915_surface_needs_tiling (other))
+ tiling = I915_TILING_X;
+
+ return i915_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device,
+ format,
+ width, height,
+ tiling, TRUE);
+}
+
+static cairo_status_t
+i915_surface_finish (void *abstract_surface)
+{
+ i915_surface_t *surface = abstract_surface;
+ i915_device_t *device = i915_device (surface);
+
+ if (surface->stencil != NULL) {
+ intel_bo_in_flight_add (&device->intel, surface->stencil);
+ intel_bo_destroy (&device->intel, surface->stencil);
+ }
+
+ if (surface->is_current_texture) {
+ if (surface->is_current_texture & CURRENT_SOURCE)
+ device->current_source = NULL;
+ if (surface->is_current_texture & CURRENT_MASK)
+ device->current_mask = NULL;
+ if (surface->is_current_texture & CURRENT_CLIP)
+ device->current_clip = NULL;
+ device->current_n_samplers = 0;
+ }
+
+ if (surface == device->current_target)
+ device->current_target = NULL;
+
+ if (surface->cache != NULL) {
+ i915_image_private_t *node = surface->cache;
+ intel_buffer_cache_t *cache = node->container;
+
+ if (--cache->ref_count == 0) {
+ intel_bo_in_flight_add (&device->intel, cache->buffer.bo);
+ intel_bo_destroy (&device->intel, cache->buffer.bo);
+ _cairo_rtree_fini (&cache->rtree);
+ cairo_list_del (&cache->link);
+ free (cache);
+ } else {
+ node->node.state = CAIRO_RTREE_NODE_AVAILABLE;
+ cairo_list_move (&node->node.link, &cache->rtree.available);
+ _cairo_rtree_node_collapse (&cache->rtree, node->node.parent);
+ }
+ }
+
+ return intel_surface_finish (&surface->intel);
+}
+
+static cairo_status_t
+i915_surface_batch_flush (i915_surface_t *surface)
+{
+ cairo_status_t status;
+ intel_bo_t *bo;
+
+ assert (surface->intel.drm.fallback == NULL);
+
+ bo = to_intel_bo (surface->intel.drm.bo);
+ if (bo == NULL || bo->batch_write_domain == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = cairo_device_acquire (surface->intel.drm.base.device);
+ if (unlikely (status))
+ return status;
+
+ status = i915_batch_flush (i915_device (surface));
+ cairo_device_release (surface->intel.drm.base.device);
+
+ return status;
+}
+
+static cairo_status_t
+i915_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ i915_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->intel.drm.fallback == NULL) {
+ if (surface->intel.drm.base.finished) {
+ /* Forgo flushing on finish as the user cannot access the surface directly. */
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (surface->deferred_clear) {
+ status = i915_surface_clear (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ return i915_surface_batch_flush (surface);
+ }
+
+ return intel_surface_flush (abstract_surface, flags);
+}
+
+/* rasterisation */
+
+static cairo_status_t
+_composite_boxes_spans (void *closure,
+ cairo_span_renderer_t *renderer,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_boxes_t *boxes = closure;
+ cairo_rectangular_scan_converter_t converter;
+ struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ int i;
+
+ _cairo_rectangular_scan_converter_init (&converter, extents);
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+ }
+
+ status = converter.base.generate (&converter.base, renderer);
+
+CLEANUP:
+ converter.base.destroy (&converter.base);
+ return status;
+}
+
+cairo_status_t
+i915_fixup_unbounded (i915_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ i915_shader_t shader;
+ i915_device_t *device;
+ cairo_status_t status;
+
+ if (clip != NULL) {
+ cairo_region_t *clip_region = NULL;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+ assert (clip_region == NULL);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ clip = NULL;
+ } else {
+ if (extents->bounded.width == extents->unbounded.width &&
+ extents->bounded.height == extents->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ if (clip != NULL) {
+ i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.);
+ i915_shader_set_clip (&shader, clip);
+ status = i915_shader_acquire_pattern (&shader,
+ &shader.source,
+ &_cairo_pattern_white.base,
+ &extents->unbounded);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ } else {
+ i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.);
+ status = i915_shader_acquire_pattern (&shader,
+ &shader.source,
+ &_cairo_pattern_clear.base,
+ &extents->unbounded);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ device = i915_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ return status;
+
+ status = i915_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+ shader.add_rectangle (&shader,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->unbounded.width,
+ extents->unbounded.height);
+ } else {
+ /* top */
+ if (extents->bounded.y != extents->unbounded.y) {
+ shader.add_rectangle (&shader,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->unbounded.width,
+ extents->bounded.y - extents->unbounded.y);
+ }
+
+ /* left */
+ if (extents->bounded.x != extents->unbounded.x) {
+ shader.add_rectangle (&shader,
+ extents->unbounded.x,
+ extents->bounded.y,
+ extents->bounded.x - extents->unbounded.x,
+ extents->bounded.height);
+ }
+
+ /* right */
+ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+ shader.add_rectangle (&shader,
+ extents->bounded.x + extents->bounded.width,
+ extents->bounded.y,
+ extents->unbounded.x + extents->unbounded.width - (extents->bounded.x + extents->bounded.width),
+ extents->bounded.height);
+ }
+
+ /* bottom */
+ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+ shader.add_rectangle (&shader,
+ extents->unbounded.x,
+ extents->bounded.y + extents->bounded.height,
+ extents->unbounded.width,
+ extents->unbounded.y + extents->unbounded.height - (extents->bounded.y + extents->bounded.height));
+ }
+ }
+
+ i915_shader_fini (&shader);
+ BAIL:
+ cairo_device_release (&device->intel.base.base);
+ return status;
+}
+
+static cairo_status_t
+i915_fixup_unbounded_boxes (i915_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip,
+ cairo_boxes_t *boxes)
+{
+ cairo_boxes_t clear;
+ cairo_box_t box;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ if (boxes->num_boxes <= 1)
+ return i915_fixup_unbounded (dst, extents, clip);
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ clip = NULL;
+ }
+
+ if (clip_region == NULL) {
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+
+ tmp.chunks.next = NULL;
+ } else {
+ pixman_box32_t *pbox;
+
+ pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+ _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+ status = _cairo_boxes_add (&clear, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (&clear, &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&clear);
+ return status;
+ }
+ }
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ }
+
+ if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) {
+ i915_shader_t shader;
+ i915_device_t *device;
+
+ if (clip != NULL) {
+ i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.);
+ i915_shader_set_clip (&shader, clip);
+ status = i915_shader_acquire_pattern (&shader,
+ &shader.source,
+ &_cairo_pattern_white.base,
+ &extents->unbounded);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ } else {
+ i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.);
+ status = i915_shader_acquire_pattern (&shader,
+ &shader.source,
+ &_cairo_pattern_clear.base,
+ &extents->unbounded);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ device = i915_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i915_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto err_device;
+
+ for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+ }
+ }
+err_device:
+ cairo_device_release (&device->intel.base.base);
+err_shader:
+ i915_shader_fini (&shader);
+ }
+
+ _cairo_boxes_fini (&clear);
+
+ return status;
+}
+
+static cairo_bool_t
+i915_can_blt (i915_surface_t *dst,
+ const cairo_pattern_t *pattern)
+{
+ const cairo_surface_pattern_t *spattern;
+ i915_surface_t *src;
+
+ spattern = (const cairo_surface_pattern_t *) pattern;
+ src = (i915_surface_t *) spattern->surface;
+
+ if (src->intel.drm.base.device != dst->intel.drm.base.device)
+ return FALSE;
+
+ if (! i915_surface_needs_tiling (dst))
+ return FALSE;
+
+ if (! _cairo_matrix_is_translation (&pattern->matrix))
+ return FALSE;
+
+ if (! (pattern->filter == CAIRO_FILTER_NEAREST ||
+ pattern->filter == CAIRO_FILTER_FAST))
+ {
+ if (! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.x0)) ||
+ ! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.y0)))
+ {
+ return FALSE;
+ }
+ }
+
+ return _cairo_format_bits_per_pixel (src->intel.drm.format) ==
+ _cairo_format_bits_per_pixel (dst->intel.drm.format);
+}
+
+static cairo_status_t
+i915_blt (i915_surface_t *src,
+ i915_surface_t *dst,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y,
+ cairo_bool_t flush)
+{
+ i915_device_t *device;
+ intel_bo_t *bo_array[2];
+ cairo_status_t status;
+ int br13, cmd;
+
+ bo_array[0] = to_intel_bo (dst->intel.drm.bo);
+ bo_array[1] = to_intel_bo (src->intel.drm.bo);
+
+ status = i915_surface_fallback_flush (src);
+ if (unlikely (status))
+ return status;
+
+ device = i915_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ return status;
+
+ if (! i915_check_aperture_and_fences (device, bo_array, 2) ||
+ i915_batch_space (device) < 9)
+ {
+ status = i915_batch_flush (device);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+
+ cmd = XY_SRC_COPY_BLT_CMD;
+ br13 = (0xCC << 16) | dst->intel.drm.stride;
+ switch (dst->intel.drm.format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_A8:
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ br13 |= BR13_565;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_ARGB32:
+ br13 |= BR13_8888;
+ cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+ break;
+ }
+
+ OUT_DWORD (cmd);
+ OUT_DWORD (br13);
+ OUT_DWORD ((dst_y << 16) | dst_x);
+ OUT_DWORD (((dst_y + height - 1) << 16) | (dst_x + width - 1));
+ OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+ OUT_DWORD ((src_y << 16) | src_x);
+ OUT_DWORD (src->intel.drm.stride);
+ OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0);
+ /* require explicit RenderCache flush for 2D -> 3D sampler? */
+ if (flush)
+ OUT_DWORD (MI_FLUSH);
+
+CLEANUP:
+ cairo_device_release (&device->intel.base.base);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+i915_surface_copy_subimage (i915_device_t *device,
+ i915_surface_t *src,
+ const cairo_rectangle_int_t *extents,
+ cairo_bool_t flush,
+ i915_surface_t **clone_out)
+{
+ i915_surface_t *clone;
+ cairo_status_t status;
+
+ clone = (i915_surface_t *)
+ i915_surface_create_internal (&device->intel.base,
+ src->intel.drm.format,
+ extents->width,
+ extents->height,
+ I915_TILING_X, TRUE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ status = i915_blt (src, clone,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ 0, 0,
+ flush);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return status;
+ }
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_clear_boxes (i915_surface_t *dst,
+ const cairo_boxes_t *boxes)
+{
+ i915_device_t *device = i915_device (dst);
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) };
+ int cmd, br13, clear = 0, i;
+
+ cmd = XY_COLOR_BLT_CMD;
+ br13 = (0xCC << 16) | dst->intel.drm.stride;
+ switch (dst->intel.drm.format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_A8:
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ br13 |= BR13_565;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ clear = 0xff000000;
+ case CAIRO_FORMAT_ARGB32:
+ br13 |= BR13_8888;
+ cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+ break;
+ }
+
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ return status;
+
+ if (! i915_check_aperture_and_fences (device, bo_array, 1) ||
+ i915_batch_space (device) < 6 * boxes->num_boxes)
+ {
+ status = i915_batch_flush (device);
+ if (unlikely (status))
+ goto RELEASE;
+ }
+
+ if (device->vertex_count)
+ i915_vbo_flush (device);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+ if (x2 <= x1 || y2 <= y1)
+ continue;
+
+ OUT_DWORD (cmd);
+ OUT_DWORD (br13);
+ OUT_DWORD ((y1 << 16) | x1);
+ OUT_DWORD (((y2 - 1) << 16) | (x2 - 1));
+ OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+ OUT_DWORD (clear);
+ }
+ }
+
+RELEASE:
+ cairo_device_release (&device->intel.base.base);
+ return status;
+}
+
+static cairo_status_t
+i915_surface_extract_X_from_Y (i915_device_t *device,
+ i915_surface_t *src,
+ const cairo_rectangle_int_t *extents,
+ i915_surface_t **clone_out)
+{
+ i915_surface_t *clone;
+ i915_shader_t shader;
+ cairo_surface_pattern_t pattern;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ status = i915_surface_fallback_flush (src);
+ if (unlikely (status))
+ return status;
+
+ clone = (i915_surface_t *)
+ i915_surface_create_internal (&device->intel.base,
+ src->intel.drm.format,
+ extents->width,
+ extents->height,
+ I915_TILING_X, TRUE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ i915_shader_init (&shader, clone, CAIRO_OPERATOR_SOURCE, 1.);
+
+ _cairo_pattern_init_for_surface (&pattern, &src->intel.drm.base);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y);
+
+ rect.x = rect.y = 0;
+ rect.width = extents->width;
+ rect.height = extents->height;
+ status = i915_shader_acquire_pattern (&shader, &shader.source, &pattern.base, &rect);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status))
+ goto err_shader;
+
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i915_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto err_device;
+
+ shader.add_rectangle (&shader, 0, 0, extents->width, extents->height);
+
+ cairo_device_release (&device->intel.base.base);
+ i915_shader_fini (&shader);
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+
+err_device:
+ cairo_device_release (&device->intel.base.base);
+err_shader:
+ i915_shader_fini (&shader);
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return status;
+}
+
+static cairo_status_t
+i915_blt_boxes (i915_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ const cairo_boxes_t *boxes)
+{
+ const cairo_surface_pattern_t *spattern;
+ i915_device_t *device;
+ i915_surface_t *src;
+ cairo_surface_t *free_me = NULL;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ int br13, cmd, tx, ty;
+ intel_bo_t *bo_array[2];
+ int i;
+
+ if (! i915_can_blt (dst, pattern))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ spattern = (const cairo_surface_pattern_t *) pattern;
+ src = (i915_surface_t *) spattern->surface;
+
+ if (src->intel.drm.base.is_clear)
+ return i915_clear_boxes (dst, boxes);
+
+ if (pattern->extend != CAIRO_EXTEND_NONE &&
+ (extents->x + tx < 0 ||
+ extents->y + ty < 0 ||
+ extents->x + tx + extents->width > src->intel.drm.width ||
+ extents->y + ty + extents->height > src->intel.drm.height))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = i915_surface_fallback_flush (src);
+ if (unlikely (status))
+ return status;
+
+ tx = _cairo_lround (pattern->matrix.x0);
+ ty = _cairo_lround (pattern->matrix.y0);
+
+ device = i915_device (dst);
+ if (to_intel_bo (src->intel.drm.bo)->tiling == I915_TILING_Y) {
+ cairo_rectangle_int_t extents;
+
+ _cairo_boxes_extents (boxes, &extents);
+ extents.x += tx;
+ extents.y += ty;
+
+ status = i915_surface_extract_X_from_Y (device, src, &extents, &src);
+ if (unlikely (status))
+ return status;
+
+ free_me = &src->intel.drm.base;
+ tx = -extents.x;
+ ty = -extents.y;
+ }
+
+ bo_array[0] = to_intel_bo (dst->intel.drm.bo);
+ bo_array[1] = to_intel_bo (src->intel.drm.bo);
+
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ if (! i915_check_aperture_and_fences (device, bo_array, 2) ||
+ i915_batch_space (device) < 8 * boxes->num_boxes)
+ {
+ status = i915_batch_flush (device);
+ if (unlikely (status))
+ goto CLEANUP_DEVICE;
+ }
+
+ cmd = XY_SRC_COPY_BLT_CMD;
+ br13 = (0xCC << 16) | dst->intel.drm.stride;
+ switch (dst->intel.drm.format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_A8:
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ br13 |= BR13_565;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_ARGB32:
+ br13 |= BR13_8888;
+ cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+ break;
+ }
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+ if (x1 + tx < 0)
+ x1 = -tx;
+ if (x2 + tx > src->intel.drm.width)
+ x2 = src->intel.drm.width - tx;
+
+ if (y1 + ty < 0)
+ y1 = -ty;
+ if (y2 + ty > src->intel.drm.height)
+ y2 = src->intel.drm.height - ty;
+
+ if (x2 <= x1 || y2 <= y1)
+ continue;
+ if (x2 < 0 || y2 < 0)
+ continue;
+ if (x1 >= dst->intel.drm.width || y2 >= dst->intel.drm.height)
+ continue;
+
+ OUT_DWORD (cmd);
+ OUT_DWORD (br13);
+ OUT_DWORD ((y1 << 16) | x1);
+ OUT_DWORD (((y2 - 1) << 16) | (x2 - 1));
+ OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+ OUT_DWORD (((y1 + ty) << 16) | (x1 + tx));
+ OUT_DWORD (src->intel.drm.stride);
+ OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0);
+ }
+ }
+
+ /* XXX fixup blank portions */
+
+CLEANUP_DEVICE:
+ cairo_device_release (&device->intel.base.base);
+CLEANUP_SURFACE:
+ cairo_surface_destroy (free_me);
+ return status;
+}
+
+static cairo_status_t
+_upload_image_inplace (i915_surface_t *surface,
+ const cairo_pattern_t *source,
+ const cairo_rectangle_int_t *extents,
+ const cairo_boxes_t *boxes)
+{
+ i915_device_t *device;
+ const cairo_surface_pattern_t *pattern;
+ cairo_image_surface_t *image;
+ const struct _cairo_boxes_chunk *chunk;
+ intel_bo_t *bo;
+ int tx, ty, i;
+
+ if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ image = (cairo_image_surface_t *) pattern->surface;
+ if (source->extend != CAIRO_EXTEND_NONE &&
+ (extents->x + tx < 0 ||
+ extents->y + ty < 0 ||
+ extents->x + tx + extents->width > image->width ||
+ extents->y + ty + extents->height > image->height))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ device = i915_device (surface);
+ bo = to_intel_bo (surface->intel.drm.bo);
+ if (bo->exec != NULL || ! intel_bo_is_inactive (&device->intel, bo)) {
+ intel_bo_t *new_bo;
+ cairo_bool_t need_clear = FALSE;
+
+ if (boxes->num_boxes != 1 ||
+ extents->width < surface->intel.drm.width ||
+ extents->height < surface->intel.drm.height)
+ {
+ if (! surface->intel.drm.base.is_clear)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ need_clear = TRUE;
+ }
+
+ new_bo = intel_bo_create (&device->intel,
+ bo->full_size, bo->base.size,
+ FALSE, bo->tiling, bo->stride);
+ if (unlikely (new_bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ intel_bo_in_flight_add (&device->intel, bo);
+ intel_bo_destroy (&device->intel, bo);
+
+ bo = new_bo;
+ surface->intel.drm.bo = &bo->base;
+
+ if (need_clear) {
+ memset (intel_bo_map (&device->intel, bo), 0,
+ bo->stride * surface->intel.drm.height);
+ }
+ }
+
+ if (image->format == surface->intel.drm.format) {
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+ cairo_status_t status;
+
+ if (x1 + tx < 0)
+ x1 = -tx;
+ if (x2 + tx > image->width)
+ x2 = image->width - tx;
+
+ if (y1 + ty < 0)
+ y1 = -ty;
+ if (y2 + ty > image->height)
+ y2 = image->height - ty;
+
+ if (x2 <= x1 || y2 <= y1)
+ continue;
+ if (x2 < 0 || y2 < 0)
+ continue;
+ if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height)
+ continue;
+
+ status = intel_bo_put_image (&device->intel,
+ bo,
+ image,
+ x1 + tx, y1 + ty,
+ x2 - x1, y2 - y1,
+ x1, y1);
+ if (unlikely (status))
+ return status;
+ }
+ }
+ } else {
+ pixman_image_t *dst;
+ void *ptr;
+
+ ptr = intel_bo_map (&device->intel, bo);
+ if (unlikely (ptr == NULL))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ dst = pixman_image_create_bits (_cairo_format_to_pixman_format_code (surface->intel.drm.format),
+ surface->intel.drm.width,
+ surface->intel.drm.height,
+ ptr,
+ surface->intel.drm.stride);
+ if (unlikely (dst == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+ if (x1 + tx < 0)
+ x1 = -tx;
+ if (x2 + tx > image->width)
+ x2 = image->width - tx;
+
+ if (y1 + ty < 0)
+ y1 = -ty;
+ if (y2 + ty > image->height)
+ y2 = image->height - ty;
+
+ if (x2 <= x1 || y2 <= y1)
+ continue;
+ if (x2 < 0 || y2 < 0)
+ continue;
+ if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height)
+ continue;
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, dst,
+ x1 + tx, y1 + ty,
+ 0, 0,
+ x1, y1,
+ x2 - x1, y2 - y1);
+ }
+ }
+
+ pixman_image_unref (dst);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_composite_boxes (i915_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ double opacity,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_bool_t need_clip_surface = FALSE;
+ cairo_region_t *clip_region = NULL;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ i915_shader_t shader;
+ i915_device_t *device;
+ int i;
+
+ /* If the boxes are not pixel-aligned, we will need to compute a real mask */
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ if (! boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (clip == NULL && op == CAIRO_OPERATOR_SOURCE && opacity == 1.) {
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ status = i915_blt_boxes (dst, pattern, &extents->bounded, boxes);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _upload_image_inplace (dst, pattern,
+ &extents->bounded, boxes);
+ }
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+ }
+
+ if (i915_surface_needs_tiling (dst)) {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ i915_shader_init (&shader, dst, op, opacity);
+
+ status = i915_shader_acquire_pattern (&shader,
+ &shader.source,
+ pattern,
+ &extents->bounded);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_clip_surface)
+ i915_shader_set_clip (&shader, clip);
+ }
+
+ device = i915_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i915_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto err_device;
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+ if (x2 > x1 && y2 > y1)
+ shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+ }
+ }
+
+ if (! extents->is_bounded)
+ status = i915_fixup_unbounded_boxes (dst, extents, clip, boxes);
+
+ err_device:
+ cairo_device_release (&device->intel.base.base);
+ err_shader:
+ i915_shader_fini (&shader);
+
+ return status;
+}
+
+cairo_status_t
+i915_surface_clear (i915_surface_t *dst)
+{
+ i915_device_t *device;
+ cairo_status_t status;
+ intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) };
+
+ device = i915_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ return status;
+
+ if (i915_surface_needs_tiling (dst)) {
+ int cmd, br13, clear = 0;
+
+ if (! i915_check_aperture_and_fences (device, bo_array, 1) ||
+ i915_batch_space (device) < 6)
+ {
+ status = i915_batch_flush (device);
+ if (unlikely (status)) {
+ cairo_device_release (&device->intel.base.base);
+ return status;
+ }
+ }
+
+ if (device->vertex_count)
+ i915_vbo_flush (device);
+
+ cmd = XY_COLOR_BLT_CMD;
+ br13 = (0xCC << 16) | dst->intel.drm.stride;
+ switch (dst->intel.drm.format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_A8:
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ br13 |= BR13_565;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ clear = 0xff000000;
+ case CAIRO_FORMAT_ARGB32:
+ br13 |= BR13_8888;
+ cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+ break;
+ }
+
+ OUT_DWORD (cmd);
+ OUT_DWORD (br13);
+ OUT_DWORD (0);
+ OUT_DWORD (((dst->intel.drm.height - 1) << 16) |
+ (dst->intel.drm.width - 1));
+ OUT_RELOC_FENCED (dst,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+ OUT_DWORD (clear);
+ } else {
+ if (! i915_check_aperture (device, bo_array, 1) ||
+ i915_batch_space (device) < 24)
+ {
+ status = i915_batch_flush (device);
+ if (unlikely (status)) {
+ cairo_device_release (&device->intel.base.base);
+ return status;
+ }
+ }
+
+ if (device->vertex_count)
+ i915_vbo_flush (device);
+
+ i915_set_dst (device, dst);
+
+ /* set clear parameters */
+ if (device->clear_alpha != (dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA)) {
+ device->clear_alpha = dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA;
+ OUT_DWORD (_3DSTATE_CLEAR_PARAMETERS);
+ OUT_DWORD (CLEARPARAM_CLEAR_RECT | CLEARPARAM_WRITE_COLOR);
+ /* ZONE_INIT color */
+ if (device->clear_alpha) /* XXX depends on pixel format, 16bit needs replication, 8bit? */
+ OUT_DWORD (0x00000000);
+ else
+ OUT_DWORD (0xff000000);
+ OUT_DWORD (0); /* ZONE_INIT depth */
+ /* CLEAR_RECT color */
+ if (device->clear_alpha)
+ OUT_DWORD (0x00000000);
+ else
+ OUT_DWORD (0xff000000);
+ OUT_DWORD (0); /* CLEAR_RECT depth */
+ OUT_DWORD (0); /* CLEAR_RECT stencil */
+ }
+
+ OUT_DWORD (PRIM3D_CLEAR_RECT | 5);
+ OUT_DWORD (pack_float (dst->intel.drm.width));
+ OUT_DWORD (pack_float (dst->intel.drm.height));
+ OUT_DWORD (0);
+ OUT_DWORD (pack_float (dst->intel.drm.height));
+ OUT_DWORD (0);
+ OUT_DWORD (0);
+ }
+
+ cairo_device_release (&device->intel.base.base);
+
+ dst->deferred_clear = FALSE;
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (i915_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip,
+ double opacity)
+{
+ cairo_status_t status;
+
+ if (boxes->num_boxes == 0) {
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ return i915_fixup_unbounded (dst, extents, clip);
+ }
+
+ if (clip == NULL &&
+ (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && dst->intel.drm.base.is_clear)) &&
+ opacity == 1. &&
+ boxes->num_boxes == 1 &&
+ extents->bounded.width == dst->intel.drm.width &&
+ extents->bounded.height == dst->intel.drm.height)
+ {
+ op = CAIRO_OPERATOR_SOURCE;
+ dst->deferred_clear = FALSE;
+
+ status = _upload_image_inplace (dst, src,
+ &extents->bounded, boxes);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ if (dst->deferred_clear) {
+ status = i915_surface_clear (dst);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Use a fast path if the boxes are pixel aligned */
+ status = _composite_boxes (dst, op, src, boxes, antialias, clip, opacity, extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ /* Otherwise render the boxes via an implicit mask and composite in the usual
+ * fashion.
+ */
+ return i915_clip_and_composite_spans (dst, op, src, antialias,
+ _composite_boxes_spans, boxes,
+ extents, clip, opacity);
+}
+
+static cairo_clip_path_t *
+_clip_get_solitary_path (cairo_clip_t *clip)
+{
+ cairo_clip_path_t *iter = clip->path;
+ cairo_clip_path_t *path = NULL;
+
+ do {
+ if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) {
+ if (path != NULL)
+ return FALSE;
+
+ path = iter;
+ }
+ iter = iter->prev;
+ } while (iter != NULL);
+
+ return path;
+}
+
+typedef struct {
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+} composite_polygon_info_t;
+
+static cairo_status_t
+_composite_polygon_spans (void *closure,
+ cairo_span_renderer_t *renderer,
+ const cairo_rectangle_int_t *extents)
+{
+ composite_polygon_info_t *info = closure;
+ cairo_botor_scan_converter_t converter;
+ cairo_status_t status;
+ cairo_box_t box;
+
+ box.p1.x = _cairo_fixed_from_int (extents->x);
+ box.p1.y = _cairo_fixed_from_int (extents->y);
+ box.p2.x = _cairo_fixed_from_int (extents->x + extents->width);
+ box.p2.y = _cairo_fixed_from_int (extents->y + extents->height);
+
+ _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
+
+ status = converter.base.add_polygon (&converter.base, &info->polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = converter.base.generate (&converter.base, renderer);
+
+ converter.base.destroy (&converter.base);
+
+ return status;
+}
+
+static cairo_int_status_t
+i915_surface_fill_with_alpha (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ double opacity)
+{
+ i915_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ composite_polygon_info_t info;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (extents.is_bounded && clip != NULL) {
+ cairo_clip_path_t *clip_path;
+
+ if (((clip_path = _clip_get_solitary_path (clip)) != NULL) &&
+ _cairo_path_fixed_equal (&clip_path->path, path))
+ {
+ clip = NULL;
+ }
+ }
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ assert (! _cairo_path_fixed_fill_is_empty (path));
+
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (dst, op, source,
+ &boxes, antialias,
+ &extents, clip,
+ opacity);
+ }
+
+ _cairo_boxes_fini (&boxes);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto CLEANUP_BOXES;
+ }
+
+ _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon);
+ if (unlikely (status))
+ goto CLEANUP_POLYGON;
+
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t rect;
+
+ _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+ goto CLEANUP_POLYGON;
+ }
+
+ if (info.polygon.num_edges == 0) {
+ if (! extents.is_bounded)
+ status = i915_fixup_unbounded (dst, &extents, clip);
+
+ goto CLEANUP_POLYGON;
+ }
+
+ info.fill_rule = fill_rule;
+ info.antialias = antialias;
+ status = i915_clip_and_composite_spans (dst, op, source, antialias,
+ _composite_polygon_spans, &info,
+ &extents, clip, opacity);
+
+CLEANUP_POLYGON:
+ _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+i915_surface_paint_with_alpha (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip,
+ double opacity)
+{
+ i915_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_clip_path_t *clip_path;
+ cairo_boxes_t boxes;
+ int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded);
+ cairo_box_t *clip_boxes = boxes.boxes_embedded;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ /* If the clip cannot be reduced to a set of boxes, we will need to
+ * use a clipmask. Paint is special as it is the only operation that
+ * does not implicitly use a mask, so we may be able to reduce this
+ * operation to a fill...
+ */
+ if (clip != NULL &&
+ extents.is_bounded &&
+ (clip_path = _clip_get_solitary_path (clip)) != NULL)
+ {
+ status = i915_surface_fill_with_alpha (dst, op, source,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL, opacity);
+ }
+ else
+ {
+ _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+ status = _clip_and_composite_boxes (dst, op, source,
+ &boxes, CAIRO_ANTIALIAS_DEFAULT,
+ &extents, clip, opacity);
+ }
+ if (clip_boxes != boxes.boxes_embedded)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+i915_surface_paint (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ i915_surface_t *dst = abstract_dst;
+
+ /* XXX unsupported operators? use pixel shader blending, eventually */
+
+ if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
+ dst->deferred_clear = TRUE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return i915_surface_paint_with_alpha (dst, op, source, clip, 1.);
+}
+
+static cairo_int_status_t
+i915_surface_mask (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ i915_surface_t *dst = abstract_dst;
+ i915_device_t *device;
+ cairo_composite_rectangles_t extents;
+ i915_shader_t shader;
+ cairo_clip_t local_clip;
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t need_clip_surface = FALSE;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask;
+ return i915_surface_paint_with_alpha (dst, op, source, clip, solid->color.alpha);
+ }
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status)) {
+ _cairo_clip_fini (&local_clip);
+ return status;
+ }
+
+ have_clip = TRUE;
+ }
+
+ i915_shader_init (&shader, dst, op, 1.);
+
+ status = i915_shader_acquire_pattern (&shader,
+ &shader.source,
+ source,
+ &extents.bounded);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i915_shader_acquire_pattern (&shader,
+ &shader.mask,
+ mask,
+ &extents.bounded);
+ if (unlikely (status))
+ goto err_shader;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ goto err_shader;
+ }
+
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_clip_surface)
+ i915_shader_set_clip (&shader, clip);
+
+ if (clip_region != NULL) {
+ cairo_rectangle_int_t rect;
+ cairo_bool_t is_empty;
+
+ status = CAIRO_STATUS_SUCCESS;
+ cairo_region_get_extents (clip_region, &rect);
+ is_empty = ! _cairo_rectangle_intersect (&extents.unbounded, &rect);
+ if (unlikely (is_empty))
+ goto err_shader;
+
+ is_empty = ! _cairo_rectangle_intersect (&extents.bounded, &rect);
+ if (unlikely (is_empty && extents.is_bounded))
+ goto err_shader;
+
+ if (cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+ }
+ }
+
+ if (i915_surface_needs_tiling (dst)) {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ device = i915_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto err_shader;
+
+ if (dst->deferred_clear) {
+ status = i915_surface_clear (dst);
+ if (unlikely (status))
+ goto err_shader;
+ }
+
+ status = i915_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto err_device;
+
+ if (clip_region != NULL) {
+ unsigned int n, num_rectangles;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rectangles; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+
+ shader.add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.x + rect.width, rect.y + rect.height);
+ }
+ } else {
+ shader.add_rectangle (&shader,
+ extents.bounded.x, extents.bounded.y,
+ extents.bounded.x + extents.bounded.width,
+ extents.bounded.y + extents.bounded.height);
+ }
+
+ if (! extents.is_bounded)
+ status = i915_fixup_unbounded (dst, &extents, clip);
+
+ err_device:
+ cairo_device_release (&device->intel.base.base);
+ err_shader:
+ i915_shader_fini (&shader);
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+i915_surface_stroke (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ i915_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ composite_polygon_info_t info;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source,
+ path, stroke_style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ stroke_style,
+ ctm,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (dst, op, source,
+ &boxes, antialias,
+ &extents, clip, 1.);
+ }
+
+ _cairo_boxes_fini (&boxes);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto CLEANUP_BOXES;
+ }
+
+ _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &info.polygon);
+ if (unlikely (status))
+ goto CLEANUP_POLYGON;
+
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t rect;
+
+ _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+ goto CLEANUP_POLYGON;
+ }
+
+ if (info.polygon.num_edges == 0) {
+ if (! extents.is_bounded)
+ status = i915_fixup_unbounded (dst, &extents, clip);
+
+ goto CLEANUP_POLYGON;
+ }
+
+ info.fill_rule = CAIRO_FILL_RULE_WINDING;
+ info.antialias = antialias;
+ status = i915_clip_and_composite_spans (dst, op, source, antialias,
+ _composite_polygon_spans, &info,
+ &extents, clip, 1.);
+
+CLEANUP_POLYGON:
+ _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+i915_surface_fill (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ return i915_surface_fill_with_alpha (abstract_dst, op, source, path, fill_rule, tolerance, antialias, clip, 1.);
+}
+
+static const cairo_surface_backend_t i915_surface_backend = {
+ CAIRO_SURFACE_TYPE_DRM,
+ _cairo_default_context_create,
+
+ i915_surface_create_similar,
+ i915_surface_finish,
+
+ NULL,
+ intel_surface_acquire_source_image,
+ intel_surface_release_source_image,
+
+ NULL, NULL, NULL,
+ NULL, /* composite */
+ NULL, /* fill */
+ NULL, /* trapezoids */
+ NULL, /* span */
+ NULL, /* check-span */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_drm_surface_get_extents,
+ NULL, /* old-glyphs */
+ _cairo_drm_surface_get_font_options,
+
+ i915_surface_flush,
+ NULL, /* mark_dirty */
+ intel_scaled_font_fini,
+ intel_scaled_glyph_fini,
+
+ i915_surface_paint,
+ i915_surface_mask,
+ i915_surface_stroke,
+ i915_surface_fill,
+ i915_surface_glyphs,
+};
+
+static void
+i915_surface_init (i915_surface_t *surface,
+ cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ intel_surface_init (&surface->intel, &i915_surface_backend, device,
+ format, width, height);
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_ARGB32:
+ surface->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+ surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ surface->map0 = MAPSURF_32BIT | MT_32BIT_XRGB8888;
+ surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ surface->map0 = MAPSURF_16BIT | MT_16BIT_RGB565;
+ surface->colorbuf = COLR_BUF_RGB565;
+ break;
+ case CAIRO_FORMAT_A8:
+ surface->map0 = MAPSURF_8BIT | MT_8BIT_A8;
+ surface->colorbuf = COLR_BUF_8BIT | DEPTH_FRMT_24_FIXED_8_OTHER;
+ break;
+ }
+ surface->colorbuf |= DSTORG_HORT_BIAS (0x8) | DSTORG_VERT_BIAS (0x8);
+ surface->map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+ ((width - 1) << MS3_WIDTH_SHIFT);
+ surface->map1 = 0;
+
+ surface->is_current_texture = 0;
+ surface->deferred_clear = FALSE;
+
+ surface->offset = 0;
+
+ surface->stencil = NULL;
+ surface->cache = NULL;
+}
+
+cairo_surface_t *
+i915_surface_create_internal (cairo_drm_device_t *base_dev,
+ cairo_format_t format,
+ int width, int height,
+ uint32_t tiling,
+ cairo_bool_t gpu_target)
+{
+ i915_surface_t *surface;
+ cairo_status_t status_ignored;
+
+ surface = malloc (sizeof (i915_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ i915_surface_init (surface, base_dev, format, width, height);
+
+ if (width && height) {
+ uint32_t size, stride;
+ intel_bo_t *bo;
+
+ width = (width + 3) & -4;
+ stride = cairo_format_stride_for_width (surface->intel.drm.format, width);
+ /* check for tiny surfaces for which tiling is irrelevant */
+ if (height * stride <= 4096)
+ tiling = I915_TILING_NONE;
+ if (tiling != I915_TILING_NONE && stride <= 512)
+ tiling = I915_TILING_NONE;
+ if (tiling != I915_TILING_NONE) {
+ if (height <= 8)
+ tiling = I915_TILING_NONE;
+ else if (height <= 16)
+ tiling = I915_TILING_X;
+ }
+ /* large surfaces we need to blt, so force TILING_X */
+ if (height > 2048)
+ tiling = I915_TILING_X;
+ /* but there is a maximum limit to the tiling pitch */
+ if (tiling != I915_TILING_NONE && stride > 8192)
+ tiling = I915_TILING_NONE;
+
+ stride = i915_tiling_stride (tiling, stride);
+ assert (stride >= (uint32_t) cairo_format_stride_for_width (surface->intel.drm.format, width));
+ assert (tiling == I915_TILING_NONE || stride <= 8192);
+ height = i915_tiling_height (tiling, height);
+ if (height > 64*1024) {
+ free (surface);
+ cairo_device_destroy (&base_dev->base);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ }
+
+ size = stride * height;
+ bo = intel_bo_create (to_intel_device (&base_dev->base),
+ i915_tiling_size (tiling, size), size,
+ gpu_target, tiling, stride);
+ if (bo == NULL) {
+ status_ignored = _cairo_drm_surface_finish (&surface->intel.drm);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ assert (bo->base.size >= size);
+
+ surface->intel.drm.bo = &bo->base;
+ surface->intel.drm.stride = stride;
+
+ surface->map0 |= MS3_tiling (tiling);
+ surface->map1 = (stride/4 - 1) << MS4_PITCH_SHIFT;
+ }
+
+ return &surface->intel.drm.base;
+}
+
+static cairo_surface_t *
+i915_surface_create (cairo_drm_device_t *base_dev,
+ cairo_format_t format,
+ int width, int height)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_A8:
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ case CAIRO_FORMAT_A1:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return i915_surface_create_internal (base_dev, format, width, height,
+ I915_TILING_DEFAULT, TRUE);
+}
+
+static cairo_surface_t *
+i915_surface_create_for_name (cairo_drm_device_t *base_dev,
+ unsigned int name,
+ cairo_format_t format,
+ int width, int height, int stride)
+{
+ i915_surface_t *surface;
+
+ /* Vol I, p134: size restrictions for textures */
+ /* Vol I, p129: destination surface stride must be a multiple of 32 bytes */
+ if (stride < cairo_format_stride_for_width (format, (width + 3) & -4) ||
+ stride & 31)
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+ }
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_A8:
+ break;
+ }
+
+ surface = malloc (sizeof (i915_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ i915_surface_init (surface, base_dev, format, width, height);
+
+ if (width && height) {
+ surface->intel.drm.stride = stride;
+ surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT;
+
+ surface->intel.drm.bo =
+ &intel_bo_create_for_name (to_intel_device (&base_dev->base),
+ name)->base;
+ if (unlikely (surface->intel.drm.bo == NULL)) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ to_intel_bo (surface->intel.drm.bo)->stride = stride;
+
+ surface->map0 |= MS3_tiling (to_intel_bo (surface->intel.drm.bo)->tiling);
+ }
+
+ return &surface->intel.drm.base;
+}
+
+static cairo_status_t
+i915_buffer_cache_init (intel_buffer_cache_t *cache,
+ i915_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ const uint32_t tiling = I915_TILING_DEFAULT;
+ uint32_t stride, size;
+
+ assert ((width & 3) == 0);
+ assert ((height & 1) == 0);
+ cache->buffer.width = width;
+ cache->buffer.height = height;
+
+ switch (format) {
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_RGB16_565:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_ARGB32:
+ cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+ stride = width * 4;
+ break;
+ case CAIRO_FORMAT_A8:
+ cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8;
+ stride = width;
+ break;
+ }
+ assert ((stride & 7) == 0);
+ assert (i915_tiling_stride (tiling, stride) == stride);
+ assert (i915_tiling_height (tiling, height) == height);
+
+ size = height * stride;
+ assert (i915_tiling_size (tiling, size) == size);
+ cache->buffer.bo = intel_bo_create (&device->intel, size, size, FALSE, tiling, stride);
+ if (unlikely (cache->buffer.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ cache->buffer.stride = cache->buffer.bo->stride;
+
+ cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+ ((width - 1) << MS3_WIDTH_SHIFT);
+ cache->buffer.map0 |= MS3_tiling (tiling);
+ cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT;
+
+ cache->ref_count = 0;
+ cairo_list_init (&cache->link);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+i915_surface_t *
+i915_surface_create_from_cacheable_image_internal (i915_device_t *device,
+ cairo_image_surface_t *image)
+{
+ i915_surface_t *surface;
+ cairo_status_t status;
+ cairo_list_t *caches;
+ intel_buffer_cache_t *cache;
+ cairo_rtree_node_t *node;
+ cairo_format_t format;
+ int width, height, bpp;
+
+ format = image->format;
+ if (format == CAIRO_FORMAT_A1)
+ format = CAIRO_FORMAT_A8;
+
+ width = image->width;
+ height = image->height;
+ if (width > IMAGE_CACHE_WIDTH/2 || height > IMAGE_CACHE_HEIGHT/2) {
+ surface = (i915_surface_t *)
+ i915_surface_create_internal (&device->intel.base,
+ format,
+ width, height,
+ I915_TILING_NONE, FALSE);
+ if (unlikely (surface->intel.drm.base.status))
+ return surface;
+
+ status = intel_bo_put_image (&device->intel,
+ to_intel_bo (surface->intel.drm.bo),
+ image,
+ 0, 0,
+ width, height,
+ 0, 0);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->intel.drm.base);
+ return (i915_surface_t *) _cairo_surface_create_in_error (status);
+ }
+
+ return surface;
+ }
+
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ return (i915_surface_t *) _cairo_surface_create_in_error (status);
+
+ switch (image->format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_RGB16_565:
+ caches = &device->image_caches[0];
+ format = CAIRO_FORMAT_ARGB32;
+ bpp = 4;
+ break;
+ case CAIRO_FORMAT_A8:
+ case CAIRO_FORMAT_A1:
+ caches = &device->image_caches[1];
+ format = CAIRO_FORMAT_A8;
+ bpp = 1;
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ goto CLEANUP_DEVICE;
+ }
+
+ node = NULL;
+ cairo_list_foreach_entry (cache, intel_buffer_cache_t, caches, link) {
+ if (! intel_bo_is_inactive (&device->intel, cache->buffer.bo))
+ continue;
+
+ status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+ if (unlikely (_cairo_status_is_error (status)))
+ goto CLEANUP_DEVICE;
+ if (status == CAIRO_STATUS_SUCCESS)
+ break;
+ }
+ if (node == NULL) {
+ cache = malloc (sizeof (intel_buffer_cache_t));
+ if (unlikely (cache == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_DEVICE;
+ }
+
+ status = i915_buffer_cache_init (cache, device, format,
+ IMAGE_CACHE_WIDTH,
+ IMAGE_CACHE_HEIGHT);
+ if (unlikely (status)) {
+ free (cache);
+ goto CLEANUP_DEVICE;
+ }
+
+ _cairo_rtree_init (&cache->rtree,
+ IMAGE_CACHE_WIDTH,
+ IMAGE_CACHE_HEIGHT,
+ 4,
+ sizeof (i915_image_private_t));
+
+ status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_list_init (&cache->link);
+ }
+ cairo_list_move (&cache->link, caches);
+ ((i915_image_private_t *) node)->container = cache;
+
+ status = intel_bo_put_image (&device->intel,
+ cache->buffer.bo,
+ image,
+ 0, 0,
+ width, height,
+ node->x, node->y);
+ if (unlikely (status))
+ goto CLEANUP_CACHE;
+
+ surface = malloc (sizeof (i915_surface_t));
+ if (unlikely (surface == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_CACHE;
+ }
+
+ i915_surface_init (surface, &device->intel.base,
+ format, width, height);
+
+ surface->intel.drm.stride = cache->buffer.stride;
+
+ surface->map0 |= MS3_tiling (cache->buffer.bo->tiling);
+ surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT;
+
+ surface->intel.drm.bo = &intel_bo_reference (cache->buffer.bo)->base;
+ surface->offset = node->y * cache->buffer.stride + bpp * node->x;
+
+ surface->cache = (i915_image_private_t *) node;
+ cache->ref_count++;
+
+ cairo_device_release (&device->intel.base.base);
+
+ return surface;
+
+CLEANUP_CACHE:
+ _cairo_rtree_node_destroy (&cache->rtree, node);
+ if (cache->ref_count == 0) {
+ intel_bo_destroy (&device->intel, cache->buffer.bo);
+ _cairo_rtree_fini (&cache->rtree);
+ cairo_list_del (&cache->link);
+ free (cache);
+ }
+CLEANUP_DEVICE:
+ cairo_device_release (&device->intel.base.base);
+ return (i915_surface_t *) _cairo_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+i915_surface_create_from_cacheable_image (cairo_drm_device_t *device,
+ cairo_surface_t *source)
+{
+ i915_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ surface = i915_surface_create_from_cacheable_image_internal ((i915_device_t *) device, image);
+
+ _cairo_surface_release_source_image (source, image, image_extra);
+
+ return &surface->intel.drm.base;
+}
+
+static cairo_status_t
+i915_surface_enable_scan_out (void *abstract_surface)
+{
+ i915_surface_t *surface = abstract_surface;
+ intel_bo_t *bo;
+ cairo_status_t status;
+
+ if (unlikely (surface->intel.drm.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+
+ bo = to_intel_bo (surface->intel.drm.bo);
+ if (bo->tiling == I915_TILING_Y) {
+ status = i915_surface_batch_flush (surface);
+ if (unlikely (status))
+ return status;
+
+ bo->tiling = I915_TILING_X;
+ surface->map0 &= ~MS3_tiling (I915_TILING_Y);
+ surface->map0 |= MS3_tiling (I915_TILING_X);
+ }
+
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+i915_device_flush (cairo_drm_device_t *device)
+{
+ cairo_status_t status;
+
+ if (unlikely (device->base.finished))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = cairo_device_acquire (&device->base);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = i915_batch_flush ((i915_device_t *) device);
+ cairo_device_release (&device->base);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+i915_device_throttle (cairo_drm_device_t *device)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&device->base);
+ if (unlikely (status))
+ return status;
+
+ status = i915_batch_flush ((i915_device_t *) device);
+ intel_throttle ((intel_device_t *) device);
+
+ cairo_device_release (&device->base);
+
+ return status;
+}
+
+static void
+i915_device_destroy (void *data)
+{
+ i915_device_t *device = data;
+
+ if (device->last_vbo)
+ intel_bo_destroy (&device->intel, device->last_vbo);
+
+ i915_batch_cleanup (device);
+
+ intel_device_fini (&device->intel);
+ free (device);
+}
+
+COMPILE_TIME_ASSERT (sizeof (i915_batch_setup) == sizeof (((i915_device_t *)0)->batch_header));
+COMPILE_TIME_ASSERT (offsetof (i915_device_t, batch_base) == offsetof (i915_device_t, batch_header) + sizeof (i915_batch_setup));
+
+cairo_drm_device_t *
+_cairo_drm_i915_device_create (int fd, dev_t dev_id, int vendor_id, int chip_id)
+{
+ i915_device_t *device;
+ cairo_status_t status;
+ uint64_t gtt_size;
+ int n;
+
+ if (! intel_info (fd, &gtt_size))
+ return NULL;
+
+ device = malloc (sizeof (i915_device_t));
+ if (device == NULL)
+ return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = intel_device_init (&device->intel, fd);
+ if (unlikely (status)) {
+ free (device);
+ return (cairo_drm_device_t *) _cairo_device_create_in_error (status);
+ }
+
+ device->debug = 0;
+ if (getenv ("CAIRO_DEBUG_DRM") != NULL)
+ device->debug = I915_DEBUG_SYNC;
+
+ n = intel_get (fd, I915_PARAM_NUM_FENCES_AVAIL);
+ if (n == 0)
+ n = 8;
+ device->batch.fences_avail = n - 2; /* conservative */
+
+ device->batch.gtt_avail_size = device->intel.gtt_avail_size / 4;
+ device->batch.est_gtt_size = I915_BATCH_SIZE;
+ device->batch.total_gtt_size = I915_BATCH_SIZE;
+ device->batch.exec_count = 0;
+ device->batch.reloc_count = 0;
+ device->batch.used = 0;
+ device->batch.fences = 0;
+
+ memcpy (device->batch_header, i915_batch_setup, sizeof (i915_batch_setup));
+ device->vbo = 0;
+ device->vbo_offset = 0;
+ device->vbo_used = 0;
+ device->vertex_index = 0;
+ device->vertex_count = 0;
+ device->last_vbo = NULL;
+
+ for (n = 0; n < ARRAY_LENGTH (device->image_caches); n++)
+ cairo_list_init (&device->image_caches[n]);
+
+ device->intel.base.surface.create = i915_surface_create;
+ device->intel.base.surface.create_for_name = i915_surface_create_for_name;
+ device->intel.base.surface.create_from_cacheable_image = i915_surface_create_from_cacheable_image;
+
+ device->intel.base.surface.flink = _cairo_drm_surface_flink;
+ device->intel.base.surface.enable_scan_out = i915_surface_enable_scan_out;
+ device->intel.base.surface.map_to_image = intel_surface_map_to_image;
+
+ device->intel.base.device.flush = i915_device_flush;
+ device->intel.base.device.throttle = i915_device_throttle;
+ device->intel.base.device.destroy = i915_device_destroy;
+
+ device->floats_per_vertex = 0;
+ device->current_source = NULL;
+ device->current_mask = NULL;
+ device->current_clip = NULL;
+
+ i915_device_reset (device);
+
+ return _cairo_drm_device_init (&device->intel.base,
+ fd, dev_id, vendor_id, chip_id,
+ 16*1024);
+}
diff --git a/src/drm/cairo-drm-i965-glyphs.c b/src/drm/cairo-drm-i965-glyphs.c
new file mode 100755
index 000000000..c66a63d73
--- /dev/null
+++ b/src/drm/cairo-drm-i965-glyphs.c
@@ -0,0 +1,504 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-drm-i965-private.h"
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+
+typedef struct _i965_glyphs i965_glyphs_t;
+
+typedef float *
+(*i965_get_rectangle_func_t) (i965_glyphs_t *glyphs);
+
+struct _i965_glyphs {
+ i965_get_rectangle_func_t get_rectangle;
+ i965_shader_t shader;
+
+ struct i965_vbo head, *tail;
+
+ unsigned int vbo_offset;
+ float *vbo_base;
+};
+
+static float *
+i965_glyphs_emit_rectangle (i965_glyphs_t *glyphs)
+{
+ return i965_add_rectangle (glyphs->shader.device);
+}
+
+static float *
+i965_glyphs_accumulate_rectangle (i965_glyphs_t *glyphs)
+{
+ float *vertices;
+ uint32_t size;
+
+ size = glyphs->shader.device->rectangle_size;
+ if (unlikely (glyphs->vbo_offset + size > I965_VERTEX_SIZE)) {
+ struct i965_vbo *vbo;
+
+ vbo = malloc (sizeof (struct i965_vbo));
+ if (unlikely (vbo == NULL)) {
+ /* throw error! */
+ }
+
+ glyphs->tail->next = vbo;
+ glyphs->tail = vbo;
+
+ vbo->next = NULL;
+ vbo->bo = intel_bo_create (&glyphs->shader.device->intel,
+ I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+ FALSE, I915_TILING_NONE, 0);
+ vbo->count = 0;
+
+ glyphs->vbo_offset = 0;
+ glyphs->vbo_base = intel_bo_map (&glyphs->shader.device->intel, vbo->bo);
+ }
+
+ vertices = glyphs->vbo_base + glyphs->vbo_offset;
+ glyphs->vbo_offset += size;
+ glyphs->tail->count += 3;
+
+ return vertices;
+}
+
+static void
+i965_add_glyph_rectangle (i965_glyphs_t *glyphs,
+ int x1, int y1,
+ int x2, int y2,
+ intel_glyph_t *glyph)
+{
+ float *v;
+
+ /* Each vertex is:
+ * 2 vertex coordinates
+ * 1 glyph texture coordinate
+ */
+
+ v = glyphs->get_rectangle (glyphs);
+
+ /* bottom right */
+ *v++ = x2; *v++ = y2;
+ *v++ = glyph->texcoord[0];
+
+ /* bottom left */
+ *v++ = x1; *v++ = y2;
+ *v++ = glyph->texcoord[1];
+
+ /* top left */
+ *v++ = x1; *v++ = y1;
+ *v++ = glyph->texcoord[2];
+}
+
+static cairo_status_t
+i965_surface_mask_internal (i965_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ i965_surface_t *mask,
+ cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ i965_device_t *device;
+ i965_shader_t shader;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ i965_shader_init (&shader, dst, op);
+
+ status = i965_shader_acquire_pattern (&shader, &shader.source,
+ source, &extents->bounded);
+ if (unlikely (status))
+ return status;
+
+ shader.mask.type.vertex = VS_NONE;
+ shader.mask.type.fragment = FS_SURFACE;
+ shader.mask.base.content = mask->intel.drm.base.content;
+ shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+ shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE);
+
+ cairo_matrix_init_translate (&shader.mask.base.matrix,
+ -extents->bounded.x,
+ -extents->bounded.y);
+ cairo_matrix_scale (&shader.mask.base.matrix,
+ 1. / mask->intel.drm.width,
+ 1. / mask->intel.drm.height);
+
+ shader.mask.base.bo = to_intel_bo (mask->intel.drm.bo);
+ shader.mask.base.format = mask->intel.drm.format;
+ shader.mask.base.width = mask->intel.drm.width;
+ shader.mask.base.height = mask->intel.drm.height;
+ shader.mask.base.stride = mask->intel.drm.stride;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ i965_shader_set_clip (&shader, clip);
+ }
+
+ status = cairo_device_acquire (dst->intel.drm.base.device);
+ if (unlikely (status))
+ goto CLEANUP_SHADER;
+
+ device = i965_device (dst);
+
+ status = i965_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto CLEANUP_DEVICE;
+
+ if (clip_region != NULL) {
+ unsigned int n, num_rectangles;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rectangles; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+
+ i965_shader_add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+ } else {
+ i965_shader_add_rectangle (&shader,
+ extents->bounded.x,
+ extents->bounded.y,
+ extents->bounded.width,
+ extents->bounded.height);
+ }
+
+ if (! extents->is_bounded)
+ status = i965_fixup_unbounded (dst, extents, clip);
+
+ CLEANUP_DEVICE:
+ cairo_device_release (&device->intel.base.base);
+ CLEANUP_SHADER:
+ i965_shader_fini (&shader);
+ return status;
+}
+
+cairo_int_status_t
+i965_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *g,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ i965_surface_t *surface = abstract_surface;
+ i965_surface_t *mask = NULL;
+ i965_device_t *device;
+ i965_glyphs_t glyphs;
+ cairo_composite_rectangles_t extents;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_bool_t overlap;
+ cairo_region_t *clip_region = NULL;
+ intel_bo_t *last_bo = NULL;
+ cairo_scaled_glyph_t *glyph_cache[64];
+ cairo_status_t status;
+ int mask_x = 0, mask_y = 0;
+ int i = 0;
+
+ *num_remaining = 0;
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ surface->intel.drm.width,
+ surface->intel.drm.height,
+ op, source,
+ scaled_font,
+ g, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL && _cairo_clip_contains_rectangle (clip, &extents.mask))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status))
+ return status;
+
+ have_clip = TRUE;
+ }
+
+ if (overlap || ! extents.is_bounded) {
+ cairo_format_t format;
+
+ format = CAIRO_FORMAT_A8;
+ if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
+ format = CAIRO_FORMAT_ARGB32;
+
+ mask = (i965_surface_t *)
+ i965_surface_create_internal (&i965_device (surface)->intel.base,
+ format,
+ extents.bounded.width,
+ extents.bounded.height,
+ I965_TILING_DEFAULT,
+ TRUE);
+ if (unlikely (mask->intel.drm.base.status))
+ return mask->intel.drm.base.status;
+
+ status = _cairo_surface_paint (&mask->intel.drm.base,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ NULL);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&mask->intel.drm.base);
+ return status;
+ }
+
+ i965_shader_init (&glyphs.shader, mask, CAIRO_OPERATOR_ADD);
+
+ status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source,
+ &_cairo_pattern_white.base,
+ &extents.bounded);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&mask->intel.drm.base);
+ return status;
+ }
+
+ mask_x = -extents.bounded.x;
+ mask_y = -extents.bounded.y;
+ } else {
+ i965_shader_init (&glyphs.shader, surface, op);
+
+ status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source,
+ source, &extents.bounded);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ i965_shader_set_clip (&glyphs.shader, clip);
+ }
+ }
+
+ glyphs.head.next = NULL;
+ glyphs.head.bo = NULL;
+ glyphs.head.count = 0;
+ glyphs.tail = &glyphs.head;
+
+ device = i965_device (surface);
+ if (mask != NULL || clip_region == NULL) {
+ glyphs.get_rectangle = i965_glyphs_emit_rectangle;
+ } else {
+ glyphs.get_rectangle = i965_glyphs_accumulate_rectangle;
+ glyphs.head.bo = intel_bo_create (&device->intel,
+ I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+ FALSE, I915_TILING_NONE, 0);
+ if (unlikely (glyphs.head.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ glyphs.vbo_base = intel_bo_map (&device->intel, glyphs.head.bo);
+ }
+ glyphs.vbo_offset = 0;
+
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto CLEANUP_GLYPHS;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ //private = _cairo_scaled_font_get_device (scaled_font, device);
+ if (scaled_font->surface_private == NULL) {
+ scaled_font->surface_private = device;
+ scaled_font->surface_backend = surface->intel.drm.base.backend;
+ cairo_list_add (&scaled_font->link, &device->intel.fonts);
+ }
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+
+ for (i = 0; i < num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+ int x, y, x1, x2, y1, y2;
+ int cache_index = g[i].index % ARRAY_LENGTH (glyph_cache);
+ intel_glyph_t *glyph;
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != g[i].index)
+ {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ g[i].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status))
+ goto FINISH;
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ if (unlikely (scaled_glyph->metrics.width == 0 ||
+ scaled_glyph->metrics.height == 0))
+ {
+ continue;
+ }
+
+ /* XXX glyph images are snapped to pixel locations */
+ x = _cairo_lround (g[i].x);
+ y = _cairo_lround (g[i].y);
+
+ x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+ y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+ x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
+ y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
+
+ if (x2 < extents.bounded.x ||
+ y2 < extents.bounded.y ||
+ x1 > extents.bounded.x + extents.bounded.width ||
+ y1 > extents.bounded.y + extents.bounded.height)
+ {
+ continue;
+ }
+
+ if (scaled_glyph->surface_private == NULL) {
+ status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) {
+ status = CAIRO_STATUS_SUCCESS;
+ continue;
+ }
+ if (unlikely (status))
+ goto FINISH;
+ }
+ glyph = intel_glyph_pin (scaled_glyph->surface_private);
+
+ if (glyph->cache->buffer.bo != last_bo) {
+ intel_buffer_cache_t *cache = glyph->cache;
+
+ glyphs.shader.mask.type.vertex = VS_GLYPHS;
+ glyphs.shader.mask.type.fragment = FS_GLYPHS;
+ glyphs.shader.mask.type.pattern = PATTERN_BASE;
+
+ glyphs.shader.mask.base.bo = cache->buffer.bo;
+ glyphs.shader.mask.base.format = cache->buffer.format;
+ glyphs.shader.mask.base.width = cache->buffer.width;
+ glyphs.shader.mask.base.height = cache->buffer.height;
+ glyphs.shader.mask.base.stride = cache->buffer.stride;
+ glyphs.shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+ glyphs.shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE);
+ glyphs.shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
+
+ glyphs.shader.committed = FALSE;
+ status = i965_shader_commit (&glyphs.shader, device);
+ if (unlikely (status))
+ goto FINISH;
+
+ last_bo = cache->buffer.bo;
+ }
+
+ x2 = x1 + glyph->width;
+ y2 = y1 + glyph->height;
+
+ if (mask_x)
+ x1 += mask_x, x2 += mask_x;
+ if (mask_y)
+ y1 += mask_y, y2 += mask_y;
+
+ i965_add_glyph_rectangle (&glyphs, x1, y1, x2, y2, glyph);
+ }
+
+ if (mask != NULL && clip_region != NULL)
+ i965_clipped_vertices (device, &glyphs.head, clip_region);
+
+ status = CAIRO_STATUS_SUCCESS;
+ FINISH:
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ cairo_device_release (surface->intel.drm.base.device);
+ CLEANUP_GLYPHS:
+ i965_shader_fini (&glyphs.shader);
+
+ if (glyphs.head.bo != NULL) {
+ struct i965_vbo *vbo, *next;
+
+ intel_bo_destroy (&device->intel, glyphs.head.bo);
+ for (vbo = glyphs.head.next; vbo != NULL; vbo = next) {
+ next = vbo->next;
+ intel_bo_destroy (&device->intel, vbo->bo);
+ free (vbo);
+ }
+ }
+
+ if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) {
+ cairo_path_fixed_t path;
+
+ _cairo_path_fixed_init (&path);
+ status = _cairo_scaled_font_glyph_path (scaled_font,
+ g + i, num_glyphs - i,
+ &path);
+ if (mask_x | mask_y) {
+ _cairo_path_fixed_translate (&path,
+ _cairo_fixed_from_int (mask_x),
+ _cairo_fixed_from_int (mask_y));
+ }
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = surface->intel.drm.base.backend->fill (glyphs.shader.target,
+ glyphs.shader.op,
+ mask != NULL ? &_cairo_pattern_white.base : source,
+ &path,
+ CAIRO_FILL_RULE_WINDING,
+ 0,
+ scaled_font->options.antialias,
+ clip);
+ }
+ _cairo_path_fixed_fini (&path);
+ }
+
+ if (mask != NULL) {
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = i965_surface_mask_internal (surface, op, source, mask,
+ clip, &extents);
+ }
+ cairo_surface_finish (&mask->intel.drm.base);
+ cairo_surface_destroy (&mask->intel.drm.base);
+ }
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
diff --git a/src/drm/cairo-drm-i965-private.h b/src/drm/cairo-drm-i965-private.h
new file mode 100755
index 000000000..79568a63d
--- /dev/null
+++ b/src/drm/cairo-drm-i965-private.h
@@ -0,0 +1,737 @@
+#ifndef CAIRO_DRM_I965_PRIVATE_H
+#define CAIRO_DRM_I965_PRIVATE_H
+
+#include "cairo-drm-intel-private.h"
+
+#include "cairo-hash-private.h"
+#include "cairo-freelist-private.h"
+
+#include "cairo-drm-intel-brw-defines.h"
+
+#include <setjmp.h>
+
+#define BRW_MI_GLOBAL_SNAPSHOT_RESET (1 << 3)
+
+/*
+ * New regs for broadwater -- we need to split this file up sensibly somehow.
+ */
+#define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \
+ ((Pipeline) << 27) | \
+ ((Opcode) << 24) | \
+ ((Subopcode) << 16))
+
+#define BRW_URB_FENCE BRW_3D(0, 0, 0)
+#define BRW_CS_URB_STATE BRW_3D(0, 0, 1)
+#define BRW_CONSTANT_BUFFER BRW_3D(0, 0, 2)
+#define BRW_STATE_PREFETCH BRW_3D(0, 0, 3)
+
+#define BRW_STATE_BASE_ADDRESS BRW_3D(0, 1, 1)
+#define BRW_STATE_SIP BRW_3D(0, 1, 2)
+#define BRW_PIPELINE_SELECT BRW_3D(0, 1, 4)
+
+#define NEW_PIPELINE_SELECT BRW_3D(1, 1, 4)
+
+#define BRW_MEDIA_STATE_POINTERS BRW_3D(2, 0, 0)
+#define BRW_MEDIA_OBJECT BRW_3D(2, 1, 0)
+
+#define BRW_3DSTATE_PIPELINED_POINTERS BRW_3D(3, 0, 0)
+#define BRW_3DSTATE_BINDING_TABLE_POINTERS BRW_3D(3, 0, 1)
+#define BRW_3DSTATE_VERTEX_BUFFERS BRW_3D(3, 0, 8)
+#define BRW_3DSTATE_VERTEX_ELEMENTS BRW_3D(3, 0, 9)
+#define BRW_3DSTATE_INDEX_BUFFER BRW_3D(3, 0, 0xa)
+#define BRW_3DSTATE_VF_STATISTICS BRW_3D(3, 0, 0xb)
+
+#define BRW_3DSTATE_DRAWING_RECTANGLE BRW_3D(3, 1, 0)
+#define BRW_3DSTATE_CONSTANT_COLOR BRW_3D(3, 1, 1)
+#define BRW_3DSTATE_SAMPLER_PALETTE_LOAD BRW_3D(3, 1, 2)
+#define BRW_3DSTATE_CHROMA_KEY BRW_3D(3, 1, 4)
+#define BRW_3DSTATE_DEPTH_BUFFER BRW_3D(3, 1, 5)
+#define BRW_3DSTATE_POLY_STIPPLE_OFFSET BRW_3D(3, 1, 6)
+#define BRW_3DSTATE_POLY_STIPPLE_PATTERN BRW_3D(3, 1, 7)
+#define BRW_3DSTATE_LINE_STIPPLE BRW_3D(3, 1, 8)
+#define BRW_3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP BRW_3D(3, 1, 9)
+/* These two are BLC and CTG only, not BW or CL */
+#define BRW_3DSTATE_AA_LINE_PARAMS BRW_3D(3, 1, 0xa)
+#define BRW_3DSTATE_GS_SVB_INDEX BRW_3D(3, 1, 0xb)
+
+#define BRW_PIPE_CONTROL BRW_3D(3, 2, 0)
+
+#define BRW_3DPRIMITIVE BRW_3D(3, 3, 0)
+
+#define PIPELINE_SELECT_3D 0
+#define PIPELINE_SELECT_MEDIA 1
+
+#define UF0_CS_REALLOC (1 << 13)
+#define UF0_VFE_REALLOC (1 << 12)
+#define UF0_SF_REALLOC (1 << 11)
+#define UF0_CLIP_REALLOC (1 << 10)
+#define UF0_GS_REALLOC (1 << 9)
+#define UF0_VS_REALLOC (1 << 8)
+#define UF1_CLIP_FENCE_SHIFT 20
+#define UF1_GS_FENCE_SHIFT 10
+#define UF1_VS_FENCE_SHIFT 0
+#define UF2_CS_FENCE_SHIFT 20
+#define UF2_VFE_FENCE_SHIFT 10
+#define UF2_SF_FENCE_SHIFT 0
+
+/* for BRW_STATE_BASE_ADDRESS */
+#define BASE_ADDRESS_MODIFY (1 << 0)
+
+/* for BRW_3DSTATE_PIPELINED_POINTERS */
+#define BRW_GS_DISABLE 0
+#define BRW_GS_ENABLE 1
+#define BRW_CLIP_DISABLE 0
+#define BRW_CLIP_ENABLE 1
+
+/* for BRW_PIPE_CONTROL */
+#define BRW_PIPE_CONTROL_NOWRITE (0 << 14)
+#define BRW_PIPE_CONTROL_WRITE_QWORD (1 << 14)
+#define BRW_PIPE_CONTROL_WRITE_DEPTH (2 << 14)
+#define BRW_PIPE_CONTROL_WRITE_TIME (3 << 14)
+#define BRW_PIPE_CONTROL_DEPTH_STALL (1 << 13)
+#define BRW_PIPE_CONTROL_WC_FLUSH (1 << 12)
+#define BRW_PIPE_CONTROL_IS_FLUSH (1 << 11)
+#define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8)
+#define BRW_PIPE_CONTROL_GLOBAL_GTT (1 << 2)
+#define BRW_PIPE_CONTROL_LOCAL_PGTT (0 << 2)
+
+/* VERTEX_BUFFER_STATE Structure */
+#define VB0_BUFFER_INDEX_SHIFT 27
+#define VB0_VERTEXDATA (0 << 26)
+#define VB0_INSTANCEDATA (1 << 26)
+#define VB0_BUFFER_PITCH_SHIFT 0
+
+/* VERTEX_ELEMENT_STATE Structure */
+#define VE0_VERTEX_BUFFER_INDEX_SHIFT 27
+#define VE0_VALID (1 << 26)
+#define VE0_FORMAT_SHIFT 16
+#define VE0_OFFSET_SHIFT 0
+#define VE1_VFCOMPONENT_0_SHIFT 28
+#define VE1_VFCOMPONENT_1_SHIFT 24
+#define VE1_VFCOMPONENT_2_SHIFT 20
+#define VE1_VFCOMPONENT_3_SHIFT 16
+#define VE1_DESTINATION_ELEMENT_OFFSET_SHIFT 0
+
+/* 3DPRIMITIVE bits */
+#define BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL (0 << 15)
+#define BRW_3DPRIMITIVE_VERTEX_RANDOM (1 << 15)
+/* Primitive types are in brw_defines.h */
+#define BRW_3DPRIMITIVE_TOPOLOGY_SHIFT 10
+
+#define BRW_SVG_CTL 0x7400
+
+#define BRW_SVG_CTL_GS_BA (0 << 8)
+#define BRW_SVG_CTL_SS_BA (1 << 8)
+#define BRW_SVG_CTL_IO_BA (2 << 8)
+#define BRW_SVG_CTL_GS_AUB (3 << 8)
+#define BRW_SVG_CTL_IO_AUB (4 << 8)
+#define BRW_SVG_CTL_SIP (5 << 8)
+
+#define BRW_SVG_RDATA 0x7404
+#define BRW_SVG_WORK_CTL 0x7408
+
+#define BRW_VF_CTL 0x7500
+
+#define BRW_VF_CTL_SNAPSHOT_COMPLETE (1 << 31)
+#define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_THREADID (0 << 8)
+#define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_VF_DEBUG (1 << 8)
+#define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_SEQUENCE (0 << 4)
+#define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_INDEX (1 << 4)
+#define BRW_VF_CTL_SKIP_INITIAL_PRIMITIVES (1 << 3)
+#define BRW_VF_CTL_MAX_PRIMITIVES_LIMIT_ENABLE (1 << 2)
+#define BRW_VF_CTL_VERTEX_RANGE_LIMIT_ENABLE (1 << 1)
+#define BRW_VF_CTL_SNAPSHOT_ENABLE (1 << 0)
+
+#define BRW_VF_STRG_VAL 0x7504
+#define BRW_VF_STR_VL_OVR 0x7508
+#define BRW_VF_VC_OVR 0x750c
+#define BRW_VF_STR_PSKIP 0x7510
+#define BRW_VF_MAX_PRIM 0x7514
+#define BRW_VF_RDATA 0x7518
+
+#define BRW_VS_CTL 0x7600
+#define BRW_VS_CTL_SNAPSHOT_COMPLETE (1 << 31)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_0 (0 << 8)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_1 (1 << 8)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VALID_COUNT (2 << 8)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VS_KERNEL_POINTER (3 << 8)
+#define BRW_VS_CTL_SNAPSHOT_ALL_THREADS (1 << 2)
+#define BRW_VS_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1)
+#define BRW_VS_CTL_SNAPSHOT_ENABLE (1 << 0)
+
+#define BRW_VS_STRG_VAL 0x7604
+#define BRW_VS_RDATA 0x7608
+
+#define BRW_SF_CTL 0x7b00
+#define BRW_SF_CTL_SNAPSHOT_COMPLETE (1 << 31)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_FF_ID (0 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_REL_COUNT (1 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_FF_ID (2 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_REL_COUNT (3 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_FF_ID (4 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_REL_COUNT (5 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_COUNT (6 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_SF_KERNEL_POINTER (7 << 8)
+#define BRW_SF_CTL_MIN_MAX_PRIMITIVE_RANGE_ENABLE (1 << 4)
+#define BRW_SF_CTL_DEBUG_CLIP_RECTANGLE_ENABLE (1 << 3)
+#define BRW_SF_CTL_SNAPSHOT_ALL_THREADS (1 << 2)
+#define BRW_SF_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1)
+#define BRW_SF_CTL_SNAPSHOT_ENABLE (1 << 0)
+
+#define BRW_SF_STRG_VAL 0x7b04
+#define BRW_SF_RDATA 0x7b18
+
+#define BRW_WIZ_CTL 0x7c00
+#define BRW_WIZ_CTL_SNAPSHOT_COMPLETE (1 << 31)
+#define BRW_WIZ_CTL_SUBSPAN_INSTANCE_SHIFT 16
+#define BRW_WIZ_CTL_SNAPSHOT_MUX_WIZ_KERNEL_POINTER (0 << 8)
+#define BRW_WIZ_CTL_SNAPSHOT_MUX_SUBSPAN_INSTANCE (1 << 8)
+#define BRW_WIZ_CTL_SNAPSHOT_MUX_PRIMITIVE_SEQUENCE (2 << 8)
+#define BRW_WIZ_CTL_SINGLE_SUBSPAN_DISPATCH (1 << 6)
+#define BRW_WIZ_CTL_IGNORE_COLOR_SCOREBOARD_STALLS (1 << 5)
+#define BRW_WIZ_CTL_ENABLE_SUBSPAN_INSTANCE_COMPARE (1 << 4)
+#define BRW_WIZ_CTL_USE_UPSTREAM_SNAPSHOT_FLAG (1 << 3)
+#define BRW_WIZ_CTL_SNAPSHOT_ALL_THREADS (1 << 2)
+#define BRW_WIZ_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1)
+#define BRW_WIZ_CTL_SNAPSHOT_ENABLE (1 << 0)
+
+#define BRW_WIZ_STRG_VAL 0x7c04
+#define BRW_WIZ_RDATA 0x7c18
+
+#define BRW_TS_CTL 0x7e00
+#define BRW_TS_CTL_SNAPSHOT_COMPLETE (1 << 31)
+#define BRW_TS_CTL_SNAPSHOT_MESSAGE_ERROR (0 << 8)
+#define BRW_TS_CTL_SNAPSHOT_INTERFACE_DESCRIPTOR (3 << 8)
+#define BRW_TS_CTL_SNAPSHOT_ALL_CHILD_THREADS (1 << 2)
+#define BRW_TS_CTL_SNAPSHOT_ALL_ROOT_THREADS (1 << 1)
+#define BRW_TS_CTL_SNAPSHOT_ENABLE (1 << 0)
+
+#define BRW_TS_STRG_VAL 0x7e04
+#define BRW_TS_RDATA 0x7e08
+
+#define BRW_TD_CTL 0x8000
+#define BRW_TD_CTL_MUX_SHIFT 8
+#define BRW_TD_CTL_EXTERNAL_HALT_R0_DEBUG_MATCH (1 << 7)
+#define BRW_TD_CTL_FORCE_EXTERNAL_HALT (1 << 6)
+#define BRW_TD_CTL_EXCEPTION_MASK_OVERRIDE (1 << 5)
+#define BRW_TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE (1 << 4)
+#define BRW_TD_CTL_BREAKPOINT_ENABLE (1 << 2)
+#define BRW_TD_CTL2 0x8004
+#define BRW_TD_CTL2_ILLEGAL_OPCODE_EXCEPTION_OVERRIDE (1 << 28)
+#define BRW_TD_CTL2_MASKSTACK_EXCEPTION_OVERRIDE (1 << 26)
+#define BRW_TD_CTL2_SOFTWARE_EXCEPTION_OVERRIDE (1 << 25)
+#define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_SHIFT 16
+#define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_ENABLE (1 << 8)
+#define BRW_TD_CTL2_THREAD_SPAWNER_EXECUTION_MASK_ENABLE (1 << 7)
+#define BRW_TD_CTL2_WIZ_EXECUTION_MASK_ENABLE (1 << 6)
+#define BRW_TD_CTL2_SF_EXECUTION_MASK_ENABLE (1 << 5)
+#define BRW_TD_CTL2_CLIPPER_EXECUTION_MASK_ENABLE (1 << 4)
+#define BRW_TD_CTL2_GS_EXECUTION_MASK_ENABLE (1 << 3)
+#define BRW_TD_CTL2_VS_EXECUTION_MASK_ENABLE (1 << 0)
+#define BRW_TD_VF_VS_EMSK 0x8008
+#define BRW_TD_GS_EMSK 0x800c
+#define BRW_TD_CLIP_EMSK 0x8010
+#define BRW_TD_SF_EMSK 0x8014
+#define BRW_TD_WIZ_EMSK 0x8018
+#define BRW_TD_0_6_EHTRG_VAL 0x801c
+#define BRW_TD_0_7_EHTRG_VAL 0x8020
+#define BRW_TD_0_6_EHTRG_MSK 0x8024
+#define BRW_TD_0_7_EHTRG_MSK 0x8028
+#define BRW_TD_RDATA 0x802c
+#define BRW_TD_TS_EMSK 0x8030
+
+#define BRW_EU_CTL 0x8800
+#define BRW_EU_CTL_SELECT_SHIFT 16
+#define BRW_EU_CTL_DATA_MUX_SHIFT 8
+#define BRW_EU_ATT_0 0x8810
+#define BRW_EU_ATT_1 0x8814
+#define BRW_EU_ATT_DATA_0 0x8820
+#define BRW_EU_ATT_DATA_1 0x8824
+#define BRW_EU_ATT_CLR_0 0x8830
+#define BRW_EU_ATT_CLR_1 0x8834
+#define BRW_EU_RDATA 0x8840
+
+typedef struct i965_device i965_device_t;
+typedef struct i965_surface i965_surface_t;
+typedef struct i965_shader i965_shader_t;
+typedef struct i965_stream i965_stream_t;
+
+struct i965_sf_state {
+ cairo_hash_entry_t entry;
+ uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_sf_state_equal (const void *, const void *);
+
+struct i965_cc_state {
+ cairo_hash_entry_t entry;
+ uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_cc_state_equal (const void *, const void *);
+
+struct i965_wm_kernel {
+ cairo_hash_entry_t entry;
+ uint32_t offset;
+};
+
+struct i965_wm_state {
+ cairo_hash_entry_t entry;
+ uint32_t kernel;
+ uint32_t sampler;
+ uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_wm_state_equal (const void *, const void *);
+
+struct i965_wm_binding {
+ cairo_hash_entry_t entry;
+ uint32_t table[4];
+ int size;
+ uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_wm_binding_equal (const void *, const void *);
+
+struct i965_sampler {
+ cairo_hash_entry_t entry;
+ uint32_t offset;
+};
+
+struct i965_vbo {
+ struct i965_vbo *next;
+ intel_bo_t *bo;
+ unsigned int count;
+};
+
+struct i965_surface {
+ intel_surface_t intel;
+
+ uint32_t stream;
+ uint32_t offset;
+};
+
+struct i965_pending_relocation {
+ uint32_t offset;
+ uint32_t read_domains;
+ uint32_t write_domain;
+ uint32_t delta;
+};
+
+struct i965_stream {
+ uint32_t used;
+ uint32_t committed;
+ uint32_t size;
+ uint8_t *data;
+ uint32_t serial;
+
+ int num_pending_relocations;
+ int max_pending_relocations;
+ struct i965_pending_relocation *pending_relocations;
+
+ int num_relocations;
+ int max_relocations;
+ struct drm_i915_gem_relocation_entry *relocations;
+};
+
+#define I965_BATCH_SIZE (16 * 4096)
+#define I965_GENERAL_SIZE (16 * 4096)
+#define I965_SURFACE_SIZE (32 * 4096)
+#define I965_VERTEX_SIZE (128 * 4096)
+
+#define I965_TILING_DEFAULT I915_TILING_Y
+
+
+struct i965_device {
+ intel_device_t intel;
+
+ cairo_bool_t is_g4x;
+
+ i965_shader_t *shader; /* note: only valid during geometry emission */
+
+ /* track state changes */
+ struct i965_sf_state sf_state;
+ struct i965_cc_state cc_state;
+ struct i965_wm_state wm_state;
+ struct i965_wm_binding wm_binding;
+
+ i965_surface_t *target;
+ uint32_t target_offset;
+
+ intel_bo_t *source;
+ uint32_t source_offset;
+
+ intel_bo_t *mask;
+ uint32_t mask_offset;
+
+ intel_bo_t *clip;
+ uint32_t clip_offset;
+
+ uint32_t draw_rectangle;
+
+ uint32_t vs_offset;
+ uint32_t border_color_offset;
+ cairo_hash_table_t *sf_states;
+ cairo_hash_table_t *cc_states;
+ cairo_hash_table_t *wm_kernels;
+ cairo_hash_table_t *wm_states;
+ cairo_hash_table_t *wm_bindings;
+ cairo_hash_table_t *samplers;
+ intel_bo_t *general_state;
+
+ cairo_freelist_t sf_freelist;
+ cairo_freelist_t cc_freelist;
+ cairo_freelist_t wm_kernel_freelist;
+ cairo_freelist_t wm_state_freelist;
+ cairo_freelist_t wm_binding_freelist;
+ cairo_freelist_t sampler_freelist;
+
+ uint32_t vertex_type;
+ uint32_t vertex_size;
+ uint32_t rectangle_size;
+ uint32_t last_vertex_size;
+
+ float *constants; /* 4 x matrix + 2 x source */
+ unsigned constants_size;
+ cairo_bool_t have_urb_fences;
+
+ i965_stream_t batch;
+ uint8_t batch_base[I965_BATCH_SIZE];
+ struct drm_i915_gem_relocation_entry batch_relocations[2048];
+
+ i965_stream_t surface;
+ uint8_t surface_base[I965_SURFACE_SIZE];
+ struct i965_pending_relocation surface_pending_relocations[1];
+ struct drm_i915_gem_relocation_entry surface_relocations[1024];
+
+ i965_stream_t general;
+ uint8_t general_base[I965_GENERAL_SIZE];
+ struct i965_pending_relocation general_pending_relocations[1];
+
+ i965_stream_t vertex;
+ uint8_t vertex_base[I965_VERTEX_SIZE];
+ struct i965_pending_relocation vertex_pending_relocations[512];
+
+ struct {
+ size_t gtt_size;
+
+ intel_bo_t *bo[1024];
+ int count;
+
+ struct drm_i915_gem_exec_object2 exec[1024];
+ } exec;
+ cairo_list_t flush;
+};
+
+typedef enum {
+ VS_NONE = 0,
+ VS_GLYPHS,
+ VS_SPANS,
+} i965_vertex_shader_t;
+
+typedef enum {
+ FS_NONE = 0,
+ FS_CONSTANT,
+ FS_LINEAR,
+ FS_RADIAL,
+ FS_SURFACE,
+ FS_GLYPHS,
+ FS_SPANS,
+} i965_fragment_shader_t;
+
+typedef enum {
+ PATTERN_BASE,
+ PATTERN_SOLID,
+ PATTERN_LINEAR,
+ PATTERN_RADIAL,
+ PATTERN_SURFACE,
+} i965_shader_channel_t;
+#define PATTERN_NONE (i965_shader_channel_t)-1
+
+struct i965_shader {
+ i965_device_t *device;
+ i965_surface_t *target;
+
+ cairo_operator_t op;
+
+ cairo_bool_t committed;
+ cairo_bool_t need_combine;
+
+ float constants[4*8 + 2*8]; /* 4 x matrix + 2 x source */
+ unsigned constants_size;
+
+ union i965_shader_channel {
+ struct {
+ i965_vertex_shader_t vertex;
+ i965_fragment_shader_t fragment;
+ i965_shader_channel_t pattern;
+ } type;
+ struct i965_shader_base {
+ i965_vertex_shader_t vertex;
+ i965_fragment_shader_t fragment;
+ i965_shader_channel_t pattern;
+
+ uint32_t mode;
+
+ float constants[8];
+ unsigned constants_size;
+
+ intel_bo_t *bo;
+ cairo_format_t format;
+ cairo_content_t content;
+ int width, height, stride;
+ int filter, extend;
+ cairo_matrix_t matrix;
+ cairo_bool_t has_component_alpha;
+ } base;
+ struct i965_shader_solid {
+ struct i965_shader_base base;
+ } solid;
+ struct i965_shader_linear {
+ struct i965_shader_base base;
+ } linear;
+ struct i965_shader_radial {
+ struct i965_shader_base base;
+ } radial;
+ struct i965_shader_surface {
+ struct i965_shader_base base;
+ cairo_surface_t *surface;
+ } surface;
+ } source, mask, clip, dst;
+
+ jmp_buf unwind;
+};
+
+enum i965_shader_linear_mode {
+ /* XXX REFLECT */
+ LINEAR_TEXTURE,
+ LINEAR_NONE,
+ LINEAR_REPEAT,
+ LINEAR_PAD,
+};
+
+enum i965_shader_radial_mode {
+ RADIAL_ONE,
+ RADIAL_TWO
+};
+
+typedef cairo_status_t
+(*i965_spans_func_t) (void *closure,
+ cairo_span_renderer_t *renderer,
+ const cairo_rectangle_int_t *extents);
+
+static inline i965_device_t *
+i965_device (i965_surface_t *surface)
+{
+ return (i965_device_t *) surface->intel.drm.base.device;
+}
+
+cairo_private void
+i965_emit_relocation (i965_device_t *device,
+ i965_stream_t *stream,
+ intel_bo_t *target,
+ uint32_t target_offset,
+ uint32_t read_domains,
+ uint32_t write_domain,
+ uint32_t offset);
+
+static cairo_always_inline uint32_t
+i965_stream_emit (i965_stream_t *stream, const void *data, size_t size)
+{
+ uint32_t offset;
+
+ offset = stream->used;
+ assert (offset + size <= stream->size);
+ memcpy (stream->data + offset, data, size);
+ stream->used += size;
+
+ return offset;
+}
+
+static cairo_always_inline void
+i965_stream_align (i965_stream_t *stream, uint32_t size)
+{
+ stream->used = (stream->used + size - 1) & -size;
+}
+
+static cairo_always_inline void *
+i965_stream_alloc (i965_stream_t *stream, uint32_t align, uint32_t size)
+{
+ void *ptr;
+
+ if (align)
+ i965_stream_align (stream, align);
+
+ assert (stream->used + size <= stream->size);
+ ptr = stream->data + stream->used;
+ stream->used += size;
+
+ return ptr;
+}
+
+static cairo_always_inline uint32_t
+i965_stream_offsetof (i965_stream_t *stream, const void *ptr)
+{
+ return (char *) ptr - (char *) stream->data;
+}
+
+cairo_private void
+i965_stream_commit (i965_device_t *device,
+ i965_stream_t *stream);
+
+cairo_private void
+i965_general_state_reset (i965_device_t *device);
+
+static inline void
+i965_batch_emit_dword (i965_device_t *device, uint32_t dword)
+{
+ *(uint32_t *) (device->batch.data + device->batch.used) = dword;
+ device->batch.used += 4;
+}
+
+#define OUT_BATCH(dword) i965_batch_emit_dword(device, dword)
+
+cairo_private void
+i965_clipped_vertices (i965_device_t *device,
+ struct i965_vbo *vbo,
+ cairo_region_t *clip_region);
+
+cairo_private void
+i965_flush_vertices (i965_device_t *device);
+
+cairo_private void
+i965_finish_vertices (i965_device_t *device);
+
+static inline float *
+i965_add_rectangle (i965_device_t *device)
+{
+ float *vertices;
+ uint32_t size;
+
+ size = device->rectangle_size;
+ if (unlikely (device->vertex.used + size > device->vertex.size))
+ i965_finish_vertices (device);
+
+ vertices = (float *) (device->vertex.data + device->vertex.used);
+ device->vertex.used += size;
+
+ return vertices;
+}
+
+static inline void
+i965_shader_add_rectangle (const i965_shader_t *shader,
+ int x, int y,
+ int w, int h)
+{
+ float *v;
+
+ v= i965_add_rectangle (shader->device);
+
+ /* bottom-right */
+ *v++ = x + w;
+ *v++ = y + h;
+
+ /* bottom-left */
+ *v++ = x;
+ *v++ = y + h;
+
+ /* top-left */
+ *v++ = x;
+ *v++ = y;
+}
+
+cairo_private cairo_surface_t *
+i965_surface_create_internal (cairo_drm_device_t *base_dev,
+ cairo_format_t format,
+ int width, int height,
+ uint32_t tiling,
+ cairo_bool_t gpu_target);
+
+cairo_private cairo_status_t
+i965_clip_and_composite_spans (i965_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_antialias_t antialias,
+ i965_spans_func_t draw_func,
+ void *draw_closure,
+ const cairo_composite_rectangles_t*extents,
+ cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+i965_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining);
+
+cairo_private void
+i965_shader_init (i965_shader_t *shader,
+ i965_surface_t *dst,
+ cairo_operator_t op);
+
+cairo_private cairo_status_t
+i965_shader_acquire_pattern (i965_shader_t *shader,
+ union i965_shader_channel *src,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private void
+i965_shader_set_clip (i965_shader_t *shader,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+i965_shader_commit (i965_shader_t *shader,
+ i965_device_t *device);
+
+cairo_private void
+i965_shader_fini (i965_shader_t *shader);
+
+cairo_private cairo_status_t
+i965_device_flush (i965_device_t *device);
+
+cairo_private cairo_status_t
+i965_fixup_unbounded (i965_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip);
+
+static inline int
+i965_filter (cairo_filter_t filter)
+{
+ switch (filter) {
+ default:
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ return BRW_MAPFILTER_NEAREST;
+
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return BRW_MAPFILTER_LINEAR;
+ }
+}
+
+static inline int
+i965_extend (cairo_extend_t extend)
+{
+ switch (extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ return BRW_TEXCOORDMODE_CLAMP_BORDER;
+ case CAIRO_EXTEND_REPEAT:
+ return BRW_TEXCOORDMODE_WRAP;
+ case CAIRO_EXTEND_PAD:
+ return BRW_TEXCOORDMODE_CLAMP;
+ case CAIRO_EXTEND_REFLECT:
+ return BRW_TEXCOORDMODE_MIRROR;
+ }
+}
+
+#endif /* CAIRO_DRM_I965_PRIVATE_H */
diff --git a/src/drm/cairo-drm-i965-shader.c b/src/drm/cairo-drm-i965-shader.c
new file mode 100755
index 000000000..eed5f5f09
--- /dev/null
+++ b/src/drm/cairo-drm-i965-shader.c
@@ -0,0 +1,2825 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Kristian Høgsberg <krh@bitplanet.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-drm-i965-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-private.h"
+
+#include "cairo-drm-intel-brw-eu.h"
+
+/* Theory of shaders:
+ *
+ * 3 types of rectangular inputs:
+ * (a) standard composite: x,y, use source, mask matrices to compute texcoords
+ * (b) spans: x,y, alpha, use source matrix
+ * (c) glyphs: x,y, s,t, use source matrix
+ *
+ * 5 types of pixel shaders:
+ * (a) Solid colour
+ * (b) Linear gradient (via 1D texture, with precomputed tex)
+ * (c) Radial gradient (per-pixel s computation, 1D texture)
+ * (d) Spans (mask only): apply opacity
+ * (e) Texture (includes glyphs).
+ *
+ * Clip masks are limited to 2D textures only.
+ */
+
+/* XXX dual source blending for LERP + ComponentAlpha!!! */
+
+#define BRW_GRF_BLOCKS(nreg) ((nreg + 15) / 16 - 1)
+
+#define SF_KERNEL_NUM_GRF 1
+#define SF_MAX_THREADS 24
+
+#define PS_MAX_THREADS_CTG 50
+#define PS_MAX_THREADS_BRW 32
+
+#define URB_CS_ENTRY_SIZE 3 /* We need 4 matrices + 2 sources */
+#define URB_CS_ENTRIES 4 /* 4x sets of CONSTANT_BUFFER */
+
+#define URB_VS_ENTRY_SIZE 1
+#define URB_VS_ENTRIES 8
+
+#define URB_GS_ENTRY_SIZE 0
+#define URB_GS_ENTRIES 0
+
+#define URB_CLIP_ENTRY_SIZE 0
+#define URB_CLIP_ENTRIES 0
+
+#define URB_SF_ENTRY_SIZE 1
+#define URB_SF_ENTRIES (SF_MAX_THREADS + 1)
+
+static void
+i965_pipelined_flush (i965_device_t *device)
+{
+ intel_bo_t *bo, *next;
+
+ if (device->batch.used == 0)
+ return;
+
+ OUT_BATCH (BRW_PIPE_CONTROL |
+ BRW_PIPE_CONTROL_NOWRITE |
+ BRW_PIPE_CONTROL_WC_FLUSH |
+ 2);
+ OUT_BATCH(0); /* Destination address */
+ OUT_BATCH(0); /* Immediate data low DW */
+ OUT_BATCH(0); /* Immediate data high DW */
+
+ cairo_list_foreach_entry_safe (bo, next, intel_bo_t, &device->flush, link) {
+ bo->batch_write_domain = 0;
+ cairo_list_init (&bo->link);
+ }
+ cairo_list_init (&device->flush);
+}
+
+static cairo_status_t
+i965_shader_acquire_solid (i965_shader_t *shader,
+ union i965_shader_channel *src,
+ const cairo_solid_pattern_t *solid,
+ const cairo_rectangle_int_t *extents)
+{
+ src->type.fragment = FS_CONSTANT;
+ src->type.vertex = VS_NONE;
+ src->type.pattern = PATTERN_SOLID;
+
+ src->base.content = _cairo_color_get_content (&solid->color);
+ src->base.constants[0] = solid->color.red * solid->color.alpha;
+ src->base.constants[1] = solid->color.green * solid->color.alpha;
+ src->base.constants[2] = solid->color.blue * solid->color.alpha;
+ src->base.constants[3] = solid->color.alpha;
+ src->base.constants_size = 4;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_linear (i965_shader_t *shader,
+ union i965_shader_channel *src,
+ const cairo_linear_pattern_t *linear,
+ const cairo_rectangle_int_t *extents)
+{
+ intel_buffer_t buffer;
+ cairo_status_t status;
+ double x0, y0, sf;
+ double dx, dy, offset;
+
+ status = intel_gradient_render (&i965_device (shader->target)->intel,
+ &linear->base, &buffer);
+ if (unlikely (status))
+ return status;
+
+ src->type.vertex = VS_NONE;
+ src->type.pattern = PATTERN_LINEAR;
+ src->type.fragment = FS_LINEAR;
+ src->base.bo = buffer.bo;
+ src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+ src->base.format = buffer.format;
+ src->base.width = buffer.width;
+ src->base.height = buffer.height;
+ src->base.stride = buffer.stride;
+ src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR);
+ src->base.extend = i965_extend (linear->base.base.extend);
+
+ dx = linear->pd2.x - linear->pd1.x;
+ dy = linear->pd2.y - linear->pd1.y;
+ sf = 1. / (dx * dx + dy * dy);
+ dx *= sf;
+ dy *= sf;
+
+ x0 = linear->pd1.x;
+ y0 = linear->pd1.y;
+ offset = dx*x0 + dy*y0;
+
+ if (_cairo_matrix_is_identity (&linear->base.base.matrix)) {
+ src->base.matrix.xx = dx;
+ src->base.matrix.xy = dy;
+ src->base.matrix.x0 = -offset;
+ } else {
+ cairo_matrix_t m;
+
+ cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0);
+ cairo_matrix_multiply (&src->base.matrix, &linear->base.base.matrix, &m);
+ }
+ src->base.matrix.yx = 0.;
+ src->base.matrix.yy = 1.;
+ src->base.matrix.y0 = 0.;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_radial (i965_shader_t *shader,
+ union i965_shader_channel *src,
+ const cairo_radial_pattern_t *radial,
+ const cairo_rectangle_int_t *extents)
+{
+ intel_buffer_t buffer;
+ cairo_status_t status;
+ double dx, dy, dr, r1;
+
+ status = intel_gradient_render (&i965_device (shader->target)->intel,
+ &radial->base, &buffer);
+ if (unlikely (status))
+ return status;
+
+ src->type.vertex = VS_NONE;
+ src->type.pattern = PATTERN_RADIAL;
+ src->type.fragment = FS_RADIAL;
+ src->base.bo = buffer.bo;
+ src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+ src->base.format = buffer.format;
+ src->base.width = buffer.width;
+ src->base.height = buffer.height;
+ src->base.stride = buffer.stride;
+ src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR);
+ src->base.extend = i965_extend (radial->base.base.extend);
+
+ dx = radial->cd2.center.x - radial->cd1.center.x;
+ dy = radial->cd2.center.y - radial->cd1.center.y;
+ dr = radial->cd2.radius - radial->cd1.radius;
+
+ r1 = radial->cd1.radius;
+
+ if (FALSE && (radial->cd2.center.x == radial->cd1.center.x &&
+ radial->cd2.center.y == radial->cd1.center.y))
+ {
+ /* XXX dr == 0, meaningless with anything other than PAD */
+ src->base.constants[0] = radial->cd1.center.x / dr;
+ src->base.constants[1] = radial->cd1.center.y / dr;
+ src->base.constants[2] = 1. / dr;
+ src->base.constants[3] = -r1 / dr;
+
+ src->base.constants_size = 4;
+ src->base.mode = RADIAL_ONE;
+ } else {
+ src->base.constants[0] = -radial->cd1.center.x;
+ src->base.constants[1] = -radial->cd1.center.y;
+ src->base.constants[2] = r1;
+ src->base.constants[3] = -4 * (dx*dx + dy*dy - dr*dr);
+
+ src->base.constants[4] = -2 * dx;
+ src->base.constants[5] = -2 * dy;
+ src->base.constants[6] = -2 * r1 * dr;
+ src->base.constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr));
+
+ src->base.constants_size = 8;
+ src->base.mode = RADIAL_TWO;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_surface_clone (i965_device_t *device,
+ cairo_image_surface_t *image,
+ i965_surface_t **clone_out)
+{
+ i965_surface_t *clone;
+ cairo_status_t status;
+
+ clone = (i965_surface_t *)
+ i965_surface_create_internal (&device->intel.base,
+ image->base.content,
+ image->width,
+ image->height,
+ I965_TILING_DEFAULT,
+ FALSE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ status = intel_bo_put_image (&device->intel,
+ to_intel_bo (clone->intel.drm.bo),
+ image,
+ 0, 0,
+ image->width, image->height,
+ 0, 0);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return status;
+ }
+
+ status = intel_snapshot_cache_insert (&device->intel, &clone->intel);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return status;
+ }
+
+ _cairo_surface_attach_snapshot (&image->base,
+ &clone->intel.drm.base,
+ intel_surface_detach_snapshot);
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_surface_clone_subimage (i965_device_t *device,
+ cairo_image_surface_t *image,
+ const cairo_rectangle_int_t *extents,
+ i965_surface_t **clone_out)
+{
+ i965_surface_t *clone;
+ cairo_status_t status;
+
+ clone = (i965_surface_t *)
+ i965_surface_create_internal (&device->intel.base,
+ image->base.content,
+ extents->width,
+ extents->height,
+ I965_TILING_DEFAULT,
+ FALSE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ status = intel_bo_put_image (to_intel_device (clone->intel.drm.base.device),
+ to_intel_bo (clone->intel.drm.bo),
+ image,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ 0, 0);
+ if (unlikely (status))
+ return status;
+
+ *clone_out = clone;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_solid_surface (i965_shader_t *shader,
+ union i965_shader_channel *src,
+ cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+ uint32_t argb;
+
+ status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ if (image->format != CAIRO_FORMAT_ARGB32) {
+ cairo_surface_t *pixel;
+ cairo_surface_pattern_t pattern;
+
+ /* extract the pixel as argb32 */
+ pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+ _cairo_pattern_init_for_surface (&pattern, &image->base);
+ cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status)) {
+ _cairo_surface_release_source_image (surface, image, image_extra);
+ cairo_surface_destroy (pixel);
+ return status;
+ }
+
+ argb = *(uint32_t *) ((cairo_image_surface_t *) pixel)->data;
+ cairo_surface_destroy (pixel);
+ } else {
+ argb = ((uint32_t *) (image->data + extents->y * image->stride))[extents->x];
+ }
+
+ _cairo_surface_release_source_image (surface, image, image_extra);
+
+ if (argb >> 24 == 0)
+ argb = 0;
+
+ src->base.constants[0] = ((argb >> 16) & 0xff) / 255.;
+ src->base.constants[1] = ((argb >> 8) & 0xff) / 255.;
+ src->base.constants[2] = ((argb >> 0) & 0xff) / 255.;
+ src->base.constants[3] = ((argb >> 24) & 0xff) / 255.;
+ src->base.constants_size = 4;
+
+ src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+ if (CAIRO_ALPHA_IS_OPAQUE(src->base.constants[3]))
+ src->base.content &= ~CAIRO_CONTENT_ALPHA;
+ src->type.fragment = FS_CONSTANT;
+ src->type.vertex = VS_NONE;
+ src->type.pattern = PATTERN_SOLID;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_surface (i965_shader_t *shader,
+ union i965_shader_channel *src,
+ const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *surface, *drm;
+ cairo_matrix_t m;
+ cairo_status_t status;
+ int src_x = 0, src_y = 0;
+
+ assert (src->type.fragment == FS_NONE);
+ drm = surface = pattern->surface;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_DRM) {
+ if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ drm = ((cairo_surface_subsurface_t *) surface)->target;
+ } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+ drm = ((cairo_surface_snapshot_t *) surface)->target;
+ }
+ }
+
+ src->type.pattern = PATTERN_SURFACE;
+ src->surface.surface = NULL;
+ if (drm->type == CAIRO_SURFACE_TYPE_DRM) {
+ i965_surface_t *s = (i965_surface_t *) drm;
+
+ if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ if (s->intel.drm.base.device == shader->target->intel.drm.base.device) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface;
+ if (s != shader->target) {
+ int x;
+
+ if (s->intel.drm.fallback != NULL) {
+ status = intel_surface_flush (s, 0);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+ i965_pipelined_flush (i965_device (s));
+
+ src->type.fragment = FS_SURFACE;
+
+ src->base.bo = to_intel_bo (s->intel.drm.bo);
+ src->base.format = s->intel.drm.format;
+ src->base.content = s->intel.drm.base.content;
+ src->base.width = sub->extents.width;
+ src->base.height = sub->extents.height;
+ src->base.stride = s->intel.drm.stride;
+
+ x = sub->extents.x;
+ if (s->intel.drm.format != CAIRO_FORMAT_A8)
+ x *= 4;
+
+ /* XXX tiling restrictions upon offset? */
+ //src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x;
+ } else {
+ i965_surface_t *clone;
+ cairo_surface_pattern_t pattern;
+
+ clone = (i965_surface_t *)
+ i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device,
+ s->intel.drm.base.content,
+ sub->extents.width,
+ sub->extents.height,
+ I965_TILING_DEFAULT,
+ TRUE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ sub->extents.x, sub->extents.y);
+
+ status = _cairo_surface_paint (&clone->intel.drm.base,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base,
+ NULL);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return status;
+ }
+
+ i965_pipelined_flush (i965_device (s));
+ src->type.fragment = FS_SURFACE;
+
+ src->base.bo = to_intel_bo (clone->intel.drm.bo);
+ src->base.format = clone->intel.drm.format;
+ src->base.content = clone->intel.drm.base.content;
+ src->base.width = clone->intel.drm.width;
+ src->base.height = clone->intel.drm.height;
+ src->base.stride = clone->intel.drm.stride;
+
+ src->surface.surface = &clone->intel.drm.base;
+ }
+
+ src_x = sub->extents.x;
+ src_y = sub->extents.y;
+ }
+ } else {
+ if (s->intel.drm.base.device == shader->target->intel.drm.base.device) {
+ if (s != shader->target) {
+ if (s->intel.drm.fallback != NULL) {
+ status = intel_surface_flush (s, 0);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+ i965_pipelined_flush (i965_device (s));
+
+ src->type.fragment = FS_SURFACE;
+
+ src->base.bo = to_intel_bo (s->intel.drm.bo);
+ src->base.format = s->intel.drm.format;
+ src->base.content = s->intel.drm.base.content;
+ src->base.width = s->intel.drm.width;
+ src->base.height = s->intel.drm.height;
+ src->base.stride = s->intel.drm.stride;
+ } else {
+ i965_surface_t *clone;
+ cairo_surface_pattern_t pattern;
+
+ clone = (i965_surface_t *)
+ i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device,
+ s->intel.drm.base.content,
+ s->intel.drm.width,
+ s->intel.drm.height,
+ I965_TILING_DEFAULT,
+ TRUE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (&clone->intel.drm.base,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base,
+ NULL);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->intel.drm.base);
+ return status;
+ }
+
+ i965_pipelined_flush (i965_device (s));
+ src->type.fragment = FS_SURFACE;
+
+ src->base.bo = to_intel_bo (clone->intel.drm.bo);
+ src->base.format = clone->intel.drm.format;
+ src->base.content = clone->intel.drm.base.content;
+ src->base.width = clone->intel.drm.width;
+ src->base.height = clone->intel.drm.height;
+ src->base.stride = clone->intel.drm.stride;
+
+ src->surface.surface = &clone->intel.drm.base;
+ }
+ }
+ }
+ }
+
+ if (src->type.fragment == FS_NONE) {
+ i965_surface_t *s;
+
+ if (extents->width == 1 && extents->height == 1) {
+ return i965_shader_acquire_solid_surface (shader, src,
+ surface, extents);
+ }
+
+ s = (i965_surface_t *)
+ _cairo_surface_has_snapshot (surface,
+ shader->target->intel.drm.base.backend);
+ if (s != NULL) {
+ i965_device_t *device = i965_device (shader->target);
+ intel_bo_t *bo = to_intel_bo (s->intel.drm.bo);
+
+ if (bo->purgeable &&
+ ! intel_bo_madvise (&device->intel, bo, I915_MADV_WILLNEED))
+ {
+ _cairo_surface_detach_snapshot (&s->intel.drm.base);
+ s = NULL;
+ }
+
+ if (s != NULL)
+ cairo_surface_reference (&s->intel.drm.base);
+ }
+
+ if (s == NULL) {
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ if (image->width < 8192 && image->height < 8192) {
+ status = i965_surface_clone (i965_device (shader->target), image, &s);
+ } else {
+ status = i965_surface_clone_subimage (i965_device (shader->target),
+ image, extents, &s);
+ src_x = -extents->x;
+ src_y = -extents->y;
+ }
+
+ _cairo_surface_release_source_image (surface, image, image_extra);
+
+ if (unlikely (status))
+ return status;
+
+ /* XXX? */
+ //intel_bo_mark_purgeable (to_intel_bo (s->intel.drm.bo), TRUE);
+ }
+
+ src->type.fragment = FS_SURFACE;
+
+ src->base.bo = to_intel_bo (s->intel.drm.bo);
+ src->base.content = s->intel.drm.base.content;
+ src->base.format = s->intel.drm.format;
+ src->base.width = s->intel.drm.width;
+ src->base.height = s->intel.drm.height;
+ src->base.stride = s->intel.drm.stride;
+
+ src->surface.surface = &s->intel.drm.base;
+
+ drm = &s->intel.drm.base;
+ }
+
+ /* XXX transform nx1 or 1xn surfaces to 1D? */
+
+ src->type.vertex = VS_NONE;
+
+ src->base.extend = i965_extend (pattern->base.extend);
+ if (pattern->base.extend == CAIRO_EXTEND_NONE &&
+ extents->x >= 0 && extents->y >= 0 &&
+ extents->x + extents->width <= src->base.width &&
+ extents->y + extents->height <= src->base.height)
+ {
+ /* Convert a wholly contained NONE to a REFLECT as the contiguous sampler
+ * cannot not handle CLAMP_BORDER textures.
+ */
+ src->base.extend = i965_extend (CAIRO_EXTEND_REFLECT);
+ /* XXX also need to check |u,v| < 3 */
+ }
+
+ src->base.filter = i965_filter (pattern->base.filter);
+ if (_cairo_matrix_is_pixel_exact (&pattern->base.matrix))
+ src->base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+
+ /* tweak the src matrix to map from dst to texture coordinates */
+ src->base.matrix = pattern->base.matrix;
+ if (src_x | src_y)
+ cairo_matrix_translate (&src->base.matrix, src_x, src_x);
+ cairo_matrix_init_scale (&m, 1. / src->base.width, 1. / src->base.height);
+ cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+i965_shader_acquire_pattern (i965_shader_t *shader,
+ union i965_shader_channel *src,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return i965_shader_acquire_solid (shader, src,
+ (cairo_solid_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return i965_shader_acquire_linear (shader, src,
+ (cairo_linear_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return i965_shader_acquire_radial (shader, src,
+ (cairo_radial_pattern_t *) pattern,
+ extents);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return i965_shader_acquire_surface (shader, src,
+ (cairo_surface_pattern_t *) pattern,
+ extents);
+
+ default:
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+ }
+}
+
+static void
+i965_shader_channel_init (union i965_shader_channel *channel)
+{
+ channel->type.vertex = VS_NONE;
+ channel->type.fragment = FS_NONE;
+ channel->type.pattern = PATTERN_NONE;
+
+ channel->base.mode = 0;
+ channel->base.bo = NULL;
+ channel->base.filter = i965_extend (CAIRO_FILTER_NEAREST);
+ channel->base.extend = i965_extend (CAIRO_EXTEND_NONE);
+ channel->base.has_component_alpha = 0;
+ channel->base.constants_size = 0;
+}
+
+void
+i965_shader_init (i965_shader_t *shader,
+ i965_surface_t *dst,
+ cairo_operator_t op)
+{
+ shader->committed = FALSE;
+ shader->device = i965_device (dst);
+ shader->target = dst;
+ shader->op = op;
+ shader->constants_size = 0;
+
+ shader->need_combine = FALSE;
+
+ i965_shader_channel_init (&shader->source);
+ i965_shader_channel_init (&shader->mask);
+ i965_shader_channel_init (&shader->clip);
+ i965_shader_channel_init (&shader->dst);
+}
+
+void
+i965_shader_fini (i965_shader_t *shader)
+{
+ if (shader->source.type.pattern == PATTERN_SURFACE)
+ cairo_surface_destroy (shader->source.surface.surface);
+ if (shader->mask.type.pattern == PATTERN_SURFACE)
+ cairo_surface_destroy (shader->mask.surface.surface);
+ if (shader->clip.type.pattern == PATTERN_SURFACE)
+ cairo_surface_destroy (shader->clip.surface.surface);
+ if (shader->dst.type.pattern == PATTERN_SURFACE)
+ cairo_surface_destroy (shader->dst.surface.surface);
+}
+
+void
+i965_shader_set_clip (i965_shader_t *shader,
+ cairo_clip_t *clip)
+{
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+ union i965_shader_channel *channel;
+ i965_surface_t *s;
+
+ clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y);
+ assert (clip_surface->status == CAIRO_STATUS_SUCCESS);
+ assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM);
+ s = (i965_surface_t *) clip_surface;
+
+ if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+ i965_pipelined_flush (i965_device (s));
+
+ channel = &shader->clip;
+ channel->type.pattern = PATTERN_BASE;
+ channel->type.vertex = VS_NONE;
+ channel->type.fragment = FS_SURFACE;
+
+ channel->base.bo = to_intel_bo (s->intel.drm.bo);
+ channel->base.content = CAIRO_CONTENT_ALPHA;
+ channel->base.format = CAIRO_FORMAT_A8;
+ channel->base.width = s->intel.drm.width;
+ channel->base.height = s->intel.drm.height;
+ channel->base.stride = s->intel.drm.stride;
+
+ channel->base.extend = i965_extend (CAIRO_EXTEND_NONE);
+ channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+
+ cairo_matrix_init_scale (&shader->clip.base.matrix,
+ 1. / s->intel.drm.width,
+ 1. / s->intel.drm.height);
+
+ cairo_matrix_translate (&shader->clip.base.matrix,
+ -clip_x, -clip_y);
+}
+
+static cairo_bool_t
+i965_shader_check_aperture (i965_shader_t *shader,
+ i965_device_t *device)
+{
+ uint32_t size = device->exec.gtt_size;
+
+ if (shader->target != device->target) {
+ const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+ if (bo->exec == NULL)
+ size += bo->base.size;
+ }
+
+ if (shader->source.base.bo != NULL && shader->source.base.bo != device->source) {
+ const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+ if (bo->exec == NULL)
+ size += bo->base.size;
+ }
+
+ if (shader->mask.base.bo != NULL && shader->mask.base.bo != device->mask) {
+ const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+ if (bo->exec == NULL)
+ size += bo->base.size;
+ }
+
+ if (shader->clip.base.bo != NULL && shader->clip.base.bo != device->clip) {
+ const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+ if (bo->exec == NULL)
+ size += bo->base.size;
+ }
+
+ return size <= device->intel.gtt_avail_size;
+}
+
+static cairo_status_t
+i965_shader_setup_dst (i965_shader_t *shader)
+{
+ union i965_shader_channel *channel;
+ i965_surface_t *s, *clone;
+
+ /* We need to manual blending if we have a clip surface and an unbounded op,
+ * or an extended blend mode.
+ */
+ if (shader->need_combine ||
+ (shader->op < CAIRO_OPERATOR_SATURATE &&
+ (shader->clip.type.fragment == FS_NONE ||
+ _cairo_operator_bounded_by_mask (shader->op))))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ shader->need_combine = TRUE;
+
+ s = shader->target;
+
+ /* we need to allocate a new render target and use the original as a source */
+ clone = (i965_surface_t *)
+ i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device,
+ s->intel.drm.base.content,
+ s->intel.drm.width,
+ s->intel.drm.height,
+ I965_TILING_DEFAULT,
+ TRUE);
+ if (unlikely (clone->intel.drm.base.status))
+ return clone->intel.drm.base.status;
+
+ if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+ i965_pipelined_flush (i965_device (s));
+
+ channel = &shader->dst;
+
+ channel->type.vertex = VS_NONE;
+ channel->type.fragment = FS_SURFACE;
+ channel->type.pattern = PATTERN_SURFACE;
+
+ /* swap buffer objects */
+ channel->base.bo = to_intel_bo (s->intel.drm.bo);
+ s->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo;
+ ((cairo_drm_surface_t *) clone)->bo = &channel->base.bo->base;
+
+ channel->base.content = s->intel.drm.base.content;
+ channel->base.format = s->intel.drm.format;
+ channel->base.width = s->intel.drm.width;
+ channel->base.height = s->intel.drm.height;
+ channel->base.stride = s->intel.drm.stride;
+
+ channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+ channel->base.extend = i965_extend (CAIRO_EXTEND_NONE);
+
+ cairo_matrix_init_scale (&channel->base.matrix,
+ 1. / s->intel.drm.width,
+ 1. / s->intel.drm.height);
+
+ channel->surface.surface = &clone->intel.drm.base;
+
+ s->intel.drm.base.content = clone->intel.drm.base.content;
+ s->intel.drm.format = clone->intel.drm.format;
+ assert (s->intel.drm.width == clone->intel.drm.width);
+ assert (s->intel.drm.height == clone->intel.drm.height);
+ s->intel.drm.stride = clone->intel.drm.stride;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+constant_add_float (i965_shader_t *shader, float v)
+{
+ shader->constants[shader->constants_size++] = v;
+}
+
+static inline void
+i965_shader_copy_channel_constants (i965_shader_t *shader,
+ const union i965_shader_channel *channel)
+{
+ if (channel->base.constants_size) {
+ assert (shader->constants_size + channel->base.constants_size < ARRAY_LENGTH (shader->constants));
+
+ memcpy (shader->constants + shader->constants_size,
+ channel->base.constants,
+ sizeof (float) * channel->base.constants_size);
+ shader->constants_size += channel->base.constants_size;
+ }
+}
+
+static void
+i965_shader_setup_channel_constants (i965_shader_t *shader,
+ const union i965_shader_channel *channel)
+{
+ switch (channel->type.fragment) {
+ case FS_NONE:
+ case FS_CONSTANT:
+ /* no plane equations */
+ break;
+
+ case FS_LINEAR:
+ constant_add_float (shader, channel->base.matrix.xx);
+ constant_add_float (shader, channel->base.matrix.xy);
+ constant_add_float (shader, 0);
+ constant_add_float (shader, channel->base.matrix.x0);
+ break;
+
+ case FS_RADIAL:
+ case FS_SURFACE:
+ constant_add_float (shader, channel->base.matrix.xx);
+ constant_add_float (shader, channel->base.matrix.xy);
+ constant_add_float (shader, 0);
+ constant_add_float (shader, channel->base.matrix.x0);
+
+ constant_add_float (shader, channel->base.matrix.yx);
+ constant_add_float (shader, channel->base.matrix.yy);
+ constant_add_float (shader, 0);
+ constant_add_float (shader, channel->base.matrix.y0);
+ break;
+
+ case FS_SPANS:
+ case FS_GLYPHS:
+ /* use pue from SF */
+ break;
+ }
+
+ i965_shader_copy_channel_constants (shader, channel);
+}
+
+static void
+i965_shader_setup_constants (i965_shader_t *shader)
+{
+ i965_shader_setup_channel_constants (shader, &shader->source);
+ i965_shader_setup_channel_constants (shader, &shader->mask);
+ i965_shader_setup_channel_constants (shader, &shader->clip);
+ i965_shader_setup_channel_constants (shader, &shader->dst);
+ assert (shader->constants_size < ARRAY_LENGTH (shader->constants));
+}
+
+/*
+ * Highest-valued BLENDFACTOR used in i965_blend_op.
+ *
+ * This leaves out BRW_BLENDFACTOR_INV_DST_COLOR,
+ * BRW_BLENDFACTOR_INV_CONST_{COLOR,ALPHA},
+ * BRW_BLENDFACTOR_INV_SRC1_{COLOR,ALPHA}
+ */
+#define BRW_BLENDFACTOR_COUNT (BRW_BLENDFACTOR_INV_DST_ALPHA + 1)
+
+static void
+i965_shader_get_blend_cntl (const i965_shader_t *shader,
+ uint32_t *sblend, uint32_t *dblend)
+{
+ static const struct blendinfo {
+ cairo_bool_t dst_alpha;
+ cairo_bool_t src_alpha;
+ uint32_t src_blend;
+ uint32_t dst_blend;
+ } i965_blend_op[] = {
+ /* CAIRO_OPERATOR_CLEAR treat as SOURCE with transparent */
+ {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ZERO},
+ /* CAIRO_OPERATOR_SOURCE */
+ {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ZERO},
+ /* CAIRO_OPERATOR_OVER */
+ {0, 1, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_INV_SRC_ALPHA},
+ /* CAIRO_OPERATOR_IN */
+ {1, 0, BRW_BLENDFACTOR_DST_ALPHA, BRW_BLENDFACTOR_ZERO},
+ /* CAIRO_OPERATOR_OUT */
+ {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ZERO},
+ /* CAIRO_OPERATOR_ATOP */
+ {1, 1, BRW_BLENDFACTOR_DST_ALPHA, BRW_BLENDFACTOR_INV_SRC_ALPHA},
+
+ /* CAIRO_OPERATOR_DEST */
+ {0, 0, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_ONE},
+ /* CAIRO_OPERATOR_DEST_OVER */
+ {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ONE},
+ /* CAIRO_OPERATOR_DEST_IN */
+ {0, 1, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_SRC_ALPHA},
+ /* CAIRO_OPERATOR_DEST_OUT */
+ {0, 1, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_INV_SRC_ALPHA},
+ /* CAIRO_OPERATOR_DEST_ATOP */
+ {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_SRC_ALPHA},
+ /* CAIRO_OPERATOR_XOR */
+ {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_INV_SRC_ALPHA},
+ /* CAIRO_OPERATOR_ADD */
+ {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ONE},
+ };
+ const struct blendinfo *op = &i965_blend_op[shader->op];
+
+ *sblend = op->src_blend;
+ *dblend = op->dst_blend;
+
+ /* If there's no dst alpha channel, adjust the blend op so that we'll treat
+ * it as always 1.
+ */
+ if (shader->target->intel.drm.base.content == CAIRO_CONTENT_COLOR &&
+ op->dst_alpha)
+ {
+ if (*sblend == BRW_BLENDFACTOR_DST_ALPHA)
+ *sblend = BRW_BLENDFACTOR_ONE;
+ else if (*sblend == BRW_BLENDFACTOR_INV_DST_ALPHA)
+ *sblend = BRW_BLENDFACTOR_ZERO;
+ }
+}
+
+static void
+emit_wm_subpans_to_pixels (struct brw_compile *compile,
+ int tmp)
+{
+ /* Inputs:
+ * R1.5 x/y of upper-left pixel of subspan 3
+ * R1.4 x/y of upper-left pixel of subspan 2
+ * R1.3 x/y of upper-left pixel of subspan 1
+ * R1.2 x/y of upper-left pixel of subspan 0
+ *
+ * Outputs:
+ * M1,2: u
+ * M3,4: v
+ *
+ * upper left, upper right, lower left, lower right.
+ */
+
+ /* compute pixel locations for each subspan */
+ brw_set_compression_control (compile, BRW_COMPRESSION_NONE);
+ brw_ADD (compile,
+ brw_vec8_grf (tmp),
+ brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 4,
+ BRW_REGISTER_TYPE_UW,
+ BRW_VERTICAL_STRIDE_2,
+ BRW_WIDTH_4,
+ BRW_HORIZONTAL_STRIDE_0,
+ BRW_SWIZZLE_NOOP,
+ WRITEMASK_XYZW),
+ brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE));
+ brw_ADD (compile,
+ brw_vec8_grf (tmp+1),
+ brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 8,
+ BRW_REGISTER_TYPE_UW,
+ BRW_VERTICAL_STRIDE_2,
+ BRW_WIDTH_4,
+ BRW_HORIZONTAL_STRIDE_0,
+ BRW_SWIZZLE_NOOP,
+ WRITEMASK_XYZW),
+ brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE));
+ brw_ADD (compile,
+ brw_vec8_grf (tmp+2),
+ brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 5,
+ BRW_REGISTER_TYPE_UW,
+ BRW_VERTICAL_STRIDE_2,
+ BRW_WIDTH_4,
+ BRW_HORIZONTAL_STRIDE_0,
+ BRW_SWIZZLE_NOOP,
+ WRITEMASK_XYZW),
+ brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE));
+ brw_ADD (compile,
+ brw_vec8_grf (tmp+3),
+ brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 9,
+ BRW_REGISTER_TYPE_UW,
+ BRW_VERTICAL_STRIDE_2,
+ BRW_WIDTH_4,
+ BRW_HORIZONTAL_STRIDE_0,
+ BRW_SWIZZLE_NOOP,
+ WRITEMASK_XYZW),
+ brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE));
+ brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED);
+}
+
+static void
+emit_wm_affine (struct brw_compile *compile,
+ int tmp, int reg, int msg)
+{
+ emit_wm_subpans_to_pixels (compile, tmp);
+
+ brw_LINE (compile,
+ brw_null_reg (),
+ brw_vec1_grf (reg, 0),
+ brw_vec8_grf (tmp));
+ brw_MAC (compile,
+ brw_message_reg (msg + 1),
+ brw_vec1_grf (reg, 1),
+ brw_vec8_grf (tmp+2));
+
+ brw_LINE (compile,
+ brw_null_reg (),
+ brw_vec1_grf (reg, 4),
+ brw_vec8_grf (tmp));
+ brw_MAC (compile,
+ brw_message_reg (msg + 3),
+ brw_vec1_grf (reg, 5),
+ brw_vec8_grf (tmp+2));
+}
+
+static void
+emit_wm_glyph (struct brw_compile *compile,
+ int tmp, int vue, int msg)
+{
+ emit_wm_subpans_to_pixels (compile, tmp);
+
+ brw_MUL (compile,
+ brw_null_reg (),
+ brw_vec8_grf (tmp),
+ brw_imm_f (1./1024));
+ brw_ADD (compile,
+ brw_message_reg (msg + 1),
+ brw_acc_reg (),
+ brw_vec1_grf (vue, 0));
+
+ brw_MUL (compile,
+ brw_null_reg (),
+ brw_vec8_grf (tmp + 2),
+ brw_imm_f (1./1024));
+ brw_ADD (compile,
+ brw_message_reg (msg + 3),
+ brw_acc_reg (),
+ brw_vec1_grf (vue, 1));
+}
+
+static void
+emit_wm_load_constant (struct brw_compile *compile,
+ int reg,
+ struct brw_reg *result)
+{
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ result[n] = result[n+4] = brw_reg (BRW_GENERAL_REGISTER_FILE, reg, n,
+ BRW_REGISTER_TYPE_F,
+ BRW_VERTICAL_STRIDE_0,
+ BRW_WIDTH_1,
+ BRW_HORIZONTAL_STRIDE_0,
+ BRW_SWIZZLE_XXXX,
+ WRITEMASK_XYZW);
+ }
+}
+
+static void
+emit_wm_load_opacity (struct brw_compile *compile,
+ int reg,
+ struct brw_reg *result)
+{
+ result[0] = result[1] = result[2] = result[3] =
+ result[4] = result[5] = result[6] = result[7] =
+ brw_reg (BRW_GENERAL_REGISTER_FILE, reg, 0,
+ BRW_REGISTER_TYPE_F,
+ BRW_VERTICAL_STRIDE_0,
+ BRW_WIDTH_1,
+ BRW_HORIZONTAL_STRIDE_1,
+ BRW_SWIZZLE_XXXX,
+ WRITEMASK_XYZW);
+}
+
+static void
+emit_wm_load_linear (struct brw_compile *compile,
+ int tmp, int reg, int msg)
+{
+ emit_wm_subpans_to_pixels (compile, tmp);
+
+ brw_LINE (compile,
+ brw_null_reg(),
+ brw_vec1_grf (reg, 0),
+ brw_vec8_grf (tmp));
+ brw_MAC (compile,
+ brw_message_reg(msg + 1),
+ brw_vec1_grf (reg, 1),
+ brw_vec8_grf (tmp + 2));
+}
+
+static void
+emit_wm_load_radial (struct brw_compile *compile,
+ int reg, int msg)
+
+{
+ struct brw_reg c1x = brw_vec1_grf (reg, 0);
+ struct brw_reg c1y = brw_vec1_grf (reg, 1);
+ struct brw_reg minus_r_sq = brw_vec1_grf (reg, 3);
+ struct brw_reg cdx = brw_vec1_grf (reg, 4);
+ struct brw_reg cdy = brw_vec1_grf (reg, 5);
+ struct brw_reg neg_4a = brw_vec1_grf (reg + 1, 0);
+ struct brw_reg inv_2a = brw_vec1_grf (reg + 1, 1);
+
+ struct brw_reg tmp_x = brw_uw16_grf (30, 0);
+ struct brw_reg tmp_y = brw_uw16_grf (28, 0);
+ struct brw_reg det = brw_vec8_grf (22);
+ struct brw_reg b = brw_vec8_grf (20);
+ struct brw_reg c = brw_vec8_grf (18);
+ struct brw_reg pdx = brw_vec8_grf (16);
+ struct brw_reg pdy = brw_vec8_grf (14);
+ struct brw_reg t = brw_message_reg (msg + 1);
+
+ /* cdx = (c₂x - c₁x)
+ * cdy = (c₂y - c₁y)
+ * dr = r₂-r₁
+ * pdx = px - c₁x
+ * pdy = py - c₁y
+ *
+ * A = cdx² + cdy² - dr²
+ * B = -2·(pdx·cdx + pdy·cdy + r₁·dr)
+ * C = pdx² + pdy² - r₁²
+ *
+ * t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A
+ */
+
+ brw_ADD (compile, pdx, vec8 (tmp_x), negate (c1x));
+ brw_ADD (compile, pdy, vec8 (tmp_y), negate (c1y));
+
+ brw_LINE (compile, brw_null_reg (), cdx, pdx);
+ brw_MAC (compile, b, cdy, pdy);
+
+ brw_MUL (compile, brw_null_reg (), pdx, pdx);
+ brw_MAC (compile, c, pdy, pdy);
+ brw_ADD (compile, c, c, minus_r_sq);
+
+ brw_MUL (compile, brw_null_reg (), b, b);
+ brw_MAC (compile, det, neg_4a, c);
+
+ /* XXX use rsqrt like i915?, it's faster and we need to mac anyway */
+ brw_math (compile,
+ det,
+ BRW_MATH_FUNCTION_SQRT,
+ BRW_MATH_SATURATE_NONE,
+ 2,
+ det,
+ BRW_MATH_DATA_VECTOR,
+ BRW_MATH_PRECISION_FULL);
+
+ /* XXX cmp, +- */
+
+ brw_ADD (compile, det, negate (det), negate (b));
+ brw_ADD (compile, det, det, negate (b));
+ brw_MUL (compile, t, det, inv_2a);
+}
+
+static int
+emit_wm_sample (struct brw_compile *compile,
+ union i965_shader_channel *channel,
+ int sampler,
+ int msg_base, int msg_len,
+ int dst,
+ struct brw_reg *result)
+{
+ int response_len, mask;
+
+ if (channel->base.content == CAIRO_CONTENT_ALPHA) {
+ mask = 0x7000;
+ response_len = 2;
+ result[0] = result[1] = result[2] = result[3] = brw_vec8_grf (dst);
+ result[4] = result[5] = result[6] = result[7] = brw_vec8_grf (dst + 1);
+ } else {
+ mask = 0;
+ response_len = 8;
+ result[0] = brw_vec8_grf (dst + 0);
+ result[1] = brw_vec8_grf (dst + 2);
+ result[2] = brw_vec8_grf (dst + 4);
+ result[3] = brw_vec8_grf (dst + 6);
+ result[4] = brw_vec8_grf (dst + 1);
+ result[5] = brw_vec8_grf (dst + 3);
+ result[6] = brw_vec8_grf (dst + 5);
+ result[7] = brw_vec8_grf (dst + 7);
+ }
+
+ brw_set_compression_control (compile, BRW_COMPRESSION_NONE);
+
+ brw_set_mask_control (compile, BRW_MASK_DISABLE);
+ brw_MOV (compile,
+ get_element_ud (brw_vec8_grf (0), 2),
+ brw_imm_ud (mask));
+ brw_set_mask_control (compile, BRW_MASK_ENABLE);
+
+ brw_SAMPLE (compile,
+ brw_uw16_grf (dst, 0),
+ msg_base,
+ brw_uw8_grf (0, 0),
+ sampler + 1, /* binding table */
+ sampler,
+ WRITEMASK_XYZW,
+ BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE,
+ response_len,
+ msg_len,
+ 0 /* eot */);
+
+ brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED);
+
+ return response_len;
+}
+
+#define MAX_MSG_REGISTER 16
+
+static void
+emit_wm_load_channel (struct brw_compile *compile,
+ union i965_shader_channel *channel,
+ int *vue,
+ int *cue,
+ int *msg,
+ int *sampler,
+ int *grf,
+ struct brw_reg *result)
+{
+ switch (channel->type.fragment) {
+ case FS_NONE:
+ break;
+
+ case FS_CONSTANT:
+ emit_wm_load_constant (compile, *cue, result);
+ *cue += 1;
+ break;
+
+ case FS_RADIAL:
+ emit_wm_load_radial (compile, *cue, *msg);
+ *cue += 2;
+
+ if (*msg + 3 > MAX_MSG_REGISTER)
+ *msg = 1;
+
+ *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result);
+ *sampler += 1;
+ *msg += 3;
+ break;
+
+ case FS_LINEAR:
+ emit_wm_load_linear (compile, *grf, *cue, *msg);
+ *cue += 1;
+
+ if (*msg + 3 > MAX_MSG_REGISTER)
+ *msg = 1;
+
+ *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result);
+ *sampler += 1;
+ *msg += 3;
+ break;
+
+ case FS_SURFACE:
+ emit_wm_affine (compile, *grf, *cue, *msg);
+ *cue += 2;
+
+ if (*msg + 5 > MAX_MSG_REGISTER)
+ *msg = 1;
+
+ *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result);
+ *sampler += 1;
+ *msg += 5;
+ break;
+
+ case FS_SPANS:
+ emit_wm_load_opacity (compile, *vue, result);
+ *vue += 1;
+ break;
+
+ case FS_GLYPHS:
+ emit_wm_glyph (compile, *grf, *vue, *msg);
+ *vue += 1;
+
+ if (*msg + 5 > MAX_MSG_REGISTER)
+ *msg = 1;
+
+ *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result);
+ *sampler += 1;
+ *msg += 5;
+ break;
+ }
+}
+
+static unsigned long
+i965_wm_kernel_hash (const i965_shader_t *shader)
+{
+ unsigned long hash;
+
+ hash =
+ (shader->source.type.fragment & 0xff) |
+ (shader->mask.type.fragment & 0xff) << 8 |
+ (shader->clip.type.fragment & 0xff) << 16;
+ if (shader->need_combine)
+ hash |= (1 + shader->op) << 24;
+
+ return hash;
+}
+
+static void
+i965_wm_kernel_init (struct i965_wm_kernel *key,
+ const i965_shader_t *shader)
+{
+ key->entry.hash = i965_wm_kernel_hash (shader);
+}
+
+static uint32_t
+i965_shader_const_urb_length (i965_shader_t *shader)
+{
+ const int lengths[] = { 0, 1, 1, 4, 2, 0, 0 };
+ int count = 0; /* 128-bit/16-byte increments */
+
+ count += lengths[shader->source.type.fragment];
+ count += lengths[shader->mask.type.fragment];
+ count += lengths[shader->clip.type.fragment];
+ count += lengths[shader->dst.type.fragment];
+
+ return (count + 1) / 2; /* 256-bit/32-byte increments */
+}
+
+static uint32_t
+i965_shader_pue_length (i965_shader_t *shader)
+{
+ return 1 + (shader->mask.type.vertex != VS_NONE);
+}
+
+static uint32_t
+create_wm_kernel (i965_device_t *device,
+ i965_shader_t *shader,
+ int *num_reg)
+{
+ struct brw_compile compile;
+ struct brw_reg source[8], mask[8], clip[8], dst[8];
+ const uint32_t *program;
+ uint32_t size;
+ int msg, cue, vue, grf, sampler;
+ int i;
+
+ struct i965_wm_kernel key, *cache;
+ cairo_status_t status;
+ uint32_t offset;
+
+ i965_wm_kernel_init (&key, shader);
+ cache = _cairo_hash_table_lookup (device->wm_kernels, &key.entry);
+ if (cache != NULL)
+ return cache->offset;
+
+ brw_compile_init (&compile, device->is_g4x);
+
+ if (key.entry.hash == FS_CONSTANT &&
+ to_intel_bo (shader->target->intel.drm.bo)->tiling)
+ {
+ struct brw_instruction *insn;
+
+ assert (i965_shader_const_urb_length (shader) == 1);
+ brw_MOV (&compile, brw_message4_reg (2), brw_vec4_grf (2, 0));
+ grf = 3;
+
+ brw_push_insn_state (&compile);
+ brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */
+ brw_MOV (&compile,
+ retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD),
+ retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD));
+ brw_pop_insn_state (&compile);
+
+ insn = brw_next_instruction (&compile, BRW_OPCODE_SEND);
+ insn->header.predicate_control = 0;
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.destreg__conditonalmod = 0;
+
+ brw_instruction_set_destination (insn,
+ retype (vec16 (brw_acc_reg ()),
+ BRW_REGISTER_TYPE_UW));
+
+ brw_instruction_set_source0 (insn,
+ retype (brw_vec8_grf (0),
+ BRW_REGISTER_TYPE_UW));
+
+ brw_instruction_set_dp_write_message (insn,
+ 0,
+ BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED, /* msg_control */
+ BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */
+ 3,
+ 1, /* pixel scoreboard */
+ 0,
+ TRUE);
+ }
+ else
+ {
+ msg = 1;
+ cue = 2;
+ vue = cue + i965_shader_const_urb_length (shader);
+ grf = vue + i965_shader_pue_length (shader);
+ sampler = 0;
+
+ brw_set_compression_control (&compile, BRW_COMPRESSION_COMPRESSED);
+ emit_wm_load_channel (&compile, &shader->source,
+ &vue, &cue, &msg, &sampler, &grf,
+ source);
+ emit_wm_load_channel (&compile, &shader->mask,
+ &vue, &cue, &msg, &sampler, &grf,
+ mask);
+ emit_wm_load_channel (&compile, &shader->clip,
+ &vue, &cue, &msg, &sampler, &grf,
+ clip);
+ emit_wm_load_channel (&compile, &shader->dst,
+ &vue, &cue, &msg, &sampler, &grf,
+ dst);
+ brw_set_compression_control (&compile, BRW_COMPRESSION_NONE);
+
+ if (shader->need_combine) {
+ if (shader->mask.type.fragment != FS_NONE &&
+ shader->clip.type.fragment != FS_NONE)
+ {
+ for (i = 0; i < 8; i++)
+ brw_MUL (&compile, mask[i], mask[i], clip[i]);
+ }
+
+ /* XXX LERP ! */
+ for (i = 0; i < 8; i++)
+ brw_MOV (&compile, brw_message_reg (2 + i), source[i]);
+ } else {
+ if (shader->mask.type.fragment != FS_NONE) {
+ if (shader->clip.type.fragment != FS_NONE) {
+ for (i = 0; i < 8; i++)
+ brw_MUL (&compile, mask[i], mask[i], clip[i]);
+ }
+
+ for (i = 0; i < 8; i++)
+ brw_MUL (&compile, brw_message_reg (2 + i), source[i], mask[i]);
+ } else {
+ if (shader->clip.type.fragment != FS_NONE) {
+ for (i = 0; i < 8; i++)
+ brw_MUL (&compile, brw_message_reg (2 + i), source[i], clip[i]);
+ } else {
+ for (i = 0; i < 8; i++)
+ brw_MOV (&compile, brw_message_reg (2 + i), source[i]);
+ }
+ }
+ }
+
+ brw_push_insn_state (&compile);
+ brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */
+ brw_MOV (&compile,
+ retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD),
+ retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD));
+ brw_pop_insn_state (&compile);
+
+ brw_fb_WRITE (&compile,
+ retype (vec16 (brw_acc_reg ()), BRW_REGISTER_TYPE_UW),
+ 0, /* base reg */
+ retype (brw_vec8_grf (0), BRW_REGISTER_TYPE_UW),
+ 0, /* binding table index */
+ 2 + 8, /* msg length */
+ 0, /* response length */
+ TRUE); /* EOT */
+ }
+
+ program = brw_get_program (&compile, &size);
+ *num_reg = grf;
+
+ i965_stream_align (&device->general, 64);
+ offset = i965_stream_emit (&device->general, program, size);
+
+ cache = _cairo_freelist_alloc (&device->wm_kernel_freelist);
+ if (likely (cache != NULL)) {
+ i965_wm_kernel_init (cache, shader);
+ cache->offset = offset;
+ status = _cairo_hash_table_insert (device->wm_kernels, &cache->entry);
+ if (unlikely (status))
+ _cairo_freelist_free (&device->wm_kernel_freelist, cache);
+ }
+
+ return offset;
+}
+
+static uint32_t
+create_sf_kernel (i965_device_t *device,
+ i965_shader_t *shader)
+{
+ struct brw_compile compile;
+ const uint32_t *program;
+ uint32_t size;
+ int msg_len;
+
+ brw_compile_init (&compile, device->is_g4x);
+
+ switch (shader->mask.type.vertex) {
+ default:
+ case VS_NONE:
+ /* use curb plane eq in WM */
+ msg_len = 1;
+ break;
+
+ case VS_SPANS:
+ /* just a constant opacity */
+ brw_MOV (&compile,
+ brw_message4_reg (1),
+ brw_vec4_grf (3, 0));
+ msg_len = 2;
+ break;
+
+ case VS_GLYPHS:
+ /* an offset+sf into the glyph cache */
+ brw_MOV (&compile,
+ brw_acc_reg (),
+ brw_vec2_grf (3, 0));
+ brw_MAC (&compile,
+ brw_message4_reg (1),
+ negate (brw_vec2_grf (1, 4)),
+ brw_imm_f (1./1024));
+ msg_len = 2;
+ break;
+ }
+
+ brw_urb_WRITE (&compile,
+ brw_null_reg (),
+ 0,
+ brw_vec8_grf (0), /* r0, will be copied to m0 */
+ 0, /* allocate */
+ 1, /* used */
+ msg_len,
+ 0, /* response len */
+ 1, /* eot */
+ 1, /* writes complete */
+ 0, /* offset */
+ BRW_URB_SWIZZLE_NONE);
+
+ program = brw_get_program (&compile, &size);
+
+ i965_stream_align (&device->general, 64);
+ return i965_stream_emit (&device->general, program, size);
+}
+
+static uint32_t
+i965_sf_kernel (const i965_shader_t *shader)
+{
+ return shader->mask.type.vertex;
+}
+
+static void
+i965_sf_state_init (struct i965_sf_state *key,
+ const i965_shader_t *shader)
+{
+ key->entry.hash = i965_sf_kernel (shader);
+}
+
+cairo_bool_t
+i965_sf_state_equal (const void *A, const void *B)
+{
+ const cairo_hash_entry_t *a = A, *b = B;
+ return a->hash == b->hash;
+}
+
+/*
+ * Sets up the SF state pointing at an SF kernel.
+ *
+ * The SF kernel does coord interp: for each attribute,
+ * calculate dA/dx and dA/dy. Hand these interpolation coefficients
+ * back to SF which then hands pixels off to WM.
+ */
+static uint32_t
+gen4_create_sf_state (i965_device_t *device,
+ i965_shader_t *shader)
+{
+ struct brw_sf_unit_state *state;
+ struct i965_sf_state key, *cache;
+ cairo_status_t status;
+ uint32_t offset;
+
+ i965_sf_state_init (&key, shader);
+ if (i965_sf_state_equal (&key, &device->sf_state))
+ return device->sf_state.offset;
+
+ cache = _cairo_hash_table_lookup (device->sf_states, &key.entry);
+ if (cache != NULL) {
+ offset = cache->offset;
+ goto DONE;
+ }
+
+ offset = create_sf_kernel (device, shader);
+
+ state = i965_stream_alloc (&device->general, 32, sizeof (*state));
+ memset (state, 0, sizeof (*state));
+
+ state->thread0.grf_reg_count = BRW_GRF_BLOCKS (3);
+ assert ((offset & 63) == 0);
+ state->thread0.kernel_start_pointer = offset >> 6;
+ state->sf1.single_program_flow = 1;
+ state->thread3.urb_entry_read_length = 1; /* 1 URB per vertex */
+ state->thread3.urb_entry_read_offset = 1;
+ state->thread3.dispatch_grf_start_reg = 3;
+ state->thread4.max_threads = SF_MAX_THREADS - 1;
+ state->thread4.urb_entry_allocation_size = URB_SF_ENTRY_SIZE - 1;
+ state->thread4.nr_urb_entries = URB_SF_ENTRIES;
+ state->sf6.dest_org_vbias = 0x8;
+ state->sf6.dest_org_hbias = 0x8;
+
+ offset = i965_stream_offsetof (&device->general, state);
+
+ cache = _cairo_freelist_alloc (&device->sf_freelist);
+ if (likely (cache != NULL)) {
+ i965_sf_state_init (cache, shader);
+ cache->offset = offset;
+ status = _cairo_hash_table_insert (device->sf_states, &cache->entry);
+ if (unlikely (status))
+ _cairo_freelist_free (&device->sf_freelist, cache);
+ }
+
+ DONE:
+ i965_sf_state_init (&device->sf_state, shader);
+ device->sf_state.offset = offset;
+
+ return offset;
+}
+
+static unsigned long
+i965_shader_sampler_hash (const i965_shader_t *shader)
+{
+ unsigned long hash = 0;
+ unsigned int offset = 0;
+
+ if (shader->source.base.bo != NULL) {
+ hash |= (shader->source.base.filter << offset) |
+ (shader->source.base.extend << (offset + 4));
+ offset += 8;
+ }
+
+ if (shader->mask.base.bo != NULL) {
+ hash |= (shader->mask.base.filter << offset) |
+ (shader->mask.base.extend << (offset + 4));
+ offset += 8;
+ }
+
+ if (shader->clip.base.bo != NULL) {
+ hash |= (shader->clip.base.filter << offset) |
+ (shader->clip.base.extend << (offset + 4));
+ offset += 8;
+ }
+
+ if (shader->dst.base.bo != NULL) {
+ hash |= (shader->dst.base.filter << offset) |
+ (shader->dst.base.extend << (offset + 4));
+ offset += 8;
+ }
+
+ return hash;
+}
+
+static void
+i965_sampler_init (struct i965_sampler *key,
+ const i965_shader_t *shader)
+{
+ key->entry.hash = i965_shader_sampler_hash (shader);
+}
+
+static void
+emit_sampler_channel (i965_device_t *device,
+ const union i965_shader_channel *channel,
+ uint32_t border_color)
+{
+ struct brw_sampler_state *state;
+
+ state = i965_stream_alloc (&device->general, 0, sizeof (*state));
+ memset (state, 0, sizeof (*state));
+
+ state->ss0.lod_preclamp = 1; /* GL mode */
+
+ state->ss0.border_color_mode = BRW_BORDER_COLOR_MODE_LEGACY;
+
+ state->ss0.min_filter = channel->base.filter;
+ state->ss0.mag_filter = channel->base.filter;
+
+ state->ss1.r_wrap_mode = channel->base.extend;
+ state->ss1.s_wrap_mode = channel->base.extend;
+ state->ss1.t_wrap_mode = channel->base.extend;
+
+ assert ((border_color & 31) == 0);
+ state->ss2.border_color_pointer = border_color >> 5;
+}
+
+static uint32_t
+emit_sampler_state_table (i965_device_t *device,
+ i965_shader_t *shader)
+{
+ struct i965_sampler key, *cache;
+ cairo_status_t status;
+ uint32_t offset;
+
+ if (device->border_color_offset == (uint32_t) -1) {
+ struct brw_sampler_legacy_border_color *border_color;
+
+ border_color = i965_stream_alloc (&device->general, 32,
+ sizeof (*border_color));
+ border_color->color[0] = 0; /* R */
+ border_color->color[1] = 0; /* G */
+ border_color->color[2] = 0; /* B */
+ border_color->color[3] = 0; /* A */
+
+ device->border_color_offset = i965_stream_offsetof (&device->general,
+ border_color);
+ } else {
+ i965_sampler_init (&key, shader);
+ cache = _cairo_hash_table_lookup (device->samplers, &key.entry);
+ if (cache != NULL)
+ return cache->offset;
+ }
+
+ i965_stream_align (&device->general, 32);
+ offset = device->general.used;
+ if (shader->source.base.bo != NULL) {
+ emit_sampler_channel (device,
+ &shader->source,
+ device->border_color_offset);
+ }
+ if (shader->mask.base.bo != NULL) {
+ emit_sampler_channel (device,
+ &shader->mask,
+ device->border_color_offset);
+ }
+ if (shader->clip.base.bo != NULL) {
+ emit_sampler_channel (device,
+ &shader->clip,
+ device->border_color_offset);
+ }
+ if (shader->dst.base.bo != NULL) {
+ emit_sampler_channel (device,
+ &shader->dst,
+ device->border_color_offset);
+ }
+
+ cache = _cairo_freelist_alloc (&device->sampler_freelist);
+ if (likely (cache != NULL)) {
+ i965_sampler_init (cache, shader);
+ cache->offset = offset;
+ status = _cairo_hash_table_insert (device->samplers, &cache->entry);
+ if (unlikely (status))
+ _cairo_freelist_free (&device->sampler_freelist, cache);
+ }
+
+ return offset;
+}
+
+static void
+i965_cc_state_init (struct i965_cc_state *key,
+ const i965_shader_t *shader)
+{
+ uint32_t src_blend, dst_blend;
+
+ if (shader->need_combine)
+ src_blend = dst_blend = 0;
+ else
+ i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend);
+
+ key->entry.hash = src_blend | ((dst_blend & 0xffff) << 16);
+}
+
+cairo_bool_t
+i965_cc_state_equal (const void *A, const void *B)
+{
+ const cairo_hash_entry_t *a = A, *b = B;
+ return a->hash == b->hash;
+}
+
+static uint32_t
+cc_state_emit (i965_device_t *device, i965_shader_t *shader)
+{
+ struct brw_cc_unit_state *state;
+ struct i965_cc_state key, *cache;
+ cairo_status_t status;
+ uint32_t src_blend, dst_blend;
+ uint32_t offset;
+
+ i965_cc_state_init (&key, shader);
+ if (i965_cc_state_equal (&key, &device->cc_state))
+ return device->cc_state.offset;
+
+ cache = _cairo_hash_table_lookup (device->cc_states, &key.entry);
+ if (cache != NULL) {
+ offset = cache->offset;
+ goto DONE;
+ }
+
+ if (shader->need_combine)
+ src_blend = dst_blend = 0;
+ else
+ i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend);
+
+ state = i965_stream_alloc (&device->general, 64, sizeof (*state));
+ memset (state, 0, sizeof (*state));
+
+ /* XXX Note errata, need to flush render cache when blend_enable 0 -> 1 */
+ /* XXX 2 source blend */
+ state->cc3.blend_enable = ! shader->need_combine;
+ state->cc5.ia_blend_function = BRW_BLENDFUNCTION_ADD;
+ state->cc5.ia_src_blend_factor = src_blend;
+ state->cc5.ia_dest_blend_factor = dst_blend;
+ state->cc6.blend_function = BRW_BLENDFUNCTION_ADD;
+ state->cc6.clamp_post_alpha_blend = 1;
+ state->cc6.clamp_pre_alpha_blend = 1;
+ state->cc6.src_blend_factor = src_blend;
+ state->cc6.dest_blend_factor = dst_blend;
+
+ offset = i965_stream_offsetof (&device->general, state);
+
+ cache = _cairo_freelist_alloc (&device->cc_freelist);
+ if (likely (cache != NULL)) {
+ i965_cc_state_init (cache, shader);
+ cache->offset = offset;
+ status = _cairo_hash_table_insert (device->cc_states, &cache->entry);
+ if (unlikely (status))
+ _cairo_freelist_free (&device->cc_freelist, cache);
+ }
+
+ DONE:
+ i965_cc_state_init (&device->cc_state, shader);
+ device->cc_state.offset = offset;
+
+ return offset;
+}
+
+static void
+i965_wm_state_init (struct i965_wm_state *key,
+ const i965_shader_t *shader)
+{
+ key->kernel = i965_wm_kernel_hash (shader);
+ key->sampler = i965_shader_sampler_hash (shader);
+
+ key->entry.hash = key->kernel ^ ((key->sampler) << 16 | (key->sampler >> 16));
+}
+
+cairo_bool_t
+i965_wm_state_equal (const void *A, const void *B)
+{
+ const struct i965_wm_state *a = A, *b = B;
+
+ if (a->entry.hash != b->entry.hash)
+ return FALSE;
+
+ return a->kernel == b->kernel && a->sampler == b->sampler;
+}
+
+static int
+i965_shader_binding_table_count (i965_shader_t *shader)
+{
+ int count;
+
+ count = 1;
+ if (shader->source.type.fragment != FS_CONSTANT)
+ count++;
+ switch (shader->mask.type.fragment) {
+ case FS_NONE:
+ case FS_CONSTANT:
+ case FS_SPANS:
+ break;
+ case FS_LINEAR:
+ case FS_RADIAL:
+ case FS_SURFACE:
+ case FS_GLYPHS:
+ count++;
+ }
+ if (shader->clip.type.fragment == FS_SURFACE)
+ count++;
+ if (shader->dst.type.fragment == FS_SURFACE)
+ count++;
+
+ return count;
+}
+
+static uint32_t
+gen4_create_wm_state (i965_device_t *device,
+ i965_shader_t *shader)
+{
+ struct brw_wm_unit_state *state;
+ uint32_t sampler;
+ uint32_t kernel;
+
+ struct i965_wm_state key, *cache;
+ cairo_status_t status;
+ int num_reg;
+
+ i965_wm_state_init (&key, shader);
+ if (i965_wm_state_equal (&key, &device->wm_state))
+ return device->wm_state.offset;
+
+ cache = _cairo_hash_table_lookup (device->wm_states, &key.entry);
+ if (cache != NULL) {
+ device->wm_state = *cache;
+ return cache->offset;
+ }
+
+ kernel = create_wm_kernel (device, shader, &num_reg);
+ sampler = emit_sampler_state_table (device, shader);
+
+ state = i965_stream_alloc (&device->general, 32, sizeof (*state));
+ memset (state, 0, sizeof (*state));
+ state->thread0.grf_reg_count = BRW_GRF_BLOCKS (num_reg);
+ assert ((kernel & 63) == 0);
+ state->thread0.kernel_start_pointer = kernel >> 6;
+
+ state->thread3.dispatch_grf_start_reg = 2;
+
+ state->wm4.sampler_count = 1; /* 1-4 samplers used */
+ assert ((sampler & 31) == 0);
+ state->wm4.sampler_state_pointer = sampler >> 5;
+ if (device->is_g4x)
+ state->wm5.max_threads = PS_MAX_THREADS_CTG - 1;
+ else
+ state->wm5.max_threads = PS_MAX_THREADS_BRW - 1;
+ state->wm5.thread_dispatch_enable = 1;
+
+ if (device->is_g4x) {
+ /* XXX contiguous 32 pixel dispatch */
+ }
+ state->wm5.enable_16_pix = 1;
+ /* 8 pixel dispatch and friends */
+ //state->wm5.early_depth_test = 1;
+
+ state->thread1.binding_table_entry_count = i965_shader_binding_table_count(shader);
+ state->thread3.urb_entry_read_length = i965_shader_pue_length (shader);
+ state->thread3.const_urb_entry_read_length = i965_shader_const_urb_length (shader);
+
+ key.offset = i965_stream_offsetof (&device->general, state);
+
+ cache = _cairo_freelist_alloc (&device->wm_state_freelist);
+ if (likely (cache != NULL)) {
+ *cache = key;
+ status = _cairo_hash_table_insert (device->wm_states, &cache->entry);
+ if (unlikely (status))
+ _cairo_freelist_free (&device->wm_state_freelist, cache);
+ }
+
+ device->wm_state = key;
+ return key.offset;
+}
+
+static uint32_t
+vs_unit_state_emit (i965_device_t *device)
+{
+ if (device->vs_offset == (uint32_t) -1) {
+ struct brw_vs_unit_state *state;
+
+ /* Set up the vertex shader to be disabled (passthrough) */
+ state = i965_stream_alloc (&device->general, 32, sizeof (*state));
+ memset (state, 0, sizeof (*state));
+
+ state->thread4.nr_urb_entries = URB_VS_ENTRIES;
+ state->thread4.urb_entry_allocation_size = URB_VS_ENTRY_SIZE - 1;
+ state->vs6.vert_cache_disable = 1;
+
+ device->vs_offset = i965_stream_offsetof (&device->general, state);
+ }
+
+ return device->vs_offset;
+}
+
+static uint32_t
+i965_get_card_format (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ return BRW_SURFACEFORMAT_B8G8R8A8_UNORM;
+ case CAIRO_FORMAT_RGB24:
+ return BRW_SURFACEFORMAT_B8G8R8X8_UNORM;
+ case CAIRO_FORMAT_RGB16_565:
+ return BRW_SURFACEFORMAT_B5G6R5_UNORM;
+ case CAIRO_FORMAT_A8:
+ return BRW_SURFACEFORMAT_A8_UNORM;
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ return 0;
+ }
+}
+
+static uint32_t
+i965_get_dest_format (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ return BRW_SURFACEFORMAT_B8G8R8A8_UNORM;
+ case CAIRO_FORMAT_RGB16_565:
+ return BRW_SURFACEFORMAT_B5G6R5_UNORM;
+ case CAIRO_FORMAT_A8:
+ return BRW_SURFACEFORMAT_A8_UNORM;
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ return 0;
+ }
+}
+
+/* XXX silly inline due to compiler bug... */
+static inline void
+i965_stream_add_pending_relocation (i965_stream_t *stream,
+ uint32_t target_offset,
+ uint32_t read_domains,
+ uint32_t write_domain,
+ uint32_t delta)
+{
+ int n;
+
+ n = stream->num_pending_relocations++;
+ assert (n < stream->max_pending_relocations);
+
+ stream->pending_relocations[n].offset = target_offset;
+ stream->pending_relocations[n].read_domains = read_domains;
+ stream->pending_relocations[n].write_domain = write_domain;
+ stream->pending_relocations[n].delta = delta;
+}
+
+static uint32_t
+emit_surface_state (i965_device_t *device,
+ cairo_bool_t is_target,
+ intel_bo_t *bo,
+ cairo_format_t format,
+ int width, int height, int stride,
+ int type)
+{
+ struct brw_surface_state *state;
+ uint32_t write_domain, read_domains;
+ uint32_t offset;
+
+ state = i965_stream_alloc (&device->surface, 32, sizeof (*state));
+ memset (state, 0, sizeof (*state));
+
+ state->ss0.surface_type = type;
+ if (is_target)
+ state->ss0.surface_format = i965_get_dest_format (format);
+ else
+ state->ss0.surface_format = i965_get_card_format (format);
+
+ state->ss0.data_return_format = BRW_SURFACERETURNFORMAT_FLOAT32;
+ state->ss0.color_blend = 1;
+ if (is_target && device->is_g4x)
+ state->ss0.render_cache_read_mode = 1;
+
+ state->ss1.base_addr = bo->offset;
+
+ state->ss2.height = height - 1;
+ state->ss2.width = width - 1;
+ state->ss3.pitch = stride - 1;
+ state->ss3.tile_walk = bo->tiling == I915_TILING_Y;
+ state->ss3.tiled_surface = bo->tiling != I915_TILING_NONE;
+
+ if (is_target) {
+ read_domains = I915_GEM_DOMAIN_RENDER;
+ write_domain = I915_GEM_DOMAIN_RENDER;
+ } else {
+ read_domains = I915_GEM_DOMAIN_SAMPLER;
+ write_domain = 0;
+ }
+
+ offset = i965_stream_offsetof (&device->surface, state);
+ i965_emit_relocation (device, &device->surface,
+ bo, 0,
+ read_domains, write_domain,
+ offset + offsetof (struct brw_surface_state, ss1.base_addr));
+ return offset;
+}
+
+static uint32_t
+emit_surface_state_for_shader (i965_device_t *device,
+ const union i965_shader_channel *channel)
+{
+ int type = BRW_SURFACE_2D;
+
+ assert (channel->type.fragment != FS_NONE);
+ assert (channel->type.fragment != FS_CONSTANT);
+
+ if (channel->type.fragment != FS_SURFACE)
+ type = BRW_SURFACE_1D;
+
+ return emit_surface_state (device, FALSE,
+ channel->base.bo,
+ channel->base.format,
+ channel->base.width,
+ channel->base.height,
+ channel->base.stride,
+ type);
+}
+
+cairo_bool_t
+i965_wm_binding_equal (const void *A,
+ const void *B)
+{
+ const struct i965_wm_binding *a = A, *b = B;
+
+ if (a->entry.hash != b->entry.hash)
+ return FALSE;
+
+ if (a->size != b->size)
+ return FALSE;
+
+ return memcmp (a->table, b->table, sizeof (uint32_t) * a->size) == 0;
+}
+
+static void
+i965_wm_binding_init (struct i965_wm_binding *state,
+ const uint32_t *table,
+ int size)
+{
+ int n;
+
+ state->entry.hash = size;
+ state->size = size;
+
+ for (n = 0; n < size; n++) {
+ state->table[n] = table[n];
+ state->entry.hash ^= (table[n] << (8 * n)) |
+ (table[n] >> (32 - (8*n)));
+ }
+}
+
+static uint32_t
+emit_binding_table (i965_device_t *device,
+ i965_shader_t *shader)
+{
+ intel_bo_t *bo;
+ struct i965_wm_binding key, *cache;
+ uint32_t *table;
+ int n = 0;
+
+ table = i965_stream_alloc (&device->surface, 32, 5 * sizeof (uint32_t));
+ if (shader->target->stream != device->surface.serial) {
+ shader->target->stream = device->surface.serial;
+ shader->target->offset = emit_surface_state (device,
+ TRUE,
+ to_intel_bo (shader->target->intel.drm.bo),
+ shader->target->intel.drm.format,
+ shader->target->intel.drm.width,
+ shader->target->intel.drm.height,
+ shader->target->intel.drm.stride,
+ BRW_SURFACE_2D);
+ }
+ table[n++] = shader->target->offset;
+
+ bo = shader->source.base.bo;
+ if (bo != NULL) {
+ if (bo->opaque0 != device->surface.serial) {
+ bo->opaque0 = device->surface.serial;
+ bo->opaque1 = emit_surface_state_for_shader (device, &shader->source);
+ }
+ table[n++] = bo->opaque1;
+ }
+
+ bo = shader->mask.base.bo;
+ if (bo != NULL) {
+ if (bo->opaque0 != device->surface.serial) {
+ bo->opaque0 = device->surface.serial;
+ bo->opaque1 = emit_surface_state_for_shader (device, &shader->mask);
+ }
+ table[n++] = bo->opaque1;
+ }
+
+ bo = shader->clip.base.bo;
+ if (bo != NULL) {
+ if (bo->opaque0 != device->surface.serial) {
+ bo->opaque0 = device->surface.serial;
+ bo->opaque1 = emit_surface_state_for_shader (device, &shader->clip);
+ }
+ table[n++] = bo->opaque1;
+ }
+
+ bo = shader->dst.base.bo;
+ if (bo != NULL) {
+ if (bo->opaque0 != device->surface.serial) {
+ bo->opaque0 = device->surface.serial;
+ bo->opaque1 = emit_surface_state_for_shader (device, &shader->dst);
+ }
+ table[n++] = bo->opaque1;
+ }
+
+ i965_wm_binding_init (&key, table, n);
+ key.offset = i965_stream_offsetof (&device->surface, table);
+
+ if (i965_wm_binding_equal (&key, &device->wm_binding)) {
+ device->surface.used = key.offset;
+ return device->wm_binding.offset;
+ }
+
+ cache = _cairo_hash_table_lookup (device->wm_bindings, &key.entry);
+ if (cache != NULL) {
+ device->surface.used = key.offset;
+ key.offset = cache->offset;
+ }
+
+ device->wm_binding = key;
+ return key.offset;
+}
+
+static void
+i965_emit_invariants (i965_device_t *device)
+{
+ OUT_BATCH (BRW_CS_URB_STATE | 0);
+ OUT_BATCH (((URB_CS_ENTRY_SIZE-1) << 4) | (URB_CS_ENTRIES << 0));
+}
+
+static void
+i965_emit_urb_fences (i965_device_t *device)
+{
+ int urb_vs_start, urb_vs_size;
+ int urb_gs_start, urb_gs_size;
+ int urb_clip_start, urb_clip_size;
+ int urb_sf_start, urb_sf_size;
+ int urb_cs_start, urb_cs_size;
+
+ if (device->have_urb_fences)
+ return;
+
+ /* URB fence */
+ urb_vs_start = 0;
+ urb_vs_size = URB_VS_ENTRIES * URB_VS_ENTRY_SIZE;
+ urb_gs_start = urb_vs_start + urb_vs_size;
+ urb_gs_size = URB_GS_ENTRIES * URB_GS_ENTRY_SIZE;
+ urb_clip_start = urb_gs_start + urb_gs_size;
+ urb_clip_size = URB_CLIP_ENTRIES * URB_CLIP_ENTRY_SIZE;
+ urb_sf_start = urb_clip_start + urb_clip_size;
+ urb_sf_size = URB_SF_ENTRIES * URB_SF_ENTRY_SIZE;
+ urb_cs_start = urb_sf_start + urb_sf_size;
+ urb_cs_size = URB_CS_ENTRIES * URB_CS_ENTRY_SIZE;
+
+ /* erratum: URB_FENCE must not cross a 64-byte cache-line */
+ while ((device->batch.used & 63) > 64-12)
+ OUT_BATCH (MI_NOOP);
+ OUT_BATCH (BRW_URB_FENCE |
+ UF0_CS_REALLOC |
+ UF0_SF_REALLOC |
+ UF0_CLIP_REALLOC |
+ UF0_GS_REALLOC |
+ UF0_VS_REALLOC |
+ 1);
+ OUT_BATCH (((urb_clip_start + urb_clip_size) << UF1_CLIP_FENCE_SHIFT) |
+ ((urb_gs_start + urb_gs_size) << UF1_GS_FENCE_SHIFT) |
+ ((urb_vs_start + urb_vs_size) << UF1_VS_FENCE_SHIFT));
+ OUT_BATCH (((urb_cs_start + urb_cs_size) << UF2_CS_FENCE_SHIFT) |
+ ((urb_sf_start + urb_sf_size) << UF2_SF_FENCE_SHIFT));
+
+ device->have_urb_fences = TRUE;
+ device->constants_size = 0;
+}
+
+static void
+i965_emit_base (i965_device_t *device)
+{
+ OUT_BATCH (BRW_STATE_BASE_ADDRESS | 4);
+ if (likely (device->general.num_pending_relocations == 0)) {
+ i965_stream_add_pending_relocation (&device->general,
+ device->batch.used,
+ I915_GEM_DOMAIN_INSTRUCTION, 0,
+ BASE_ADDRESS_MODIFY);
+ }
+ OUT_BATCH (0); /* pending relocation */
+
+ if (likely (device->surface.num_pending_relocations == 0)) {
+ i965_stream_add_pending_relocation (&device->surface,
+ device->batch.used,
+ I915_GEM_DOMAIN_INSTRUCTION, 0,
+ BASE_ADDRESS_MODIFY);
+ }
+ OUT_BATCH (0); /* pending relocation */
+
+ OUT_BATCH (0 | BASE_ADDRESS_MODIFY);
+ /* general state max addr, disabled */
+ OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY);
+ /* media object state max addr, disabled */
+ OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY);
+}
+
+static void
+i965_emit_vertex_element (i965_device_t *device,
+ i965_shader_t *shader)
+{
+ uint32_t offset;
+ uint32_t type;
+ int nelem;
+
+ type = 0;
+ nelem = 1;
+ if (shader->mask.type.vertex == VS_SPANS ||
+ shader->mask.type.vertex == VS_GLYPHS)
+ {
+ type = shader->mask.type.vertex;
+ nelem++;
+ }
+
+ if (type == device->vertex_type)
+ return;
+ device->vertex_type = type;
+
+ offset = 0;
+
+ OUT_BATCH (BRW_3DSTATE_VERTEX_ELEMENTS | ((2 * nelem) - 1));
+ OUT_BATCH ((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) |
+ VE0_VALID |
+ (BRW_SURFACEFORMAT_R32G32_FLOAT << VE0_FORMAT_SHIFT) |
+ (offset << VE0_OFFSET_SHIFT));
+ OUT_BATCH ((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) |
+ (BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) |
+ (BRW_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT) |
+ (BRW_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT) |
+ (4 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT));
+ offset += 8;
+
+ assert (shader->source.type.vertex == VS_NONE);
+ switch (shader->mask.type.vertex) {
+ default:
+ case VS_NONE:
+ break;
+
+ case VS_SPANS:
+ OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) |
+ VE0_VALID |
+ (BRW_SURFACEFORMAT_R32_FLOAT << VE0_FORMAT_SHIFT) |
+ (offset << VE0_OFFSET_SHIFT));
+ OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) |
+ (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_1_SHIFT) |
+ (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_2_SHIFT) |
+ (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_3_SHIFT) |
+ (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT));
+
+ offset += 4;
+ break;
+
+ case VS_GLYPHS:
+ OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) |
+ VE0_VALID |
+ (BRW_SURFACEFORMAT_R16G16_FLOAT << VE0_FORMAT_SHIFT) |
+ (offset << VE0_OFFSET_SHIFT));
+ OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) |
+ (BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) |
+ (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_2_SHIFT) |
+ (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_3_SHIFT) |
+ (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT));
+
+ offset += 4;
+ break;
+ }
+ assert (shader->clip.type.vertex == VS_NONE);
+ assert (shader->dst.type.vertex == VS_NONE);
+
+ device->vertex_size = offset;
+ i965_stream_align (&device->vertex, device->vertex_size);
+ device->vertex.committed = device->vertex.used;
+
+ device->rectangle_size = 3 * offset;
+}
+
+static cairo_bool_t
+i965_shader_needs_surface_update (const i965_shader_t *shader,
+ const i965_device_t *device)
+{
+ return device->target != shader->target || shader->target->stream == 0 ||
+ (shader->source.base.bo != NULL && device->source != shader->source.base.bo) ||
+ (shader->mask.base.bo != NULL && device->mask != shader->mask.base.bo) ||
+ (shader->clip.base.bo != NULL && device->clip != shader->clip.base.bo);
+}
+
+static cairo_bool_t
+i965_shader_needs_constants_update (const i965_shader_t *shader,
+ const i965_device_t *device)
+{
+ if (shader->constants_size == 0)
+ return FALSE;
+
+ if (device->constants_size != shader->constants_size)
+ return TRUE;
+
+ return memcmp (device->constants,
+ shader->constants,
+ sizeof (float) * shader->constants_size);
+}
+
+static cairo_bool_t
+i965_shader_needs_state_update (const i965_shader_t *shader,
+ const i965_device_t *device)
+{
+ union {
+ struct i965_sf_state sf;
+ struct i965_wm_state wm;
+ struct i965_cc_state cc;
+ } state;
+
+ i965_sf_state_init (&state.sf, shader);
+ if (! i965_sf_state_equal (&state.sf, &device->sf_state))
+ return TRUE;
+
+ i965_wm_state_init (&state.wm, shader);
+ if (! i965_wm_state_equal (&state.wm, &device->wm_state))
+ return TRUE;
+
+ i965_cc_state_init (&state.cc, shader);
+ if (! i965_cc_state_equal (&state.cc, &device->cc_state))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+i965_emit_composite (i965_device_t *device,
+ i965_shader_t *shader)
+{
+ uint32_t draw_rectangle;
+
+ if (i965_shader_needs_surface_update (shader, device)) {
+ uint32_t offset;
+
+ offset = emit_binding_table (device, shader);
+
+ /* Only the PS uses the binding table */
+ OUT_BATCH (BRW_3DSTATE_BINDING_TABLE_POINTERS | 4);
+ OUT_BATCH (0); /* vs */
+ OUT_BATCH (0); /* gs */
+ OUT_BATCH (0); /* clip */
+ OUT_BATCH (0); /* sf */
+ OUT_BATCH (offset);
+
+ device->target = shader->target;
+ device->source = shader->source.base.bo;
+ device->mask = shader->mask.base.bo;
+ device->clip = shader->clip.base.bo;
+ }
+
+ /* The drawing rectangle clipping is always on. Set it to values that
+ * shouldn't do any clipping.
+ */
+ draw_rectangle = DRAW_YMAX (shader->target->intel.drm.height) |
+ DRAW_XMAX (shader->target->intel.drm.width);
+ if (draw_rectangle != device->draw_rectangle) {
+ OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2);
+ OUT_BATCH (0x00000000); /* ymin, xmin */
+ OUT_BATCH (draw_rectangle);
+ OUT_BATCH (0x00000000); /* yorigin, xorigin */
+ device->draw_rectangle = draw_rectangle;
+ }
+
+ /* skip the depth buffer */
+ /* skip the polygon stipple */
+ /* skip the polygon stipple offset */
+ /* skip the line stipple */
+
+ /* Set the pointers to the 3d pipeline state */
+ if (i965_shader_needs_state_update (shader, device)) {
+ OUT_BATCH (BRW_3DSTATE_PIPELINED_POINTERS | 5);
+ OUT_BATCH (vs_unit_state_emit (device));
+ OUT_BATCH (BRW_GS_DISABLE);
+ OUT_BATCH (BRW_CLIP_DISABLE);
+ OUT_BATCH (gen4_create_sf_state (device, shader));
+ OUT_BATCH (gen4_create_wm_state (device, shader));
+ OUT_BATCH (cc_state_emit (device, shader));
+
+ /* Once the units are initialized, we need to setup the fences */
+ i965_emit_urb_fences (device);
+ }
+
+ if (i965_shader_needs_constants_update (shader, device)) {
+ uint32_t size = (sizeof (float) * shader->constants_size + 63) & -64;
+
+ /* XXX reuse clear/black/white
+ * ht!
+ */
+
+ /* XXX CONSTANT_BUFFER Address Offset Disable? INSTPM? */
+
+ assert (size <= 64 * URB_CS_ENTRY_SIZE);
+ assert (((sizeof (float) * shader->constants_size + 31) & -32) == 32 * i965_shader_const_urb_length (shader));
+
+ device->constants = i965_stream_alloc (&device->surface, 64, size);
+ memcpy (device->constants, shader->constants, size);
+ device->constants_size = shader->constants_size;
+
+ OUT_BATCH (BRW_CONSTANT_BUFFER | (1 << 8));
+ OUT_BATCH (i965_stream_offsetof (&device->surface, device->constants) + size / 64 - 1);
+ }
+
+ i965_emit_vertex_element (device, shader);
+}
+
+void
+i965_flush_vertices (i965_device_t *device)
+{
+ int vertex_count, vertex_start;
+
+ if (device->vertex.used == device->vertex.committed)
+ return;
+
+ assert (device->vertex.used > device->vertex.committed);
+
+ vertex_start = device->vertex.committed / device->vertex_size;
+ vertex_count =
+ (device->vertex.used - device->vertex.committed) / device->vertex_size;
+
+ assert (vertex_count);
+
+ if (device->vertex_size != device->last_vertex_size) {
+ i965_stream_add_pending_relocation (&device->vertex,
+ device->batch.used + 8,
+ I915_GEM_DOMAIN_VERTEX, 0,
+ 0);
+
+ OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3);
+ OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) |
+ VB0_VERTEXDATA |
+ (device->vertex_size << VB0_BUFFER_PITCH_SHIFT));
+ OUT_BATCH (0); /* pending relocation */
+ OUT_BATCH (0);
+ OUT_BATCH (0);
+ device->last_vertex_size = device->vertex_size;
+ }
+
+ OUT_BATCH (BRW_3DPRIMITIVE |
+ BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL |
+ (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) |
+ (0 << 9) |
+ 4);
+ OUT_BATCH (vertex_count); /* vertex count per instance */
+ OUT_BATCH (vertex_start); /* start vertex offset */
+ OUT_BATCH (1); /* single instance */
+ OUT_BATCH (0);
+ OUT_BATCH (0);
+
+ device->vertex.committed = device->vertex.used;
+}
+
+void
+i965_finish_vertices (i965_device_t *device)
+{
+ cairo_status_t status;
+
+ i965_flush_vertices (device);
+
+ i965_stream_commit (device, &device->vertex);
+
+ if (! i965_shader_check_aperture (device->shader, device)) {
+ status = i965_device_flush (device);
+ if (unlikely (status))
+ longjmp (device->shader->unwind, status);
+
+ status = i965_shader_commit (device->shader, device);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ device->last_vertex_size = 0;
+}
+
+static cairo_bool_t
+i965_shader_needs_update (const i965_shader_t *shader,
+ const i965_device_t *device)
+{
+ if (i965_shader_needs_surface_update (shader, device))
+ return TRUE;
+
+ if (i965_shader_needs_constants_update (shader, device))
+ return TRUE;
+
+ return i965_shader_needs_state_update (shader, device);
+}
+
+static void
+i965_shader_reduce (i965_shader_t *shader,
+ const i965_device_t *device)
+{
+ if (shader->op == CAIRO_OPERATOR_OVER &&
+ (i965_wm_kernel_hash (shader) & ~0xff) == 0 &&
+ (shader->source.base.content & CAIRO_CONTENT_ALPHA) == 0)
+ {
+ shader->op = CAIRO_OPERATOR_SOURCE;
+ }
+}
+
+cairo_status_t
+i965_shader_commit (i965_shader_t *shader,
+ i965_device_t *device)
+{
+ cairo_status_t status;
+
+ if (! shader->committed) {
+ device->shader = shader;
+
+ status = i965_shader_setup_dst (shader);
+ if (unlikely (status))
+ return status;
+
+ i965_shader_setup_constants (shader);
+ i965_shader_reduce (shader, device);
+
+ if ((status = setjmp (shader->unwind)))
+ return status;
+
+ shader->committed = TRUE;
+ }
+
+ if (! i965_shader_needs_update (shader, device))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* XXX too many guestimates about likely maximum sizes */
+recheck:
+ if (device->batch.used + 128 > device->batch.size ||
+ ! i965_shader_check_aperture (shader, device))
+ {
+ status = i965_device_flush (device);
+ if (unlikely (status))
+ longjmp (shader->unwind, status);
+ }
+
+ i965_flush_vertices (device);
+
+ if (unlikely (device->surface.used + 128 > device->surface.size ||
+ device->surface.num_relocations + 4 > device->surface.max_relocations))
+ {
+ i965_stream_commit (device, &device->surface);
+ goto recheck;
+ }
+
+ if (unlikely (device->general.used + 512 > device->general.size)) {
+ i965_stream_commit (device, &device->general);
+ i965_general_state_reset (device);
+ goto recheck;
+ }
+
+ if (unlikely (device->batch.used == 0))
+ i965_emit_invariants (device);
+
+ if (unlikely (device->surface.num_pending_relocations == 0 ||
+ device->general.num_pending_relocations == 0))
+ {
+ i965_emit_base (device);
+ }
+
+ i965_emit_composite (device, shader);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+i965_clipped_vertices (i965_device_t *device,
+ struct i965_vbo *vbo,
+ cairo_region_t *clip_region)
+{
+ int i, num_rectangles, size;
+ cairo_status_t status;
+
+ if (vbo->count == 0)
+ return;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+ assert (num_rectangles);
+
+ if (vbo->next ||
+ vbo->count * device->vertex_size + device->vertex.used > device->vertex.size)
+ {
+ i965_finish_vertices (device);
+
+ size = device->rectangle_size;
+ do {
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, i, &rect);
+
+ if (unlikely (device->vertex.used + size > device->vertex.size ||
+ device->batch.used + 64 > device->batch.size ||
+ ! i965_shader_check_aperture (device->shader, device)))
+ {
+ status = i965_device_flush (device);
+ if (unlikely (status))
+ longjmp (device->shader->unwind, status);
+
+ status = i965_shader_commit (device->shader, device);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ i965_emit_relocation (device, &device->batch,
+ vbo->bo, 0,
+ I915_GEM_DOMAIN_VERTEX, 0,
+ device->batch.used + 8);
+
+ OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3);
+ OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) |
+ VB0_VERTEXDATA |
+ (device->vertex_size << VB0_BUFFER_PITCH_SHIFT));
+ OUT_BATCH (vbo->bo->offset);
+ OUT_BATCH (0);
+ OUT_BATCH (0);
+
+ /* XXX scissor? */
+ OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2);
+ OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x));
+ OUT_BATCH (DRAW_YMAX (rect.y + rect.height) |
+ DRAW_XMAX (rect.x + rect.width));
+ OUT_BATCH (0x00000000); /* yorigin, xorigin */
+
+ OUT_BATCH (BRW_3DPRIMITIVE |
+ BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL |
+ (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) |
+ (0 << 9) |
+ 4);
+ OUT_BATCH (vbo->count); /* vertex count per instance */
+ OUT_BATCH (0); /* start vertex offset */
+ OUT_BATCH (1); /* single instance */
+ OUT_BATCH (0);
+ OUT_BATCH (0);
+ }
+ } while ((vbo = vbo->next) != NULL);
+ assert (device->last_vertex_size == 0);
+ } else {
+ int vertex_start, vertex_count;
+ void *ptr;
+
+ vertex_start = device->vertex.committed / device->vertex_size;
+ vertex_count = vbo->count;
+
+ size = vertex_count * device->vertex_size;
+ ptr = intel_bo_map (&device->intel, vbo->bo);
+ memcpy (device->vertex.data + device->vertex.used, ptr, size);
+ device->vertex.committed = device->vertex.used += size;
+
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, i, &rect);
+
+ /* XXX scissor? */
+ OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2);
+ OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x));
+ OUT_BATCH (DRAW_YMAX (rect.y + rect.height) |
+ DRAW_XMAX (rect.x + rect.width));
+ OUT_BATCH (0x00000000); /* yorigin, xorigin */
+
+ OUT_BATCH (BRW_3DPRIMITIVE |
+ BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL |
+ (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) |
+ (0 << 9) |
+ 4);
+ OUT_BATCH (vertex_count); /* vertex count per instance */
+ OUT_BATCH (vertex_start); /* start vertex offset */
+ OUT_BATCH (1); /* single instance */
+ OUT_BATCH (0);
+ OUT_BATCH (0);
+ }
+ }
+
+ device->draw_rectangle = 0;
+}
diff --git a/src/drm/cairo-drm-i965-spans.c b/src/drm/cairo-drm-i965-spans.c
new file mode 100755
index 000000000..5cba7cec8
--- /dev/null
+++ b/src/drm/cairo-drm-i965-spans.c
@@ -0,0 +1,407 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-drm-i965-private.h"
+
+/* Operates in either immediate or retained mode.
+ * When given a clip region we record the sequence of vbo and then
+ * replay them for each clip rectangle, otherwise we simply emit
+ * the vbo straight into the command stream.
+ */
+
+typedef struct _i965_spans i965_spans_t;
+
+typedef float *
+(*i965_get_rectangle_func_t) (i965_spans_t *spans);
+
+struct _i965_spans {
+ cairo_span_renderer_t renderer;
+
+ i965_device_t *device;
+
+ int xmin, xmax;
+ cairo_bool_t is_bounded;
+ const cairo_rectangle_int_t *extents;
+
+ i965_get_rectangle_func_t get_rectangle;
+ i965_shader_t shader;
+
+ cairo_region_t *clip_region;
+
+ struct i965_vbo head, *tail;
+
+ unsigned int vbo_offset;
+ float *vbo_base;
+};
+
+static float *
+i965_spans_emit_rectangle (i965_spans_t *spans)
+{
+ return i965_add_rectangle (spans->device);
+}
+
+static float *
+i965_spans_accumulate_rectangle (i965_spans_t *spans)
+{
+ float *vertices;
+ uint32_t size;
+
+ size = spans->device->rectangle_size;
+ if (unlikely (spans->vbo_offset + size > I965_VERTEX_SIZE)) {
+ struct i965_vbo *vbo;
+
+ vbo = malloc (sizeof (struct i965_vbo));
+ if (unlikely (vbo == NULL)) {
+ /* throw error! */
+ }
+
+ spans->tail->next = vbo;
+ spans->tail = vbo;
+
+ vbo->next = NULL;
+ vbo->bo = intel_bo_create (&spans->device->intel,
+ I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+ FALSE, I915_TILING_NONE, 0);
+ vbo->count = 0;
+
+ spans->vbo_offset = 0;
+ spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo);
+ }
+
+ vertices = spans->vbo_base + spans->vbo_offset;
+ spans->vbo_offset += size;
+ spans->tail->count += 3;
+
+ return vertices;
+}
+
+static void
+i965_span_rectangle (i965_spans_t *spans,
+ int x0, int x1, int y0, int y1,
+ int alpha)
+{
+ float *vertices;
+ float a = alpha / 255.;
+
+ vertices = spans->get_rectangle (spans);
+
+ *vertices++ = x1;
+ *vertices++ = y1;
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y1;
+ *vertices++ = a;
+
+ *vertices++ = x0;
+ *vertices++ = y0;
+ *vertices++ = a;
+}
+
+static cairo_status_t
+i965_bounded_spans_mono (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i965_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (half[0].coverage >= 128) {
+ i965_span_rectangle (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ 255);
+ }
+ half++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i965_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (half[0].coverage) {
+ i965_span_rectangle (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ half[0].coverage);
+ }
+ half++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i965_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0) {
+ i965_span_rectangle (spans,
+ spans->xmin, spans->xmax,
+ y, y + height,
+ 0);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (half[0].x != spans->xmin) {
+ i965_span_rectangle (spans,
+ spans->xmin, half[0].x,
+ y, y + height,
+ 0);
+ }
+
+ do {
+ i965_span_rectangle (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ half[0].coverage);
+ half++;
+ } while (--num_spans > 1);
+
+ if (half[0].x != spans->xmax) {
+ i965_span_rectangle (spans,
+ half[0].x, spans->xmax,
+ y, y + height,
+ 0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_unbounded_spans_mono (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *half,
+ unsigned num_spans)
+{
+ i965_spans_t *spans = abstract_renderer;
+
+ if (num_spans == 0) {
+ i965_span_rectangle (spans,
+ spans->xmin, spans->xmax,
+ y, y + height,
+ 0);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (half[0].x != spans->xmin) {
+ i965_span_rectangle (spans,
+ spans->xmin, half[0].x,
+ y, y + height,
+ 0);
+ }
+
+ do {
+ int alpha = 0;
+ if (half[0].coverage >= 128)
+ alpha = 255;
+ i965_span_rectangle (spans,
+ half[0].x, half[1].x,
+ y, y + height,
+ alpha);
+ half++;
+ } while (--num_spans > 1);
+
+ if (half[0].x != spans->xmax) {
+ i965_span_rectangle (spans,
+ half[0].x, spans->xmax,
+ y, y + height,
+ 0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_spans_init (i965_spans_t *spans,
+ i965_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_status_t status;
+
+ spans->device = i965_device (dst);
+ i965_shader_init (&spans->shader, dst, op);
+
+ spans->is_bounded = extents->is_bounded;
+ if (extents->is_bounded) {
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ spans->renderer.render_rows = i965_bounded_spans_mono;
+ else
+ spans->renderer.render_rows = i965_bounded_spans;
+
+ spans->extents = &extents->bounded;
+ } else {
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ spans->renderer.render_rows = i965_unbounded_spans_mono;
+ else
+ spans->renderer.render_rows = i965_unbounded_spans;
+
+ spans->extents = &extents->unbounded;
+ }
+ spans->xmin = spans->extents->x;
+ spans->xmax = spans->extents->x + spans->extents->width;
+
+ spans->clip_region = NULL;
+ if (clip != NULL) {
+ cairo_region_t *clip_region = NULL;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+
+ spans->clip_region = clip_region;
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ i965_shader_set_clip (&spans->shader, clip);
+ }
+
+ spans->head.next = NULL;
+ spans->head.bo = NULL;
+ spans->head.count = 0;
+ spans->tail = &spans->head;
+
+ if (spans->clip_region == NULL) {
+ spans->get_rectangle = i965_spans_emit_rectangle;
+ } else {
+ spans->get_rectangle = i965_spans_accumulate_rectangle;
+ spans->head.bo = intel_bo_create (&spans->device->intel,
+ I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+ FALSE, I915_TILING_NONE, 0);
+ if (unlikely (spans->head.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo);
+ }
+ spans->vbo_offset = 0;
+
+ return i965_shader_acquire_pattern (&spans->shader,
+ &spans->shader.source,
+ pattern, &extents->bounded);
+}
+
+static void
+i965_spans_fini (i965_spans_t *spans)
+{
+ i965_shader_fini (&spans->shader);
+
+ if (spans->head.bo != NULL) {
+ struct i965_vbo *vbo, *next;
+
+ intel_bo_destroy (&spans->device->intel, spans->head.bo);
+ for (vbo = spans->head.next; vbo != NULL; vbo = next) {
+ next = vbo->next;
+ intel_bo_destroy (&spans->device->intel, vbo->bo);
+ free (vbo);
+ }
+ }
+}
+
+cairo_status_t
+i965_clip_and_composite_spans (i965_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_antialias_t antialias,
+ i965_spans_func_t draw_func,
+ void *draw_closure,
+ const cairo_composite_rectangles_t*extents,
+ cairo_clip_t *clip)
+{
+ i965_spans_t spans;
+ i965_device_t *device;
+ cairo_status_t status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ pattern = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ status = i965_spans_init (&spans, dst, op, pattern, antialias, clip, extents);
+ if (unlikely (status))
+ return status;
+
+ spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA;
+ spans.shader.mask.type.fragment = FS_SPANS;
+ spans.shader.mask.type.vertex = VS_SPANS;
+ spans.shader.mask.type.pattern = PATTERN_BASE;
+
+ status = cairo_device_acquire (dst->intel.drm.base.device);
+ if (unlikely (status))
+ goto CLEANUP_SPANS;
+
+ device = i965_device (dst);
+ status = i965_shader_commit (&spans.shader, device);
+ if (unlikely (status))
+ goto CLEANUP_DEVICE;
+
+ status = draw_func (draw_closure, &spans.renderer, spans.extents);
+ if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS)
+ i965_clipped_vertices (device, &spans.head, spans.clip_region);
+
+ CLEANUP_DEVICE:
+ cairo_device_release (dst->intel.drm.base.device);
+ CLEANUP_SPANS:
+ i965_spans_fini (&spans);
+
+ return status;
+}
diff --git a/src/drm/cairo-drm-i965-surface.c b/src/drm/cairo-drm-i965-surface.c
new file mode 100755
index 000000000..ec7b5954c
--- /dev/null
+++ b/src/drm/cairo-drm-i965-surface.c
@@ -0,0 +1,1927 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Kristian Høgsberg.
+ *
+ * Based on the xf86-intel-driver i965 render acceleration code,
+ * authored by:
+ * Wang Zhenyu <zhenyu.z.wang@intel.com>
+ * Eric Anholt <eric@anholt.net>
+ * Carl Worth <cworth@redhat.com>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+/* XXX
+ *
+ * FIXME: Use brw_PLN for [DevCTG-B+]
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-command-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+#include "cairo-drm-i965-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#define I965_MAX_SIZE 8192
+
+static const cairo_surface_backend_t i965_surface_backend;
+
+static void
+i965_stream_init (i965_stream_t *stream,
+ uint8_t *data, uint32_t size,
+ struct i965_pending_relocation *pending, int max_pending,
+ struct drm_i915_gem_relocation_entry *relocations, int max_relocations)
+
+{
+ stream->used = stream->committed = 0;
+ stream->data = data;
+ stream->size = size;
+ stream->serial = 1;
+
+ stream->num_pending_relocations = 0;
+ stream->max_pending_relocations = max_pending;
+ stream->pending_relocations = pending;
+
+ stream->num_relocations = 0;
+ stream->max_relocations = max_relocations;
+ stream->relocations = relocations;
+}
+
+static void
+i965_add_relocation (i965_device_t *device,
+ intel_bo_t *bo,
+ uint32_t read_domains,
+ uint32_t write_domain)
+{
+ if (bo->exec == NULL) {
+ int i;
+
+ device->exec.gtt_size += bo->base.size;
+
+ i = device->exec.count++;
+ assert (i < ARRAY_LENGTH (device->exec.exec));
+
+ device->exec.exec[i].handle = bo->base.handle;
+ device->exec.exec[i].relocation_count = 0;
+ device->exec.exec[i].relocs_ptr = 0;
+ device->exec.exec[i].alignment = 0;
+ device->exec.exec[i].offset = 0;
+ device->exec.exec[i].flags = 0;
+ device->exec.exec[i].rsvd1 = 0;
+ device->exec.exec[i].rsvd2 = 0;
+
+ device->exec.bo[i] = intel_bo_reference (bo);
+ bo->exec = &device->exec.exec[i];
+ }
+
+ if (cairo_list_is_empty (&bo->link))
+ cairo_list_add_tail (&device->flush, &bo->link);
+
+ assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain);
+ bo->batch_read_domains |= read_domains;
+ bo->batch_write_domain |= write_domain;
+}
+
+void
+i965_emit_relocation (i965_device_t *device,
+ i965_stream_t *stream,
+ intel_bo_t *target,
+ uint32_t target_offset,
+ uint32_t read_domains,
+ uint32_t write_domain,
+ uint32_t offset)
+{
+ int n;
+
+ assert (target_offset < target->base.size);
+
+ i965_add_relocation (device, target, read_domains, write_domain);
+
+ n = stream->num_relocations++;
+ assert (n < stream->max_relocations);
+
+ stream->relocations[n].offset = offset;
+ stream->relocations[n].delta = target_offset;
+ stream->relocations[n].target_handle = target->base.handle;
+ stream->relocations[n].read_domains = read_domains;
+ stream->relocations[n].write_domain = write_domain;
+ stream->relocations[n].presumed_offset = target->offset;
+}
+
+static void
+i965_stream_reset (i965_stream_t *stream)
+{
+ stream->used = stream->committed = 0;
+ stream->num_relocations = 0;
+ stream->num_pending_relocations = 0;
+ if (++stream->serial == 0)
+ stream->serial = 1;
+}
+
+void
+i965_stream_commit (i965_device_t *device,
+ i965_stream_t *stream)
+{
+ intel_bo_t *bo;
+ int n;
+
+ assert (stream->used);
+
+ bo = intel_bo_create (&device->intel,
+ stream->used, stream->used,
+ FALSE, I915_TILING_NONE, 0);
+
+ /* apply pending relocations */
+ for (n = 0; n < stream->num_pending_relocations; n++) {
+ struct i965_pending_relocation *p = &stream->pending_relocations[n];
+
+ i965_emit_relocation (device, &device->batch, bo,
+ p->delta,
+ p->read_domains,
+ p->write_domain,
+ p->offset);
+ if (bo->offset)
+ *(uint32_t *) (device->batch.data + p->offset) = bo->offset + p->delta;
+ }
+
+ intel_bo_write (&device->intel, bo, 0, stream->used, stream->data);
+
+ if (stream->num_relocations) {
+ assert (bo->exec != NULL);
+ bo->exec->relocs_ptr = (uintptr_t) stream->relocations;
+ bo->exec->relocation_count = stream->num_relocations;
+ }
+
+ intel_bo_destroy (&device->intel, bo);
+
+ i965_stream_reset (stream);
+}
+
+static void
+sf_states_pluck (void *entry, void *closure)
+{
+ i965_device_t *device = closure;
+
+ _cairo_hash_table_remove (device->sf_states, entry);
+ _cairo_freelist_free (&device->sf_freelist, entry);
+}
+
+static void
+cc_offsets_pluck (void *entry, void *closure)
+{
+ i965_device_t *device = closure;
+
+ _cairo_hash_table_remove (device->cc_states, entry);
+ _cairo_freelist_free (&device->cc_freelist, entry);
+}
+
+static void
+wm_kernels_pluck (void *entry, void *closure)
+{
+ i965_device_t *device = closure;
+
+ _cairo_hash_table_remove (device->wm_kernels, entry);
+ _cairo_freelist_free (&device->wm_kernel_freelist, entry);
+}
+
+static void
+wm_states_pluck (void *entry, void *closure)
+{
+ i965_device_t *device = closure;
+
+ _cairo_hash_table_remove (device->wm_states, entry);
+ _cairo_freelist_free (&device->wm_state_freelist, entry);
+}
+
+static void
+wm_bindings_pluck (void *entry, void *closure)
+{
+ i965_device_t *device = closure;
+
+ _cairo_hash_table_remove (device->wm_bindings, entry);
+ _cairo_freelist_free (&device->wm_binding_freelist, entry);
+}
+
+static void
+samplers_pluck (void *entry, void *closure)
+{
+ i965_device_t *device = closure;
+
+ _cairo_hash_table_remove (device->samplers, entry);
+ _cairo_freelist_free (&device->sampler_freelist, entry);
+}
+
+void
+i965_general_state_reset (i965_device_t *device)
+{
+ _cairo_hash_table_foreach (device->sf_states,
+ sf_states_pluck,
+ device);
+
+ _cairo_hash_table_foreach (device->cc_states,
+ cc_offsets_pluck,
+ device);
+
+ _cairo_hash_table_foreach (device->wm_kernels,
+ wm_kernels_pluck,
+ device);
+
+ _cairo_hash_table_foreach (device->wm_states,
+ wm_states_pluck,
+ device);
+
+ _cairo_hash_table_foreach (device->wm_bindings,
+ wm_bindings_pluck,
+ device);
+
+ _cairo_hash_table_foreach (device->samplers,
+ samplers_pluck,
+ device);
+
+ device->vs_offset = (uint32_t) -1;
+ device->border_color_offset = (uint32_t) -1;
+
+ if (device->general_state != NULL) {
+ intel_bo_destroy (&device->intel, device->general_state);
+ device->general_state = NULL;
+ }
+}
+
+static void
+i965_device_reset (i965_device_t *device)
+{
+ device->exec.count = 0;
+ device->exec.gtt_size = I965_VERTEX_SIZE +
+ I965_SURFACE_SIZE +
+ I965_GENERAL_SIZE +
+ I965_BATCH_SIZE;
+
+ device->sf_state.entry.hash = (uint32_t) -1;
+ device->wm_state.entry.hash = (uint32_t) -1;
+ device->wm_binding.entry.hash = (uint32_t) -1;
+ device->cc_state.entry.hash = (uint32_t) -1;
+
+ device->target = NULL;
+ device->source = NULL;
+ device->mask = NULL;
+ device->clip = NULL;
+
+ device->draw_rectangle = (uint32_t) -1;
+
+ device->vertex_type = (uint32_t) -1;
+ device->vertex_size = 0;
+ device->rectangle_size = 0;
+ device->last_vertex_size = 0;
+
+ device->constants = NULL;
+ device->constants_size = 0;
+
+ device->have_urb_fences = FALSE;
+}
+
+static cairo_status_t
+i965_exec (i965_device_t *device, uint32_t offset)
+{
+ struct drm_i915_gem_execbuffer2 execbuf;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ int ret, i;
+
+ execbuf.buffers_ptr = (uintptr_t) device->exec.exec;
+ execbuf.buffer_count = device->exec.count;
+ execbuf.batch_start_offset = offset;
+ execbuf.batch_len = device->batch.used;
+ execbuf.DR1 = 0;
+ execbuf.DR4 = 0;
+ execbuf.num_cliprects = 0;
+ execbuf.cliprects_ptr = 0;
+ execbuf.flags = I915_GEM_3D_PIPELINE;
+ execbuf.rsvd1 = 0;
+ execbuf.rsvd2 = 0;
+
+#if 0
+ printf ("exec: offset=%d, length=%d, buffers=%d\n",
+ offset, device->batch.used, device->exec.count);
+ intel_dump_batchbuffer ((uint32_t *) device->batch.data,
+ device->batch.used,
+ device->intel.base.chip_id);
+#endif
+
+ ret = 0;
+ do {
+ ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
+ } while (ret != 0 && errno == EINTR);
+ if (unlikely (ret)) {
+ if (errno == ENOMEM)
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ else
+ status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ fprintf (stderr, "Batch submission failed: %d\n", errno);
+ fprintf (stderr, " gtt size: %zd/%zd\n",
+ device->exec.gtt_size, device->intel.gtt_avail_size);
+
+ fprintf (stderr, " %d buffers:\n",
+ device->exec.count);
+ for (i = 0; i < device->exec.count; i++) {
+ fprintf (stderr, " exec[%d] = %d\n",
+ i, device->exec.bo[i]->base.size);
+ }
+
+ intel_dump_batchbuffer ((uint32_t *) device->batch.data,
+ device->batch.used,
+ device->intel.base.chip_id);
+ }
+
+ /* XXX any write target within the batch should now be in error */
+ for (i = 0; i < device->exec.count; i++) {
+ intel_bo_t *bo = device->exec.bo[i];
+ cairo_bool_t ret;
+
+ bo->offset = device->exec.exec[i].offset;
+ bo->exec = NULL;
+ bo->batch_read_domains = 0;
+ bo->batch_write_domain = 0;
+
+ if (bo->virtual)
+ intel_bo_unmap (bo);
+ bo->cpu = FALSE;
+
+ if (bo->purgeable)
+ ret = intel_bo_madvise (&device->intel, bo, I915_MADV_DONTNEED);
+ /* ignore immediate notification of purging */
+
+ cairo_list_del (&bo->cache_list);
+ cairo_list_init (&bo->link);
+ intel_bo_destroy (&device->intel, bo);
+ }
+ cairo_list_init (&device->flush);
+
+ device->exec.count = 0;
+
+ return status;
+}
+
+static inline uint32_t
+next_bo_size (uint32_t v)
+{
+ v = (v + 8191) / 8192;
+
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+
+ return v * 8192;
+}
+
+static void
+_copy_to_bo_and_apply_relocations (i965_device_t *device,
+ intel_bo_t *bo,
+ i965_stream_t *stream,
+ uint32_t offset)
+{
+ int n;
+
+ intel_bo_write (&device->intel, bo,
+ offset, stream->used,
+ stream->data);
+
+ for (n = 0; n < stream->num_pending_relocations; n++) {
+ struct i965_pending_relocation *p = &stream->pending_relocations[n];
+
+ i965_emit_relocation (device, &device->batch, bo,
+ p->delta + offset,
+ p->read_domains,
+ p->write_domain,
+ p->offset);
+
+ if (bo->offset) {
+ *(uint32_t *) (device->batch.data + p->offset) =
+ bo->offset + p->delta + offset;
+ }
+ }
+}
+
+cairo_status_t
+i965_device_flush (i965_device_t *device)
+{
+ cairo_status_t status;
+ uint32_t aligned, max;
+ intel_bo_t *bo;
+ int n;
+
+ if (device->batch.used == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ i965_flush_vertices (device);
+
+ OUT_BATCH (MI_BATCH_BUFFER_END);
+ /* Emit a padding dword if we aren't going to be quad-word aligned. */
+ if (device->batch.used & 4)
+ OUT_BATCH (MI_NOOP);
+
+#if 0
+ printf ("device flush: vertex=%d, constant=%d, surface=%d, general=%d, batch=%d\n",
+ device->vertex.used,
+ device->constant.used,
+ device->surface.used,
+ device->general.used,
+ device->batch.used);
+#endif
+
+ /* can we pack the surface state into the tail of the general state? */
+ if (device->general.used == device->general.committed) {
+ if (device->general.used) {
+ assert (device->general.num_pending_relocations == 1);
+ assert (device->general_state != NULL);
+ i965_emit_relocation (device, &device->batch,
+ device->general_state,
+ device->general.pending_relocations[0].delta,
+ device->general.pending_relocations[0].read_domains,
+ device->general.pending_relocations[0].write_domain,
+ device->general.pending_relocations[0].offset);
+
+ if (device->general_state->offset) {
+ *(uint32_t *) (device->batch.data +
+ device->general.pending_relocations[0].offset) =
+ device->general_state->offset +
+ device->general.pending_relocations[0].delta;
+ }
+ }
+ } else {
+ assert (device->general.num_pending_relocations == 1);
+ if (device->general_state != NULL) {
+ intel_bo_destroy (&device->intel, device->general_state);
+ device->general_state = NULL;
+ }
+
+ bo = intel_bo_create (&device->intel,
+ device->general.used,
+ device->general.used,
+ FALSE, I915_TILING_NONE, 0);
+ if (unlikely (bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ aligned = (device->general.used + 31) & -32;
+ if (device->surface.used &&
+ aligned + device->surface.used <= bo->base.size)
+ {
+ _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0);
+ _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned);
+
+ if (device->surface.num_relocations) {
+ for (n = 0; n < device->surface.num_relocations; n++)
+ device->surface.relocations[n].offset += aligned;
+
+ assert (bo->exec != NULL);
+ bo->exec->relocs_ptr = (uintptr_t) device->surface.relocations;
+ bo->exec->relocation_count = device->surface.num_relocations;
+ }
+
+ i965_stream_reset (&device->surface);
+ }
+ else
+ {
+ _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0);
+ }
+
+ /* Note we don't reset the general state, just mark what data we've committed. */
+ device->general.committed = device->general.used;
+ device->general_state = bo;
+ }
+ device->general.num_pending_relocations = 0;
+
+ /* Combine vertex+constant+surface+batch streams? */
+ max = aligned = device->vertex.used;
+ if (device->surface.used) {
+ aligned = (aligned + 63) & -64;
+ aligned += device->surface.used;
+ if (device->surface.used > max)
+ max = device->surface.used;
+ }
+ aligned = (aligned + 63) & -64;
+ aligned += device->batch.used;
+ if (device->batch.used > max)
+ max = device->batch.used;
+ if (aligned <= next_bo_size (max)) {
+ int batch_num_relocations;
+
+ if (aligned <= 8192)
+ max = aligned;
+
+ bo = intel_bo_create (&device->intel,
+ max, max,
+ FALSE, I915_TILING_NONE, 0);
+ if (unlikely (bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ assert (aligned <= bo->base.size);
+
+ if (device->vertex.used)
+ _copy_to_bo_and_apply_relocations (device, bo, &device->vertex, 0);
+
+ aligned = device->vertex.used;
+
+ batch_num_relocations = device->batch.num_relocations;
+ if (device->surface.used) {
+ aligned = (aligned + 63) & -64;
+ _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned);
+
+ batch_num_relocations = device->batch.num_relocations;
+ if (device->surface.num_relocations) {
+ assert (device->batch.num_relocations + device->surface.num_relocations < device->batch.max_relocations);
+
+ memcpy (device->batch.relocations + device->batch.num_relocations,
+ device->surface.relocations,
+ sizeof (device->surface.relocations[0]) * device->surface.num_relocations);
+
+ for (n = 0; n < device->surface.num_relocations; n++)
+ device->batch.relocations[device->batch.num_relocations + n].offset += aligned;
+
+ device->batch.num_relocations += device->surface.num_relocations;
+ }
+
+ aligned += device->surface.used;
+ }
+
+ aligned = (aligned + 63) & -64;
+ intel_bo_write (&device->intel, bo,
+ aligned, device->batch.used,
+ device->batch.data);
+
+ for (n = 0; n < batch_num_relocations; n++)
+ device->batch.relocations[n].offset += aligned;
+
+ if (device->exec.bo[device->exec.count-1] == bo) {
+ assert (bo->exec == &device->exec.exec[device->exec.count-1]);
+
+ bo->exec->relocation_count = device->batch.num_relocations;
+ bo->exec->relocs_ptr = (uintptr_t) device->batch.relocations;
+ intel_bo_destroy (&device->intel, bo);
+ } else {
+ assert (bo->exec == NULL);
+
+ n = device->exec.count++;
+ device->exec.exec[n].handle = bo->base.handle;
+ device->exec.exec[n].relocation_count = device->batch.num_relocations;
+ device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations;
+ device->exec.exec[n].alignment = 0;
+ device->exec.exec[n].offset = 0;
+ device->exec.exec[n].flags = 0;
+ device->exec.exec[n].rsvd1 = 0;
+ device->exec.exec[n].rsvd2 = 0;
+
+ /* transfer ownership to the exec */
+ device->exec.bo[n] = bo;
+ }
+ } else {
+ i965_stream_commit (device, &device->vertex);
+ if (device->surface.used)
+ i965_stream_commit (device, &device->surface);
+
+ bo = intel_bo_create (&device->intel,
+ device->batch.used, device->batch.used,
+ FALSE, I915_TILING_NONE, 0);
+ if (unlikely (bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ intel_bo_write (&device->intel, bo,
+ 0, device->batch.used,
+ device->batch.data);
+
+ n = device->exec.count++;
+ device->exec.exec[n].handle = bo->base.handle;
+ device->exec.exec[n].relocation_count = device->batch.num_relocations;
+ device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations;
+ device->exec.exec[n].alignment = 0;
+ device->exec.exec[n].offset = 0;
+ device->exec.exec[n].flags = 0;
+ device->exec.exec[n].rsvd1 = 0;
+ device->exec.exec[n].rsvd2 = 0;
+
+ /* transfer ownership to the exec */
+ device->exec.bo[n] = bo;
+ aligned = 0;
+ }
+
+ status = i965_exec (device, aligned);
+
+ i965_stream_reset (&device->vertex);
+ i965_stream_reset (&device->surface);
+ i965_stream_reset (&device->batch);
+
+ intel_glyph_cache_unpin (&device->intel);
+ intel_snapshot_cache_thaw (&device->intel);
+
+ i965_device_reset (device);
+
+ return status;
+}
+
+static cairo_surface_t *
+i965_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width, int height)
+{
+ i965_surface_t *other;
+ cairo_format_t format;
+
+ if (width > 8192 || height > 8192)
+ return NULL;
+
+ other = abstract_other;
+ if (content == other->intel.drm.base.content)
+ format = other->intel.drm.format;
+ else
+ format = _cairo_format_from_content (content);
+
+ return i965_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device,
+ format,
+ width, height,
+ I965_TILING_DEFAULT, TRUE);
+}
+
+static cairo_status_t
+i965_surface_finish (void *abstract_surface)
+{
+ i965_surface_t *surface = abstract_surface;
+
+ return intel_surface_finish (&surface->intel);
+}
+
+static cairo_status_t
+i965_surface_flush (void *abstract_surface, unsigned flags)
+{
+ i965_surface_t *surface = abstract_surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->intel.drm.fallback != NULL)
+ return intel_surface_flush (abstract_surface);
+
+ /* Forgo flushing on finish as the user cannot access the surface directly. */
+ if (! surface->intel.drm.base.finished &&
+ to_intel_bo (surface->intel.drm.bo)->exec != NULL)
+ {
+ status = cairo_device_acquire (surface->intel.drm.base.device);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ i965_device_t *device;
+
+ device = i965_device (surface);
+ status = i965_device_flush (device);
+ cairo_device_release (&device->intel.base.base);
+ }
+ }
+
+ return status;
+}
+
+/* rasterisation */
+
+static cairo_status_t
+_composite_boxes_spans (void *closure,
+ cairo_span_renderer_t *renderer,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_boxes_t *boxes = closure;
+ cairo_rectangular_scan_converter_t converter;
+ struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+
+ _cairo_rectangular_scan_converter_init (&converter, extents);
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+ int i;
+
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+ }
+
+ status = converter.base.generate (&converter.base, renderer);
+
+ CLEANUP:
+ converter.base.destroy (&converter.base);
+ return status;
+}
+
+cairo_status_t
+i965_fixup_unbounded (i965_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ i965_shader_t shader;
+ i965_device_t *device;
+ cairo_status_t status;
+
+ i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR);
+
+ if (clip != NULL) {
+ cairo_region_t *clip_region = NULL;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+ assert (clip_region == NULL);
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ i965_shader_set_clip (&shader, clip);
+ } else {
+ if (extents->bounded.width == extents->unbounded.width &&
+ extents->bounded.height == extents->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ status = i965_shader_acquire_pattern (&shader,
+ &shader.source,
+ &_cairo_pattern_clear.base,
+ &extents->unbounded);
+ if (unlikely (status)) {
+ i965_shader_fini (&shader);
+ return status;
+ }
+
+ device = i965_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ return status;
+
+ status = i965_shader_commit (&shader, device);
+ if (unlikely (status)) {
+ goto BAIL;
+ }
+
+ if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+ i965_shader_add_rectangle (&shader,
+ extents->unbounded.x,
+ extents->unbounded.y,
+ extents->unbounded.width,
+ extents->unbounded.height);
+ } else { /* top */
+ if (extents->bounded.y != extents->unbounded.y) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = extents->unbounded.x;
+ rect.y = extents->unbounded.y;
+ rect.width = extents->unbounded.width;
+ rect.height = extents->bounded.y - rect.y;
+
+ i965_shader_add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+
+ /* left */
+ if (extents->bounded.x != extents->unbounded.x) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = extents->unbounded.x;
+ rect.y = extents->bounded.y;
+ rect.width = extents->bounded.x - extents->unbounded.x;
+ rect.height = extents->bounded.height;
+
+ i965_shader_add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+
+ /* right */
+ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = extents->bounded.x + extents->bounded.width;
+ rect.y = extents->bounded.y;
+ rect.width = extents->unbounded.x + extents->unbounded.width - rect.x;
+ rect.height = extents->bounded.height;
+
+ i965_shader_add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+
+ /* bottom */
+ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = extents->unbounded.x;
+ rect.y = extents->bounded.y + extents->bounded.height;
+ rect.width = extents->unbounded.width;
+ rect.height = extents->unbounded.y + extents->unbounded.height - rect.y;
+
+ i965_shader_add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+ }
+
+ i965_shader_fini (&shader);
+ BAIL:
+ cairo_device_release (&device->intel.base.base);
+ return status;
+}
+
+static cairo_status_t
+i965_fixup_unbounded_boxes (i965_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip,
+ cairo_boxes_t *boxes)
+{
+ cairo_boxes_t clear;
+ cairo_box_t box;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ i965_shader_t shader;
+ int i;
+
+ if (boxes->num_boxes <= 1)
+ return i965_fixup_unbounded (dst, extents, clip);
+
+ i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR);
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ i965_shader_set_clip (&shader, clip);
+ }
+
+ status = i965_shader_acquire_pattern (&shader,
+ &shader.source,
+ &_cairo_pattern_clear.base,
+ &extents->unbounded);
+ if (unlikely (status)) {
+ i965_shader_fini (&shader);
+ return status;
+ }
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (clip_region == NULL) {
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+
+ tmp.chunks.next = NULL;
+ } else {
+ pixman_box32_t *pbox;
+
+ pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+ _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+ status = _cairo_boxes_add (&clear, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (&clear, &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&clear);
+ return status;
+ }
+ }
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ }
+
+ if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) {
+ i965_device_t *device;
+
+ device = i965_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i965_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto err_device;
+
+ for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+ }
+ }
+
+err_device:
+ cairo_device_release (&device->intel.base.base);
+err_shader:
+ i965_shader_fini (&shader);
+ }
+
+ _cairo_boxes_fini (&clear);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_boxes (i965_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_bool_t need_clip_surface = FALSE;
+ cairo_region_t *clip_region = NULL;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ i965_shader_t shader;
+ i965_device_t *device;
+ int i;
+
+ /* If the boxes are not pixel-aligned, we will need to compute a real mask */
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ if (! boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ i965_shader_init (&shader, dst, op);
+
+ status = i965_shader_acquire_pattern (&shader,
+ &shader.source,
+ pattern,
+ &extents->bounded);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_clip_surface)
+ i965_shader_set_clip (&shader, clip);
+ }
+
+ device = i965_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i965_shader_commit (&shader, i965_device (dst));
+ if (unlikely (status))
+ goto err_device;
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+ if (x2 > x1 && y2 > y1)
+ i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+ }
+ }
+
+ if (! extents->is_bounded)
+ status = i965_fixup_unbounded_boxes (dst, extents, clip, boxes);
+
+ err_device:
+ cairo_device_release (&device->intel.base.base);
+ err_shader:
+ i965_shader_fini (&shader);
+
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (i965_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+
+ if (boxes->num_boxes == 0) {
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ return i965_fixup_unbounded (dst, extents, clip);
+ }
+
+ /* Use a fast path if the boxes are pixel aligned */
+ status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ /* Otherwise render the boxes via an implicit mask and composite in the usual
+ * fashion.
+ */
+ return i965_clip_and_composite_spans (dst, op, src, antialias,
+ _composite_boxes_spans, boxes,
+ extents, clip);
+}
+
+static cairo_int_status_t
+i965_surface_paint (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ i965_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ cairo_boxes_t boxes;
+ cairo_box_t *clip_boxes = boxes.boxes_embedded;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded);
+ cairo_status_t status;
+
+ /* XXX unsupported operators? use pixel shader blending, eventually */
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+ status = _clip_and_composite_boxes (dst, op, source,
+ &boxes, CAIRO_ANTIALIAS_DEFAULT,
+ &extents, clip);
+ if (clip_boxes != boxes.boxes_embedded)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+i965_surface_mask (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ i965_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ i965_shader_t shader;
+ i965_device_t *device;
+ cairo_clip_t local_clip;
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t need_clip_surface = FALSE;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status)) {
+ _cairo_clip_fini (&local_clip);
+ return status;
+ }
+
+ have_clip = TRUE;
+ }
+
+ i965_shader_init (&shader, dst, op);
+
+ status = i965_shader_acquire_pattern (&shader,
+ &shader.source,
+ source,
+ &extents.bounded);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i965_shader_acquire_pattern (&shader,
+ &shader.mask,
+ mask,
+ &extents.bounded);
+ if (unlikely (status))
+ goto err_shader;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_clip_surface)
+ i965_shader_set_clip (&shader, clip);
+ }
+
+ device = i965_device (dst);
+ status = cairo_device_acquire (&device->intel.base.base);
+ if (unlikely (status))
+ goto err_shader;
+
+ status = i965_shader_commit (&shader, device);
+ if (unlikely (status))
+ goto err_device;
+
+ if (clip_region != NULL) {
+ unsigned int n, num_rectangles;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rectangles; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+
+ i965_shader_add_rectangle (&shader,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+ } else {
+ i965_shader_add_rectangle (&shader,
+ extents.bounded.x,
+ extents.bounded.y,
+ extents.bounded.width,
+ extents.bounded.height);
+ }
+
+ if (! extents.is_bounded)
+ status = i965_fixup_unbounded (dst, &extents, clip);
+
+ err_device:
+ cairo_device_release (&device->intel.base.base);
+ err_shader:
+ i965_shader_fini (&shader);
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+typedef struct {
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+} composite_polygon_info_t;
+
+static cairo_status_t
+_composite_polygon_spans (void *closure,
+ cairo_span_renderer_t *renderer,
+ const cairo_rectangle_int_t *extents)
+{
+ composite_polygon_info_t *info = closure;
+ cairo_botor_scan_converter_t converter;
+ cairo_status_t status;
+ cairo_box_t box;
+
+ box.p1.x = _cairo_fixed_from_int (extents->x);
+ box.p1.y = _cairo_fixed_from_int (extents->y);
+ box.p2.x = _cairo_fixed_from_int (extents->x + extents->width);
+ box.p2.y = _cairo_fixed_from_int (extents->y + extents->height);
+
+ _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
+
+ status = converter.base.add_polygon (&converter.base, &info->polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = converter.base.generate (&converter.base, renderer);
+
+ converter.base.destroy (&converter.base);
+
+ return status;
+}
+
+static cairo_int_status_t
+i965_surface_stroke (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ i965_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ composite_polygon_info_t info;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source,
+ path, stroke_style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ stroke_style,
+ ctm,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (dst, op, source,
+ &boxes, antialias,
+ &extents, clip);
+ }
+
+ _cairo_boxes_fini (&boxes);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto CLEANUP_BOXES;
+ }
+
+ _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &info.polygon);
+ if (unlikely (status))
+ goto CLEANUP_POLYGON;
+
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t rect;
+
+ _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+ goto CLEANUP_POLYGON;
+ }
+
+ if (info.polygon.num_edges == 0) {
+ if (! extents.is_bounded)
+ status = i965_fixup_unbounded (dst, &extents, clip);
+ } else {
+ info.fill_rule = CAIRO_FILL_RULE_WINDING;
+ info.antialias = antialias;
+ status = i965_clip_and_composite_spans (dst, op, source, antialias,
+ _composite_polygon_spans, &info,
+ &extents, clip);
+ }
+
+CLEANUP_POLYGON:
+ _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+i965_surface_fill (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ i965_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ composite_polygon_info_t info;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ dst->intel.drm.width,
+ dst->intel.drm.height,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ assert (! _cairo_path_fixed_fill_is_empty (path));
+
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (dst, op, source,
+ &boxes, antialias,
+ &extents, clip);
+ }
+
+ _cairo_boxes_fini (&boxes);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto CLEANUP_BOXES;
+ }
+
+ _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon);
+ if (unlikely (status))
+ goto CLEANUP_POLYGON;
+
+ if (extents.is_bounded) {
+ cairo_rectangle_int_t rect;
+
+ _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+ goto CLEANUP_POLYGON;
+ }
+
+ if (info.polygon.num_edges == 0) {
+ if (! extents.is_bounded)
+ status = i965_fixup_unbounded (dst, &extents, clip);
+ } else {
+ info.fill_rule = fill_rule;
+ info.antialias = antialias;
+ status = i965_clip_and_composite_spans (dst, op, source, antialias,
+ _composite_polygon_spans, &info,
+ &extents, clip);
+ }
+
+CLEANUP_POLYGON:
+ _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static const cairo_surface_backend_t i965_surface_backend = {
+ CAIRO_SURFACE_TYPE_DRM,
+ _cairo_default_context_create,
+
+ i965_surface_create_similar,
+ i965_surface_finish,
+
+ NULL,
+ intel_surface_acquire_source_image,
+ intel_surface_release_source_image,
+
+ NULL, NULL, NULL,
+ NULL, /* composite */
+ NULL, /* fill */
+ NULL, /* trapezoids */
+ NULL, /* span */
+ NULL, /* check-span */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_drm_surface_get_extents,
+ NULL, /* old-glyphs */
+ _cairo_drm_surface_get_font_options,
+
+ i965_surface_flush,
+ NULL, /* mark_dirty */
+ intel_scaled_font_fini,
+ intel_scaled_glyph_fini,
+
+ i965_surface_paint,
+ i965_surface_mask,
+ i965_surface_stroke,
+ i965_surface_fill,
+ i965_surface_glyphs,
+};
+
+static void
+i965_surface_init (i965_surface_t *surface,
+ cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ intel_surface_init (&surface->intel, &i965_surface_backend, device,
+ format, width, height);
+ surface->stream = 0;
+}
+
+static inline int cairo_const
+i965_tiling_stride (uint32_t tiling, int stride)
+{
+ if (tiling == I915_TILING_NONE)
+ return stride;
+
+ return (stride + 127) & -128;
+}
+
+static inline int cairo_const
+i965_tiling_height (uint32_t tiling, int height)
+{
+ switch (tiling) {
+ default:
+ case I915_TILING_NONE: return (height + 1) & -2;
+ case I915_TILING_X: return (height + 7) & -8;
+ case I915_TILING_Y: return (height + 31) & -32;
+ }
+}
+
+cairo_surface_t *
+i965_surface_create_internal (cairo_drm_device_t *base_dev,
+ cairo_format_t format,
+ int width, int height,
+ uint32_t tiling,
+ cairo_bool_t gpu_target)
+{
+ i965_surface_t *surface;
+ cairo_status_t status_ignored;
+
+ surface = malloc (sizeof (i965_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ i965_surface_init (surface, base_dev, format, width, height);
+
+ if (width && height) {
+ uint32_t size, stride;
+ intel_bo_t *bo;
+
+ width = (width + 3) & -4;
+ stride = cairo_format_stride_for_width (surface->intel.drm.format, width);
+ stride = (stride + 63) & ~63;
+ stride = i965_tiling_stride (tiling, stride);
+ surface->intel.drm.stride = stride;
+
+ height = i965_tiling_height (tiling, height);
+ assert (height <= I965_MAX_SIZE);
+
+ size = stride * height;
+ bo = intel_bo_create (to_intel_device (&base_dev->base),
+ size, size,
+ gpu_target, tiling, stride);
+ if (bo == NULL) {
+ status_ignored = _cairo_drm_surface_finish (&surface->intel.drm);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ bo->tiling = tiling;
+ bo->stride = stride;
+ surface->intel.drm.bo = &bo->base;
+
+ assert (bo->base.size >= (size_t) stride*height);
+ }
+
+ return &surface->intel.drm.base;
+}
+
+static cairo_surface_t *
+i965_surface_create (cairo_drm_device_t *device,
+ cairo_format_t format, int width, int height)
+{
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_A8:
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ case CAIRO_FORMAT_A1:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return i965_surface_create_internal (device, format, width, height,
+ I965_TILING_DEFAULT, TRUE);
+}
+
+static cairo_surface_t *
+i965_surface_create_for_name (cairo_drm_device_t *base_dev,
+ unsigned int name,
+ cairo_format_t format,
+ int width, int height, int stride)
+{
+ i965_device_t *device;
+ i965_surface_t *surface;
+ cairo_status_t status_ignored;
+ int min_stride;
+
+ min_stride = cairo_format_stride_for_width (format, (width + 3) & -4);
+ if (stride < min_stride || stride & 63)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+ if (format == CAIRO_FORMAT_A1)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_A8:
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ case CAIRO_FORMAT_A1:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ surface = malloc (sizeof (i965_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ i965_surface_init (surface, base_dev, format, width, height);
+
+ device = (i965_device_t *) base_dev;
+ surface->intel.drm.bo = &intel_bo_create_for_name (&device->intel, name)->base;
+ if (unlikely (surface->intel.drm.bo == NULL)) {
+ status_ignored = _cairo_drm_surface_finish (&surface->intel.drm);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ surface->intel.drm.stride = stride;
+
+ return &surface->intel.drm.base;
+}
+
+static cairo_status_t
+i965_surface_enable_scan_out (void *abstract_surface)
+{
+ i965_surface_t *surface = abstract_surface;
+ intel_bo_t *bo;
+
+ if (unlikely (surface->intel.drm.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+
+ bo = to_intel_bo (surface->intel.drm.bo);
+ if (bo->tiling != I915_TILING_X) {
+ i965_device_t *device = i965_device (surface);
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *clone;
+ cairo_status_t status;
+
+ clone = i965_surface_create_internal (&device->intel.base,
+ surface->intel.drm.base.content,
+ surface->intel.drm.width,
+ surface->intel.drm.height,
+ I915_TILING_X,
+ TRUE);
+ if (unlikely (clone->status))
+ return clone->status;
+
+ /* 2D blit? */
+ _cairo_pattern_init_for_surface (&pattern, &surface->intel.drm.base);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+ status = _cairo_surface_paint (clone,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base,
+ NULL);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (clone);
+ return status;
+ }
+
+ /* swap buffer objects */
+ surface->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo;
+ ((cairo_drm_surface_t *) clone)->bo = &bo->base;
+ bo = to_intel_bo (surface->intel.drm.bo);
+
+ cairo_surface_destroy (clone);
+ }
+
+ if (unlikely (bo->tiling == I915_TILING_Y))
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_i965_device_flush (cairo_drm_device_t *device)
+{
+ cairo_status_t status;
+
+ if (unlikely (device->base.finished))
+ return CAIRO_STATUS_SUCCESS;
+
+ status = cairo_device_acquire (&device->base);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = i965_device_flush ((i965_device_t *) device);
+
+ cairo_device_release (&device->base);
+
+ return status;
+}
+
+static cairo_int_status_t
+_i965_device_throttle (cairo_drm_device_t *device)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&device->base);
+ if (unlikely (status))
+ return status;
+
+ status = i965_device_flush ((i965_device_t *) device);
+ intel_throttle ((intel_device_t *) device);
+
+ cairo_device_release (&device->base);
+
+ return status;
+}
+
+static void
+_i965_device_destroy (void *base)
+{
+ i965_device_t *device = base;
+
+ i965_device_reset (device);
+ i965_general_state_reset (device);
+
+ _cairo_hash_table_destroy (device->sf_states);
+ _cairo_hash_table_destroy (device->samplers);
+ _cairo_hash_table_destroy (device->cc_states);
+ _cairo_hash_table_destroy (device->wm_kernels);
+ _cairo_hash_table_destroy (device->wm_states);
+ _cairo_hash_table_destroy (device->wm_bindings);
+
+ _cairo_freelist_fini (&device->sf_freelist);
+ _cairo_freelist_fini (&device->cc_freelist);
+ _cairo_freelist_fini (&device->wm_kernel_freelist);
+ _cairo_freelist_fini (&device->wm_state_freelist);
+ _cairo_freelist_fini (&device->wm_binding_freelist);
+ _cairo_freelist_fini (&device->sampler_freelist);
+
+ intel_device_fini (&device->intel);
+ free (device);
+}
+
+static cairo_bool_t
+hash_equal (const void *A, const void *B)
+{
+ const cairo_hash_entry_t *a = A, *b = B;
+ return a->hash == b->hash;
+}
+
+cairo_drm_device_t *
+_cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+ i965_device_t *device;
+ uint64_t gtt_size;
+ cairo_status_t status;
+
+ if (! intel_info (fd, &gtt_size))
+ return NULL;
+
+ device = malloc (sizeof (i965_device_t));
+ if (unlikely (device == NULL))
+ return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = intel_device_init (&device->intel, fd);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ device->is_g4x = IS_G4X (chip_id);
+ //device->is_g5x = IS_G5X (chip_id);
+
+ device->intel.base.surface.create = i965_surface_create;
+ device->intel.base.surface.create_for_name = i965_surface_create_for_name;
+ device->intel.base.surface.create_from_cacheable_image = NULL;
+ device->intel.base.surface.enable_scan_out = i965_surface_enable_scan_out;
+
+ device->intel.base.device.flush = _i965_device_flush;
+ device->intel.base.device.throttle = _i965_device_throttle;
+ device->intel.base.device.destroy = _i965_device_destroy;
+
+ device->sf_states = _cairo_hash_table_create (i965_sf_state_equal);
+ if (unlikely (device->sf_states == NULL))
+ goto CLEANUP_INTEL;
+
+ _cairo_freelist_init (&device->sf_freelist,
+ sizeof (struct i965_sf_state));
+
+
+ device->cc_states = _cairo_hash_table_create (i965_cc_state_equal);
+ if (unlikely (device->cc_states == NULL))
+ goto CLEANUP_SF;
+
+ _cairo_freelist_init (&device->cc_freelist,
+ sizeof (struct i965_cc_state));
+
+
+ device->wm_kernels = _cairo_hash_table_create (hash_equal);
+ if (unlikely (device->wm_kernels == NULL))
+ goto CLEANUP_CC;
+
+ _cairo_freelist_init (&device->wm_kernel_freelist,
+ sizeof (struct i965_wm_kernel));
+
+ device->wm_states = _cairo_hash_table_create (i965_wm_state_equal);
+ if (unlikely (device->wm_states == NULL))
+ goto CLEANUP_WM_KERNEL;
+
+ _cairo_freelist_init (&device->wm_state_freelist,
+ sizeof (struct i965_wm_state));
+
+
+ device->wm_bindings = _cairo_hash_table_create (i965_wm_binding_equal);
+ if (unlikely (device->wm_bindings == NULL))
+ goto CLEANUP_WM_STATE;
+
+ _cairo_freelist_init (&device->wm_binding_freelist,
+ sizeof (struct i965_wm_binding));
+
+ device->samplers = _cairo_hash_table_create (hash_equal);
+ if (unlikely (device->samplers == NULL))
+ goto CLEANUP_WM_BINDING;
+
+ _cairo_freelist_init (&device->sampler_freelist,
+ sizeof (struct i965_sampler));
+
+ i965_stream_init (&device->batch,
+ device->batch_base, sizeof (device->batch_base),
+ NULL, 0,
+ device->batch_relocations,
+ ARRAY_LENGTH (device->batch_relocations));
+
+ i965_stream_init (&device->surface,
+ device->surface_base, sizeof (device->surface_base),
+ device->surface_pending_relocations,
+ ARRAY_LENGTH (device->surface_pending_relocations),
+ device->surface_relocations,
+ ARRAY_LENGTH (device->surface_relocations));
+
+ i965_stream_init (&device->general,
+ device->general_base, sizeof (device->general_base),
+ device->general_pending_relocations,
+ ARRAY_LENGTH (device->general_pending_relocations),
+ NULL, 0);
+
+ i965_stream_init (&device->vertex,
+ device->vertex_base, sizeof (device->vertex_base),
+ device->vertex_pending_relocations,
+ ARRAY_LENGTH (device->vertex_pending_relocations),
+ NULL, 0);
+
+ cairo_list_init (&device->flush);
+ i965_device_reset (device);
+ device->vs_offset = (uint32_t) -1;
+ device->border_color_offset = (uint32_t) -1;
+ device->general_state = NULL;
+
+ return _cairo_drm_device_init (&device->intel.base,
+ fd, dev, vendor_id, chip_id,
+ I965_MAX_SIZE);
+
+ CLEANUP_WM_BINDING:
+ _cairo_hash_table_destroy (device->wm_bindings);
+ CLEANUP_WM_STATE:
+ _cairo_hash_table_destroy (device->wm_states);
+ CLEANUP_WM_KERNEL:
+ _cairo_hash_table_destroy (device->wm_kernels);
+ CLEANUP_CC:
+ _cairo_hash_table_destroy (device->cc_states);
+ CLEANUP_SF:
+ _cairo_hash_table_destroy (device->sf_states);
+ CLEANUP_INTEL:
+ intel_device_fini (&device->intel);
+ CLEANUP:
+ free (device);
+ return (cairo_drm_device_t *) _cairo_device_create_in_error (status);
+}
diff --git a/src/drm/cairo-drm-intel-brw-defines.h b/src/drm/cairo-drm-intel-brw-defines.h
new file mode 100755
index 000000000..b2be36f18
--- /dev/null
+++ b/src/drm/cairo-drm-intel-brw-defines.h
@@ -0,0 +1,824 @@
+/**************************************************************************
+ *
+ * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef CAIRO_DRM_INTEL_BRW_DEFINES_H
+#define CAIRO_DRM_INTEL_BRW_DEFINES_H
+
+/* 3D state: */
+#define _3DOP_3DSTATE_PIPELINED 0x0
+#define _3DOP_3DSTATE_NONPIPELINED 0x1
+#define _3DOP_3DCONTROL 0x2
+#define _3DOP_3DPRIMITIVE 0x3
+
+#define _3DSTATE_PIPELINED_POINTERS 0x00
+#define _3DSTATE_BINDING_TABLE_POINTERS 0x01
+#define _3DSTATE_VERTEX_BUFFERS 0x08
+#define _3DSTATE_VERTEX_ELEMENTS 0x09
+#define _3DSTATE_INDEX_BUFFER 0x0A
+#define _3DSTATE_VF_STATISTICS 0x0B
+#define _3DSTATE_DRAWING_RECTANGLE 0x00
+#define _3DSTATE_CONSTANT_COLOR 0x01
+#define _3DSTATE_SAMPLER_PALETTE_LOAD 0x02
+#define _3DSTATE_CHROMA_KEY 0x04
+#define _3DSTATE_DEPTH_BUFFER 0x05
+#define _3DSTATE_POLY_STIPPLE_OFFSET 0x06
+#define _3DSTATE_POLY_STIPPLE_PATTERN 0x07
+#define _3DSTATE_LINE_STIPPLE 0x08
+#define _3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP 0x09
+#define _3DCONTROL 0x00
+#define _3DPRIMITIVE 0x00
+
+#define PIPE_CONTROL_NOWRITE 0x00
+#define PIPE_CONTROL_WRITEIMMEDIATE 0x01
+#define PIPE_CONTROL_WRITEDEPTH 0x02
+#define PIPE_CONTROL_WRITETIMESTAMP 0x03
+
+#define PIPE_CONTROL_GTTWRITE_PROCESS_LOCAL 0x00
+#define PIPE_CONTROL_GTTWRITE_GLOBAL 0x01
+
+#define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \
+ ((Pipeline) << 27) | \
+ ((Opcode) << 24) | \
+ ((Subopcode) << 16))
+
+#define BRW_PIPE_CONTROL BRW_3D(3, 2, 0)
+#define BRW_PIPE_CONTROL_NOWRITE (0 << 14)
+#define BRW_PIPE_CONTROL_WRITE_QWORD (1 << 14)
+#define BRW_PIPE_CONTROL_WRITE_DEPTH (2 << 14)
+#define BRW_PIPE_CONTROL_WRITE_TIME (3 << 14)
+#define BRW_PIPE_CONTROL_DEPTH_STALL (1 << 13)
+#define BRW_PIPE_CONTROL_WC_FLUSH (1 << 12)
+#define BRW_PIPE_CONTROL_IS_FLUSH (1 << 11)
+#define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8)
+#define BRW_PIPE_CONTROL_GLOBAL_GTT (1 << 2)
+#define BRW_PIPE_CONTROL_LOCAL_PGTT (0 << 2)
+
+#define _3DPRIM_POINTLIST 0x01
+#define _3DPRIM_LINELIST 0x02
+#define _3DPRIM_LINESTRIP 0x03
+#define _3DPRIM_TRILIST 0x04
+#define _3DPRIM_TRISTRIP 0x05
+#define _3DPRIM_TRIFAN 0x06
+#define _3DPRIM_QUADLIST 0x07
+#define _3DPRIM_QUADSTRIP 0x08
+#define _3DPRIM_LINELIST_ADJ 0x09
+#define _3DPRIM_LINESTRIP_ADJ 0x0A
+#define _3DPRIM_TRILIST_ADJ 0x0B
+#define _3DPRIM_TRISTRIP_ADJ 0x0C
+#define _3DPRIM_TRISTRIP_REVERSE 0x0D
+#define _3DPRIM_POLYGON 0x0E
+#define _3DPRIM_RECTLIST 0x0F
+#define _3DPRIM_LINELOOP 0x10
+#define _3DPRIM_POINTLIST_BF 0x11
+#define _3DPRIM_LINESTRIP_CONT 0x12
+#define _3DPRIM_LINESTRIP_BF 0x13
+#define _3DPRIM_LINESTRIP_CONT_BF 0x14
+#define _3DPRIM_TRIFAN_NOSTIPPLE 0x15
+
+#define _3DPRIM_VERTEXBUFFER_ACCESS_SEQUENTIAL 0
+#define _3DPRIM_VERTEXBUFFER_ACCESS_RANDOM 1
+
+#define BRW_ANISORATIO_2 0
+#define BRW_ANISORATIO_4 1
+#define BRW_ANISORATIO_6 2
+#define BRW_ANISORATIO_8 3
+#define BRW_ANISORATIO_10 4
+#define BRW_ANISORATIO_12 5
+#define BRW_ANISORATIO_14 6
+#define BRW_ANISORATIO_16 7
+
+#define BRW_BLENDFACTOR_ONE 0x1
+#define BRW_BLENDFACTOR_SRC_COLOR 0x2
+#define BRW_BLENDFACTOR_SRC_ALPHA 0x3
+#define BRW_BLENDFACTOR_DST_ALPHA 0x4
+#define BRW_BLENDFACTOR_DST_COLOR 0x5
+#define BRW_BLENDFACTOR_SRC_ALPHA_SATURATE 0x6
+#define BRW_BLENDFACTOR_CONST_COLOR 0x7
+#define BRW_BLENDFACTOR_CONST_ALPHA 0x8
+#define BRW_BLENDFACTOR_SRC1_COLOR 0x9
+#define BRW_BLENDFACTOR_SRC1_ALPHA 0x0A
+#define BRW_BLENDFACTOR_ZERO 0x11
+#define BRW_BLENDFACTOR_INV_SRC_COLOR 0x12
+#define BRW_BLENDFACTOR_INV_SRC_ALPHA 0x13
+#define BRW_BLENDFACTOR_INV_DST_ALPHA 0x14
+#define BRW_BLENDFACTOR_INV_DST_COLOR 0x15
+#define BRW_BLENDFACTOR_INV_CONST_COLOR 0x17
+#define BRW_BLENDFACTOR_INV_CONST_ALPHA 0x18
+#define BRW_BLENDFACTOR_INV_SRC1_COLOR 0x19
+#define BRW_BLENDFACTOR_INV_SRC1_ALPHA 0x1A
+
+#define BRW_BLENDFUNCTION_ADD 0
+#define BRW_BLENDFUNCTION_SUBTRACT 1
+#define BRW_BLENDFUNCTION_REVERSE_SUBTRACT 2
+#define BRW_BLENDFUNCTION_MIN 3
+#define BRW_BLENDFUNCTION_MAX 4
+
+#define BRW_ALPHATEST_FORMAT_UNORM8 0
+#define BRW_ALPHATEST_FORMAT_FLOAT32 1
+
+#define BRW_CHROMAKEY_KILL_ON_ANY_MATCH 0
+#define BRW_CHROMAKEY_REPLACE_BLACK 1
+
+#define BRW_CLIP_API_OGL 0
+#define BRW_CLIP_API_DX 1
+
+#define BRW_CLIPMODE_NORMAL 0
+#define BRW_CLIPMODE_CLIP_ALL 1
+#define BRW_CLIPMODE_CLIP_NON_REJECTED 2
+#define BRW_CLIPMODE_REJECT_ALL 3
+#define BRW_CLIPMODE_ACCEPT_ALL 4
+
+#define BRW_CLIP_NDCSPACE 0
+#define BRW_CLIP_SCREENSPACE 1
+
+#define BRW_COMPAREFUNCTION_ALWAYS 0
+#define BRW_COMPAREFUNCTION_NEVER 1
+#define BRW_COMPAREFUNCTION_LESS 2
+#define BRW_COMPAREFUNCTION_EQUAL 3
+#define BRW_COMPAREFUNCTION_LEQUAL 4
+#define BRW_COMPAREFUNCTION_GREATER 5
+#define BRW_COMPAREFUNCTION_NOTEQUAL 6
+#define BRW_COMPAREFUNCTION_GEQUAL 7
+
+#define BRW_COVERAGE_PIXELS_HALF 0
+#define BRW_COVERAGE_PIXELS_1 1
+#define BRW_COVERAGE_PIXELS_2 2
+#define BRW_COVERAGE_PIXELS_4 3
+
+#define BRW_CULLMODE_BOTH 0
+#define BRW_CULLMODE_NONE 1
+#define BRW_CULLMODE_FRONT 2
+#define BRW_CULLMODE_BACK 3
+
+#define BRW_DEFAULTCOLOR_R8G8B8A8_UNORM 0
+#define BRW_DEFAULTCOLOR_R32G32B32A32_FLOAT 1
+
+#define BRW_DEPTHFORMAT_D32_FLOAT_S8X24_UINT 0
+#define BRW_DEPTHFORMAT_D32_FLOAT 1
+#define BRW_DEPTHFORMAT_D24_UNORM_S8_UINT 2
+#define BRW_DEPTHFORMAT_D16_UNORM 5
+
+#define BRW_FLOATING_POINT_IEEE_754 0
+#define BRW_FLOATING_POINT_NON_IEEE_754 1
+
+#define BRW_FRONTWINDING_CW 0
+#define BRW_FRONTWINDING_CCW 1
+
+#define BRW_INDEX_BYTE 0
+#define BRW_INDEX_WORD 1
+#define BRW_INDEX_DWORD 2
+
+#define BRW_LOGICOPFUNCTION_CLEAR 0
+#define BRW_LOGICOPFUNCTION_NOR 1
+#define BRW_LOGICOPFUNCTION_AND_INVERTED 2
+#define BRW_LOGICOPFUNCTION_COPY_INVERTED 3
+#define BRW_LOGICOPFUNCTION_AND_REVERSE 4
+#define BRW_LOGICOPFUNCTION_INVERT 5
+#define BRW_LOGICOPFUNCTION_XOR 6
+#define BRW_LOGICOPFUNCTION_NAND 7
+#define BRW_LOGICOPFUNCTION_AND 8
+#define BRW_LOGICOPFUNCTION_EQUIV 9
+#define BRW_LOGICOPFUNCTION_NOOP 10
+#define BRW_LOGICOPFUNCTION_OR_INVERTED 11
+#define BRW_LOGICOPFUNCTION_COPY 12
+#define BRW_LOGICOPFUNCTION_OR_REVERSE 13
+#define BRW_LOGICOPFUNCTION_OR 14
+#define BRW_LOGICOPFUNCTION_SET 15
+
+#define BRW_MAPFILTER_NEAREST 0x0
+#define BRW_MAPFILTER_LINEAR 0x1
+#define BRW_MAPFILTER_ANISOTROPIC 0x2
+
+#define BRW_MIPFILTER_NONE 0
+#define BRW_MIPFILTER_NEAREST 1
+#define BRW_MIPFILTER_LINEAR 3
+
+#define BRW_POLYGON_FRONT_FACING 0
+#define BRW_POLYGON_BACK_FACING 1
+
+#define BRW_PREFILTER_ALWAYS 0x0
+#define BRW_PREFILTER_NEVER 0x1
+#define BRW_PREFILTER_LESS 0x2
+#define BRW_PREFILTER_EQUAL 0x3
+#define BRW_PREFILTER_LEQUAL 0x4
+#define BRW_PREFILTER_GREATER 0x5
+#define BRW_PREFILTER_NOTEQUAL 0x6
+#define BRW_PREFILTER_GEQUAL 0x7
+
+#define BRW_PROVOKING_VERTEX_0 0
+#define BRW_PROVOKING_VERTEX_1 1
+#define BRW_PROVOKING_VERTEX_2 2
+
+#define BRW_RASTRULE_UPPER_LEFT 0
+#define BRW_RASTRULE_UPPER_RIGHT 1
+
+#define BRW_RENDERTARGET_CLAMPRANGE_UNORM 0
+#define BRW_RENDERTARGET_CLAMPRANGE_SNORM 1
+#define BRW_RENDERTARGET_CLAMPRANGE_FORMAT 2
+
+#define BRW_STENCILOP_KEEP 0
+#define BRW_STENCILOP_ZERO 1
+#define BRW_STENCILOP_REPLACE 2
+#define BRW_STENCILOP_INCRSAT 3
+#define BRW_STENCILOP_DECRSAT 4
+#define BRW_STENCILOP_INCR 5
+#define BRW_STENCILOP_DECR 6
+#define BRW_STENCILOP_INVERT 7
+
+#define BRW_SURFACE_MIPMAPLAYOUT_BELOW 0
+#define BRW_SURFACE_MIPMAPLAYOUT_RIGHT 1
+
+#define BRW_SURFACEFORMAT_R32G32B32A32_FLOAT 0x000
+#define BRW_SURFACEFORMAT_R32G32B32A32_SINT 0x001
+#define BRW_SURFACEFORMAT_R32G32B32A32_UINT 0x002
+#define BRW_SURFACEFORMAT_R32G32B32A32_UNORM 0x003
+#define BRW_SURFACEFORMAT_R32G32B32A32_SNORM 0x004
+#define BRW_SURFACEFORMAT_R64G64_FLOAT 0x005
+#define BRW_SURFACEFORMAT_R32G32B32X32_FLOAT 0x006
+#define BRW_SURFACEFORMAT_R32G32B32A32_SSCALED 0x007
+#define BRW_SURFACEFORMAT_R32G32B32A32_USCALED 0x008
+#define BRW_SURFACEFORMAT_R32G32B32_FLOAT 0x040
+#define BRW_SURFACEFORMAT_R32G32B32_SINT 0x041
+#define BRW_SURFACEFORMAT_R32G32B32_UINT 0x042
+#define BRW_SURFACEFORMAT_R32G32B32_UNORM 0x043
+#define BRW_SURFACEFORMAT_R32G32B32_SNORM 0x044
+#define BRW_SURFACEFORMAT_R32G32B32_SSCALED 0x045
+#define BRW_SURFACEFORMAT_R32G32B32_USCALED 0x046
+#define BRW_SURFACEFORMAT_R16G16B16A16_UNORM 0x080
+#define BRW_SURFACEFORMAT_R16G16B16A16_SNORM 0x081
+#define BRW_SURFACEFORMAT_R16G16B16A16_SINT 0x082
+#define BRW_SURFACEFORMAT_R16G16B16A16_UINT 0x083
+#define BRW_SURFACEFORMAT_R16G16B16A16_FLOAT 0x084
+#define BRW_SURFACEFORMAT_R32G32_FLOAT 0x085
+#define BRW_SURFACEFORMAT_R32G32_SINT 0x086
+#define BRW_SURFACEFORMAT_R32G32_UINT 0x087
+#define BRW_SURFACEFORMAT_R32_FLOAT_X8X24_TYPELESS 0x088
+#define BRW_SURFACEFORMAT_X32_TYPELESS_G8X24_UINT 0x089
+#define BRW_SURFACEFORMAT_L32A32_FLOAT 0x08A
+#define BRW_SURFACEFORMAT_R32G32_UNORM 0x08B
+#define BRW_SURFACEFORMAT_R32G32_SNORM 0x08C
+#define BRW_SURFACEFORMAT_R64_FLOAT 0x08D
+#define BRW_SURFACEFORMAT_R16G16B16X16_UNORM 0x08E
+#define BRW_SURFACEFORMAT_R16G16B16X16_FLOAT 0x08F
+#define BRW_SURFACEFORMAT_A32X32_FLOAT 0x090
+#define BRW_SURFACEFORMAT_L32X32_FLOAT 0x091
+#define BRW_SURFACEFORMAT_I32X32_FLOAT 0x092
+#define BRW_SURFACEFORMAT_R16G16B16A16_SSCALED 0x093
+#define BRW_SURFACEFORMAT_R16G16B16A16_USCALED 0x094
+#define BRW_SURFACEFORMAT_R32G32_SSCALED 0x095
+#define BRW_SURFACEFORMAT_R32G32_USCALED 0x096
+#define BRW_SURFACEFORMAT_B8G8R8A8_UNORM 0x0C0
+#define BRW_SURFACEFORMAT_B8G8R8A8_UNORM_SRGB 0x0C1
+#define BRW_SURFACEFORMAT_R10G10B10A2_UNORM 0x0C2
+#define BRW_SURFACEFORMAT_R10G10B10A2_UNORM_SRGB 0x0C3
+#define BRW_SURFACEFORMAT_R10G10B10A2_UINT 0x0C4
+#define BRW_SURFACEFORMAT_R10G10B10_SNORM_A2_UNORM 0x0C5
+#define BRW_SURFACEFORMAT_R8G8B8A8_UNORM 0x0C7
+#define BRW_SURFACEFORMAT_R8G8B8A8_UNORM_SRGB 0x0C8
+#define BRW_SURFACEFORMAT_R8G8B8A8_SNORM 0x0C9
+#define BRW_SURFACEFORMAT_R8G8B8A8_SINT 0x0CA
+#define BRW_SURFACEFORMAT_R8G8B8A8_UINT 0x0CB
+#define BRW_SURFACEFORMAT_R16G16_UNORM 0x0CC
+#define BRW_SURFACEFORMAT_R16G16_SNORM 0x0CD
+#define BRW_SURFACEFORMAT_R16G16_SINT 0x0CE
+#define BRW_SURFACEFORMAT_R16G16_UINT 0x0CF
+#define BRW_SURFACEFORMAT_R16G16_FLOAT 0x0D0
+#define BRW_SURFACEFORMAT_B10G10R10A2_UNORM 0x0D1
+#define BRW_SURFACEFORMAT_B10G10R10A2_UNORM_SRGB 0x0D2
+#define BRW_SURFACEFORMAT_R11G11B10_FLOAT 0x0D3
+#define BRW_SURFACEFORMAT_R32_SINT 0x0D6
+#define BRW_SURFACEFORMAT_R32_UINT 0x0D7
+#define BRW_SURFACEFORMAT_R32_FLOAT 0x0D8
+#define BRW_SURFACEFORMAT_R24_UNORM_X8_TYPELESS 0x0D9
+#define BRW_SURFACEFORMAT_X24_TYPELESS_G8_UINT 0x0DA
+#define BRW_SURFACEFORMAT_L16A16_UNORM 0x0DF
+#define BRW_SURFACEFORMAT_I24X8_UNORM 0x0E0
+#define BRW_SURFACEFORMAT_L24X8_UNORM 0x0E1
+#define BRW_SURFACEFORMAT_A24X8_UNORM 0x0E2
+#define BRW_SURFACEFORMAT_I32_FLOAT 0x0E3
+#define BRW_SURFACEFORMAT_L32_FLOAT 0x0E4
+#define BRW_SURFACEFORMAT_A32_FLOAT 0x0E5
+#define BRW_SURFACEFORMAT_B8G8R8X8_UNORM 0x0E9
+#define BRW_SURFACEFORMAT_B8G8R8X8_UNORM_SRGB 0x0EA
+#define BRW_SURFACEFORMAT_R8G8B8X8_UNORM 0x0EB
+#define BRW_SURFACEFORMAT_R8G8B8X8_UNORM_SRGB 0x0EC
+#define BRW_SURFACEFORMAT_R9G9B9E5_SHAREDEXP 0x0ED
+#define BRW_SURFACEFORMAT_B10G10R10X2_UNORM 0x0EE
+#define BRW_SURFACEFORMAT_L16A16_FLOAT 0x0F0
+#define BRW_SURFACEFORMAT_R32_UNORM 0x0F1
+#define BRW_SURFACEFORMAT_R32_SNORM 0x0F2
+#define BRW_SURFACEFORMAT_R10G10B10X2_USCALED 0x0F3
+#define BRW_SURFACEFORMAT_R8G8B8A8_SSCALED 0x0F4
+#define BRW_SURFACEFORMAT_R8G8B8A8_USCALED 0x0F5
+#define BRW_SURFACEFORMAT_R16G16_SSCALED 0x0F6
+#define BRW_SURFACEFORMAT_R16G16_USCALED 0x0F7
+#define BRW_SURFACEFORMAT_R32_SSCALED 0x0F8
+#define BRW_SURFACEFORMAT_R32_USCALED 0x0F9
+#define BRW_SURFACEFORMAT_B5G6R5_UNORM 0x100
+#define BRW_SURFACEFORMAT_B5G6R5_UNORM_SRGB 0x101
+#define BRW_SURFACEFORMAT_B5G5R5A1_UNORM 0x102
+#define BRW_SURFACEFORMAT_B5G5R5A1_UNORM_SRGB 0x103
+#define BRW_SURFACEFORMAT_B4G4R4A4_UNORM 0x104
+#define BRW_SURFACEFORMAT_B4G4R4A4_UNORM_SRGB 0x105
+#define BRW_SURFACEFORMAT_R8G8_UNORM 0x106
+#define BRW_SURFACEFORMAT_R8G8_SNORM 0x107
+#define BRW_SURFACEFORMAT_R8G8_SINT 0x108
+#define BRW_SURFACEFORMAT_R8G8_UINT 0x109
+#define BRW_SURFACEFORMAT_R16_UNORM 0x10A
+#define BRW_SURFACEFORMAT_R16_SNORM 0x10B
+#define BRW_SURFACEFORMAT_R16_SINT 0x10C
+#define BRW_SURFACEFORMAT_R16_UINT 0x10D
+#define BRW_SURFACEFORMAT_R16_FLOAT 0x10E
+#define BRW_SURFACEFORMAT_I16_UNORM 0x111
+#define BRW_SURFACEFORMAT_L16_UNORM 0x112
+#define BRW_SURFACEFORMAT_A16_UNORM 0x113
+#define BRW_SURFACEFORMAT_L8A8_UNORM 0x114
+#define BRW_SURFACEFORMAT_I16_FLOAT 0x115
+#define BRW_SURFACEFORMAT_L16_FLOAT 0x116
+#define BRW_SURFACEFORMAT_A16_FLOAT 0x117
+#define BRW_SURFACEFORMAT_R5G5_SNORM_B6_UNORM 0x119
+#define BRW_SURFACEFORMAT_B5G5R5X1_UNORM 0x11A
+#define BRW_SURFACEFORMAT_B5G5R5X1_UNORM_SRGB 0x11B
+#define BRW_SURFACEFORMAT_R8G8_SSCALED 0x11C
+#define BRW_SURFACEFORMAT_R8G8_USCALED 0x11D
+#define BRW_SURFACEFORMAT_R16_SSCALED 0x11E
+#define BRW_SURFACEFORMAT_R16_USCALED 0x11F
+#define BRW_SURFACEFORMAT_R8_UNORM 0x140
+#define BRW_SURFACEFORMAT_R8_SNORM 0x141
+#define BRW_SURFACEFORMAT_R8_SINT 0x142
+#define BRW_SURFACEFORMAT_R8_UINT 0x143
+#define BRW_SURFACEFORMAT_A8_UNORM 0x144
+#define BRW_SURFACEFORMAT_I8_UNORM 0x145
+#define BRW_SURFACEFORMAT_L8_UNORM 0x146
+#define BRW_SURFACEFORMAT_P4A4_UNORM 0x147
+#define BRW_SURFACEFORMAT_A4P4_UNORM 0x148
+#define BRW_SURFACEFORMAT_R8_SSCALED 0x149
+#define BRW_SURFACEFORMAT_R8_USCALED 0x14A
+#define BRW_SURFACEFORMAT_R1_UINT 0x181
+#define BRW_SURFACEFORMAT_YCRCB_NORMAL 0x182
+#define BRW_SURFACEFORMAT_YCRCB_SWAPUVY 0x183
+#define BRW_SURFACEFORMAT_BC1_UNORM 0x186
+#define BRW_SURFACEFORMAT_BC2_UNORM 0x187
+#define BRW_SURFACEFORMAT_BC3_UNORM 0x188
+#define BRW_SURFACEFORMAT_BC4_UNORM 0x189
+#define BRW_SURFACEFORMAT_BC5_UNORM 0x18A
+#define BRW_SURFACEFORMAT_BC1_UNORM_SRGB 0x18B
+#define BRW_SURFACEFORMAT_BC2_UNORM_SRGB 0x18C
+#define BRW_SURFACEFORMAT_BC3_UNORM_SRGB 0x18D
+#define BRW_SURFACEFORMAT_MONO8 0x18E
+#define BRW_SURFACEFORMAT_YCRCB_SWAPUV 0x18F
+#define BRW_SURFACEFORMAT_YCRCB_SWAPY 0x190
+#define BRW_SURFACEFORMAT_DXT1_RGB 0x191
+#define BRW_SURFACEFORMAT_FXT1 0x192
+#define BRW_SURFACEFORMAT_R8G8B8_UNORM 0x193
+#define BRW_SURFACEFORMAT_R8G8B8_SNORM 0x194
+#define BRW_SURFACEFORMAT_R8G8B8_SSCALED 0x195
+#define BRW_SURFACEFORMAT_R8G8B8_USCALED 0x196
+#define BRW_SURFACEFORMAT_R64G64B64A64_FLOAT 0x197
+#define BRW_SURFACEFORMAT_R64G64B64_FLOAT 0x198
+#define BRW_SURFACEFORMAT_BC4_SNORM 0x199
+#define BRW_SURFACEFORMAT_BC5_SNORM 0x19A
+#define BRW_SURFACEFORMAT_R16G16B16_UNORM 0x19C
+#define BRW_SURFACEFORMAT_R16G16B16_SNORM 0x19D
+#define BRW_SURFACEFORMAT_R16G16B16_SSCALED 0x19E
+#define BRW_SURFACEFORMAT_R16G16B16_USCALED 0x19F
+
+#define BRW_SURFACERETURNFORMAT_FLOAT32 0
+#define BRW_SURFACERETURNFORMAT_S1 1
+
+#define BRW_SURFACE_1D 0
+#define BRW_SURFACE_2D 1
+#define BRW_SURFACE_3D 2
+#define BRW_SURFACE_CUBE 3
+#define BRW_SURFACE_BUFFER 4
+#define BRW_SURFACE_NULL 7
+
+#define BRW_BORDER_COLOR_MODE_DEFAULT 0
+#define BRW_BORDER_COLOR_MODE_LEGACY 1
+
+#define BRW_TEXCOORDMODE_WRAP 0
+#define BRW_TEXCOORDMODE_MIRROR 1
+#define BRW_TEXCOORDMODE_CLAMP 2
+#define BRW_TEXCOORDMODE_CUBE 3
+#define BRW_TEXCOORDMODE_CLAMP_BORDER 4
+#define BRW_TEXCOORDMODE_MIRROR_ONCE 5
+
+#define BRW_THREAD_PRIORITY_NORMAL 0
+#define BRW_THREAD_PRIORITY_HIGH 1
+
+#define BRW_TILEWALK_XMAJOR 0
+#define BRW_TILEWALK_YMAJOR 1
+
+#define BRW_VERTEX_SUBPIXEL_PRECISION_8BITS 0
+#define BRW_VERTEX_SUBPIXEL_PRECISION_4BITS 1
+
+#define BRW_VERTEXBUFFER_ACCESS_VERTEXDATA 0
+#define BRW_VERTEXBUFFER_ACCESS_INSTANCEDATA 1
+
+#define BRW_VFCOMPONENT_NOSTORE 0
+#define BRW_VFCOMPONENT_STORE_SRC 1
+#define BRW_VFCOMPONENT_STORE_0 2
+#define BRW_VFCOMPONENT_STORE_1_FLT 3
+#define BRW_VFCOMPONENT_STORE_1_INT 4
+#define BRW_VFCOMPONENT_STORE_VID 5
+#define BRW_VFCOMPONENT_STORE_IID 6
+#define BRW_VFCOMPONENT_STORE_PID 7
+
+
+
+/* Execution Unit (EU) defines */
+
+#define BRW_ALIGN_1 0
+#define BRW_ALIGN_16 1
+
+#define BRW_ADDRESS_DIRECT 0
+#define BRW_ADDRESS_REGISTER_INDIRECT_REGISTER 1
+
+#define BRW_CHANNEL_X 0
+#define BRW_CHANNEL_Y 1
+#define BRW_CHANNEL_Z 2
+#define BRW_CHANNEL_W 3
+
+#define BRW_COMPRESSION_NONE 0
+#define BRW_COMPRESSION_2NDHALF 1
+#define BRW_COMPRESSION_COMPRESSED 2
+
+#define BRW_CONDITIONAL_NONE 0
+#define BRW_CONDITIONAL_Z 1
+#define BRW_CONDITIONAL_NZ 2
+#define BRW_CONDITIONAL_EQ 1 /* Z */
+#define BRW_CONDITIONAL_NEQ 2 /* NZ */
+#define BRW_CONDITIONAL_G 3
+#define BRW_CONDITIONAL_GE 4
+#define BRW_CONDITIONAL_L 5
+#define BRW_CONDITIONAL_LE 6
+#define BRW_CONDITIONAL_C 7
+#define BRW_CONDITIONAL_O 8
+
+#define BRW_DEBUG_NONE 0
+#define BRW_DEBUG_BREAKPOINT 1
+
+#define BRW_DEPENDENCY_NORMAL 0
+#define BRW_DEPENDENCY_NOTCLEARED 1
+#define BRW_DEPENDENCY_NOTCHECKED 2
+#define BRW_DEPENDENCY_DISABLE 3
+
+#define BRW_EXECUTE_1 0
+#define BRW_EXECUTE_2 1
+#define BRW_EXECUTE_4 2
+#define BRW_EXECUTE_8 3
+#define BRW_EXECUTE_16 4
+#define BRW_EXECUTE_32 5
+
+#define BRW_HORIZONTAL_STRIDE_0 0
+#define BRW_HORIZONTAL_STRIDE_1 1
+#define BRW_HORIZONTAL_STRIDE_2 2
+#define BRW_HORIZONTAL_STRIDE_4 3
+
+#define BRW_INSTRUCTION_NORMAL 0
+#define BRW_INSTRUCTION_SATURATE 1
+
+#define BRW_MASK_ENABLE 0
+#define BRW_MASK_DISABLE 1
+
+#define BRW_OPCODE_MOV 1
+#define BRW_OPCODE_SEL 2
+#define BRW_OPCODE_NOT 4
+#define BRW_OPCODE_AND 5
+#define BRW_OPCODE_OR 6
+#define BRW_OPCODE_XOR 7
+#define BRW_OPCODE_SHR 8
+#define BRW_OPCODE_SHL 9
+#define BRW_OPCODE_RSR 10
+#define BRW_OPCODE_RSL 11
+#define BRW_OPCODE_ASR 12
+#define BRW_OPCODE_CMP 16
+#define BRW_OPCODE_JMPI 32
+#define BRW_OPCODE_IF 34
+#define BRW_OPCODE_IFF 35
+#define BRW_OPCODE_ELSE 36
+#define BRW_OPCODE_ENDIF 37
+#define BRW_OPCODE_DO 38
+#define BRW_OPCODE_WHILE 39
+#define BRW_OPCODE_BREAK 40
+#define BRW_OPCODE_CONTINUE 41
+#define BRW_OPCODE_HALT 42
+#define BRW_OPCODE_MSAVE 44
+#define BRW_OPCODE_MRESTORE 45
+#define BRW_OPCODE_PUSH 46
+#define BRW_OPCODE_POP 47
+#define BRW_OPCODE_WAIT 48
+#define BRW_OPCODE_SEND 49
+#define BRW_OPCODE_ADD 64
+#define BRW_OPCODE_MUL 65
+#define BRW_OPCODE_AVG 66
+#define BRW_OPCODE_FRC 67
+#define BRW_OPCODE_RNDU 68
+#define BRW_OPCODE_RNDD 69
+#define BRW_OPCODE_RNDE 70
+#define BRW_OPCODE_RNDZ 71
+#define BRW_OPCODE_MAC 72
+#define BRW_OPCODE_MACH 73
+#define BRW_OPCODE_LZD 74
+#define BRW_OPCODE_SAD2 80
+#define BRW_OPCODE_SADA2 81
+#define BRW_OPCODE_DP4 84
+#define BRW_OPCODE_DPH 85
+#define BRW_OPCODE_DP3 86
+#define BRW_OPCODE_DP2 87
+#define BRW_OPCODE_DPA2 88
+#define BRW_OPCODE_LINE 89
+#define BRW_OPCODE_NOP 126
+
+#define BRW_PREDICATE_NONE 0
+#define BRW_PREDICATE_NORMAL 1
+#define BRW_PREDICATE_ALIGN1_ANYV 2
+#define BRW_PREDICATE_ALIGN1_ALLV 3
+#define BRW_PREDICATE_ALIGN1_ANY2H 4
+#define BRW_PREDICATE_ALIGN1_ALL2H 5
+#define BRW_PREDICATE_ALIGN1_ANY4H 6
+#define BRW_PREDICATE_ALIGN1_ALL4H 7
+#define BRW_PREDICATE_ALIGN1_ANY8H 8
+#define BRW_PREDICATE_ALIGN1_ALL8H 9
+#define BRW_PREDICATE_ALIGN1_ANY16H 10
+#define BRW_PREDICATE_ALIGN1_ALL16H 11
+#define BRW_PREDICATE_ALIGN16_REPLICATE_X 2
+#define BRW_PREDICATE_ALIGN16_REPLICATE_Y 3
+#define BRW_PREDICATE_ALIGN16_REPLICATE_Z 4
+#define BRW_PREDICATE_ALIGN16_REPLICATE_W 5
+#define BRW_PREDICATE_ALIGN16_ANY4H 6
+#define BRW_PREDICATE_ALIGN16_ALL4H 7
+
+#define BRW_ARCHITECTURE_REGISTER_FILE 0
+#define BRW_GENERAL_REGISTER_FILE 1
+#define BRW_MESSAGE_REGISTER_FILE 2
+#define BRW_IMMEDIATE_VALUE 3
+
+#define BRW_REGISTER_TYPE_UD 0
+#define BRW_REGISTER_TYPE_D 1
+#define BRW_REGISTER_TYPE_UW 2
+#define BRW_REGISTER_TYPE_W 3
+#define BRW_REGISTER_TYPE_UB 4
+#define BRW_REGISTER_TYPE_B 5
+#define BRW_REGISTER_TYPE_VF 5 /* packed float vector, immediates only? */
+#define BRW_REGISTER_TYPE_HF 6
+#define BRW_REGISTER_TYPE_V 6 /* packed int vector, immediates only, uword dest only */
+#define BRW_REGISTER_TYPE_F 7
+
+#define BRW_ARF_NULL 0x00
+#define BRW_ARF_ADDRESS 0x10
+#define BRW_ARF_ACCUMULATOR 0x20
+#define BRW_ARF_FLAG 0x30
+#define BRW_ARF_MASK 0x40
+#define BRW_ARF_MASK_STACK 0x50
+#define BRW_ARF_MASK_STACK_DEPTH 0x60
+#define BRW_ARF_STATE 0x70
+#define BRW_ARF_CONTROL 0x80
+#define BRW_ARF_NOTIFICATION_COUNT 0x90
+#define BRW_ARF_IP 0xA0
+
+#define BRW_AMASK 0
+#define BRW_IMASK 1
+#define BRW_LMASK 2
+#define BRW_CMASK 3
+
+
+
+#define BRW_THREAD_NORMAL 0
+#define BRW_THREAD_ATOMIC 1
+#define BRW_THREAD_SWITCH 2
+
+#define BRW_VERTICAL_STRIDE_0 0
+#define BRW_VERTICAL_STRIDE_1 1
+#define BRW_VERTICAL_STRIDE_2 2
+#define BRW_VERTICAL_STRIDE_4 3
+#define BRW_VERTICAL_STRIDE_8 4
+#define BRW_VERTICAL_STRIDE_16 5
+#define BRW_VERTICAL_STRIDE_32 6
+#define BRW_VERTICAL_STRIDE_64 7
+#define BRW_VERTICAL_STRIDE_128 8
+#define BRW_VERTICAL_STRIDE_256 9
+#define BRW_VERTICAL_STRIDE_ONE_DIMENSIONAL 0xF
+
+#define BRW_WIDTH_1 0
+#define BRW_WIDTH_2 1
+#define BRW_WIDTH_4 2
+#define BRW_WIDTH_8 3
+#define BRW_WIDTH_16 4
+
+#define BRW_STATELESS_BUFFER_BOUNDARY_1K 0
+#define BRW_STATELESS_BUFFER_BOUNDARY_2K 1
+#define BRW_STATELESS_BUFFER_BOUNDARY_4K 2
+#define BRW_STATELESS_BUFFER_BOUNDARY_8K 3
+#define BRW_STATELESS_BUFFER_BOUNDARY_16K 4
+#define BRW_STATELESS_BUFFER_BOUNDARY_32K 5
+#define BRW_STATELESS_BUFFER_BOUNDARY_64K 6
+#define BRW_STATELESS_BUFFER_BOUNDARY_128K 7
+#define BRW_STATELESS_BUFFER_BOUNDARY_256K 8
+#define BRW_STATELESS_BUFFER_BOUNDARY_512K 9
+#define BRW_STATELESS_BUFFER_BOUNDARY_1M 10
+#define BRW_STATELESS_BUFFER_BOUNDARY_2M 11
+
+#define BRW_POLYGON_FACING_FRONT 0
+#define BRW_POLYGON_FACING_BACK 1
+
+#define BRW_MESSAGE_TARGET_NULL 0
+#define BRW_MESSAGE_TARGET_MATH 1
+#define BRW_MESSAGE_TARGET_SAMPLER 2
+#define BRW_MESSAGE_TARGET_GATEWAY 3
+#define BRW_MESSAGE_TARGET_DATAPORT_READ 4
+#define BRW_MESSAGE_TARGET_DATAPORT_WRITE 5
+#define BRW_MESSAGE_TARGET_URB 6
+#define BRW_MESSAGE_TARGET_THREAD_SPAWNER 7
+
+#define BRW_SAMPLER_RETURN_FORMAT_FLOAT32 0
+#define BRW_SAMPLER_RETURN_FORMAT_UINT32 2
+#define BRW_SAMPLER_RETURN_FORMAT_SINT32 3
+
+#define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE 0
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE 0
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_BIAS 0
+#define BRW_SAMPLER_MESSAGE_SIMD8_KILLPIX 1
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_LOD 1
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_LOD 1
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_GRADIENTS 2
+#define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE_GRADIENTS 2
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_COMPARE 0
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_COMPARE 2
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_RESINFO 2
+#define BRW_SAMPLER_MESSAGE_SIMD8_RESINFO 2
+#define BRW_SAMPLER_MESSAGE_SIMD16_RESINFO 2
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_LD 3
+#define BRW_SAMPLER_MESSAGE_SIMD8_LD 3
+#define BRW_SAMPLER_MESSAGE_SIMD16_LD 3
+
+#define BRW_DATAPORT_OWORD_BLOCK_1_OWORDLOW 0
+#define BRW_DATAPORT_OWORD_BLOCK_1_OWORDHIGH 1
+#define BRW_DATAPORT_OWORD_BLOCK_2_OWORDS 2
+#define BRW_DATAPORT_OWORD_BLOCK_4_OWORDS 3
+#define BRW_DATAPORT_OWORD_BLOCK_8_OWORDS 4
+
+#define BRW_DATAPORT_OWORD_DUAL_BLOCK_1OWORD 0
+#define BRW_DATAPORT_OWORD_DUAL_BLOCK_4OWORDS 2
+
+#define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_8DWORDS 2
+#define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_16DWORDS 3
+
+#define BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ 0
+#define BRW_DATAPORT_READ_MESSAGE_OWORD_DUAL_BLOCK_READ 1
+#define BRW_DATAPORT_READ_MESSAGE_DWORD_BLOCK_READ 2
+#define BRW_DATAPORT_READ_MESSAGE_DWORD_SCATTERED_READ 3
+
+#define BRW_DATAPORT_READ_TARGET_DATA_CACHE 0
+#define BRW_DATAPORT_READ_TARGET_RENDER_CACHE 1
+#define BRW_DATAPORT_READ_TARGET_SAMPLER_CACHE 2
+
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE 0
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED 1
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN01 2
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN23 3
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_SINGLE_SOURCE_SUBSPAN01 4
+
+#define BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE 0
+#define BRW_DATAPORT_WRITE_MESSAGE_OWORD_DUAL_BLOCK_WRITE 1
+#define BRW_DATAPORT_WRITE_MESSAGE_DWORD_BLOCK_WRITE 2
+#define BRW_DATAPORT_WRITE_MESSAGE_DWORD_SCATTERED_WRITE 3
+#define BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE 4
+#define BRW_DATAPORT_WRITE_MESSAGE_STREAMED_VERTEX_BUFFER_WRITE 5
+#define BRW_DATAPORT_WRITE_MESSAGE_FLUSH_RENDER_CACHE 7
+
+#define BRW_MATH_FUNCTION_INV 1
+#define BRW_MATH_FUNCTION_LOG 2
+#define BRW_MATH_FUNCTION_EXP 3
+#define BRW_MATH_FUNCTION_SQRT 4
+#define BRW_MATH_FUNCTION_RSQ 5
+#define BRW_MATH_FUNCTION_SIN 6 /* was 7 */
+#define BRW_MATH_FUNCTION_COS 7 /* was 8 */
+#define BRW_MATH_FUNCTION_SINCOS 8 /* was 6 */
+#define BRW_MATH_FUNCTION_TAN 9
+#define BRW_MATH_FUNCTION_POW 10
+#define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT_AND_REMAINDER 11
+#define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT 12
+#define BRW_MATH_FUNCTION_INT_DIV_REMAINDER 13
+
+#define BRW_MATH_INTEGER_UNSIGNED 0
+#define BRW_MATH_INTEGER_SIGNED 1
+
+#define BRW_MATH_PRECISION_FULL 0
+#define BRW_MATH_PRECISION_PARTIAL 1
+
+#define BRW_MATH_SATURATE_NONE 0
+#define BRW_MATH_SATURATE_SATURATE 1
+
+#define BRW_MATH_DATA_VECTOR 0
+#define BRW_MATH_DATA_SCALAR 1
+
+#define BRW_URB_OPCODE_WRITE 0
+
+#define BRW_URB_SWIZZLE_NONE 0
+#define BRW_URB_SWIZZLE_INTERLEAVE 1
+#define BRW_URB_SWIZZLE_TRANSPOSE 2
+
+#define BRW_SCRATCH_SPACE_SIZE_1K 0
+#define BRW_SCRATCH_SPACE_SIZE_2K 1
+#define BRW_SCRATCH_SPACE_SIZE_4K 2
+#define BRW_SCRATCH_SPACE_SIZE_8K 3
+#define BRW_SCRATCH_SPACE_SIZE_16K 4
+#define BRW_SCRATCH_SPACE_SIZE_32K 5
+#define BRW_SCRATCH_SPACE_SIZE_64K 6
+#define BRW_SCRATCH_SPACE_SIZE_128K 7
+#define BRW_SCRATCH_SPACE_SIZE_256K 8
+#define BRW_SCRATCH_SPACE_SIZE_512K 9
+#define BRW_SCRATCH_SPACE_SIZE_1M 10
+#define BRW_SCRATCH_SPACE_SIZE_2M 11
+
+
+
+
+#define CMD_URB_FENCE 0x6000
+#define CMD_CONST_BUFFER_STATE 0x6001
+#define CMD_CONST_BUFFER 0x6002
+
+#define CMD_STATE_BASE_ADDRESS 0x6101
+#define CMD_STATE_INSN_POINTER 0x6102
+#define CMD_PIPELINE_SELECT 0x6104
+
+#define CMD_PIPELINED_STATE_POINTERS 0x7800
+#define CMD_BINDING_TABLE_PTRS 0x7801
+#define CMD_VERTEX_BUFFER 0x7808
+#define CMD_VERTEX_ELEMENT 0x7809
+#define CMD_INDEX_BUFFER 0x780a
+#define CMD_VF_STATISTICS 0x780b
+
+#define CMD_DRAW_RECT 0x7900
+#define CMD_BLEND_CONSTANT_COLOR 0x7901
+#define CMD_CHROMA_KEY 0x7904
+#define CMD_DEPTH_BUFFER 0x7905
+#define CMD_POLY_STIPPLE_OFFSET 0x7906
+#define CMD_POLY_STIPPLE_PATTERN 0x7907
+#define CMD_LINE_STIPPLE_PATTERN 0x7908
+#define CMD_GLOBAL_DEPTH_OFFSET_CLAMP 0x7908
+
+#define CMD_PIPE_CONTROL 0x7a00
+
+#define CMD_3D_PRIM 0x7b00
+
+#define CMD_MI_FLUSH 0x0200
+
+
+/* Various values from the R0 vertex header:
+ */
+#define R02_PRIM_END 0x1
+#define R02_PRIM_START 0x2
+
+/* media pipeline */
+
+#define BRW_VFE_MODE_GENERIC 0x0
+#define BRW_VFE_MODE_VLD_MPEG2 0x1
+#define BRW_VFE_MODE_IS 0x2
+#define BRW_VFE_MODE_AVC_MC 0x4
+#define BRW_VFE_MODE_AVC_IT 0x7
+#define BRW_VFE_MODE_VC1_IT 0xB
+
+#define BRW_VFE_DEBUG_COUNTER_FREE 0
+#define BRW_VFE_DEBUG_COUNTER_FROZEN 1
+#define BRW_VFE_DEBUG_COUNTER_ONCE 2
+#define BRW_VFE_DEBUG_COUNTER_ALWAYS 3
+
+/* VLD_STATE */
+#define BRW_MPEG_TOP_FIELD 1
+#define BRW_MPEG_BOTTOM_FIELD 2
+#define BRW_MPEG_FRAME 3
+#define BRW_MPEG_QSCALE_LINEAR 0
+#define BRW_MPEG_QSCALE_NONLINEAR 1
+#define BRW_MPEG_ZIGZAG_SCAN 0
+#define BRW_MPEG_ALTER_VERTICAL_SCAN 1
+#define BRW_MPEG_I_PICTURE 1
+#define BRW_MPEG_P_PICTURE 2
+#define BRW_MPEG_B_PICTURE 3
+
+#endif
diff --git a/src/drm/cairo-drm-intel-brw-eu-emit.c b/src/drm/cairo-drm-intel-brw-eu-emit.c
new file mode 100755
index 000000000..f27b23804
--- /dev/null
+++ b/src/drm/cairo-drm-intel-brw-eu-emit.c
@@ -0,0 +1,1089 @@
+/*
+ Copyright (C) Intel Corp. 2006. All Rights Reserved.
+ Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+ develop this 3D driver.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial
+ portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ * Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-drm-intel-brw-eu.h"
+
+#include <string.h>
+
+/***********************************************************************
+ * Internal helper for constructing instructions
+ */
+
+static void guess_execution_size( struct brw_instruction *insn,
+ struct brw_reg reg )
+{
+ if (reg.width == BRW_WIDTH_8 &&
+ insn->header.compression_control == BRW_COMPRESSION_COMPRESSED)
+ insn->header.execution_size = BRW_EXECUTE_16;
+ else
+ insn->header.execution_size = reg.width; /* note - definitions are compatible */
+}
+
+
+void
+brw_instruction_set_destination (struct brw_instruction *insn,
+ struct brw_reg dest)
+{
+ insn->bits1.da1.dest_reg_file = dest.file;
+ insn->bits1.da1.dest_reg_type = dest.type;
+ insn->bits1.da1.dest_address_mode = dest.address_mode;
+
+ if (dest.address_mode == BRW_ADDRESS_DIRECT) {
+ insn->bits1.da1.dest_reg_nr = dest.nr;
+
+ if (insn->header.access_mode == BRW_ALIGN_1) {
+ insn->bits1.da1.dest_subreg_nr = dest.subnr;
+ if (dest.hstride == BRW_HORIZONTAL_STRIDE_0)
+ dest.hstride = BRW_HORIZONTAL_STRIDE_1;
+ insn->bits1.da1.dest_horiz_stride = dest.hstride;
+ } else {
+ insn->bits1.da16.dest_subreg_nr = dest.subnr / 16;
+ insn->bits1.da16.dest_writemask = dest.dw1.bits.writemask;
+ }
+ } else {
+ insn->bits1.ia1.dest_subreg_nr = dest.subnr;
+
+ /* These are different sizes in align1 vs align16:
+ */
+ if (insn->header.access_mode == BRW_ALIGN_1) {
+ insn->bits1.ia1.dest_indirect_offset = dest.dw1.bits.indirect_offset;
+ if (dest.hstride == BRW_HORIZONTAL_STRIDE_0)
+ dest.hstride = BRW_HORIZONTAL_STRIDE_1;
+ insn->bits1.ia1.dest_horiz_stride = dest.hstride;
+ } else {
+ insn->bits1.ia16.dest_indirect_offset = dest.dw1.bits.indirect_offset;
+ }
+ }
+
+ /* NEW: Set the execution size based on dest.width and
+ * insn->compression_control:
+ */
+ guess_execution_size(insn, dest);
+}
+
+void
+brw_instruction_set_source0 (struct brw_instruction *insn,
+ struct brw_reg reg)
+{
+ assert(reg.file != BRW_MESSAGE_REGISTER_FILE);
+
+ insn->bits1.da1.src0_reg_file = reg.file;
+ insn->bits1.da1.src0_reg_type = reg.type;
+ insn->bits2.da1.src0_abs = reg.abs;
+ insn->bits2.da1.src0_negate = reg.negate;
+ insn->bits2.da1.src0_address_mode = reg.address_mode;
+
+ if (reg.file == BRW_IMMEDIATE_VALUE) {
+ insn->bits3.ud = reg.dw1.ud;
+
+ /* Required to set some fields in src1 as well:
+ */
+ insn->bits1.da1.src1_reg_file = 0; /* arf */
+ insn->bits1.da1.src1_reg_type = reg.type;
+ } else {
+ if (reg.address_mode == BRW_ADDRESS_DIRECT) {
+ if (insn->header.access_mode == BRW_ALIGN_1) {
+ insn->bits2.da1.src0_subreg_nr = reg.subnr;
+ insn->bits2.da1.src0_reg_nr = reg.nr;
+ } else {
+ insn->bits2.da16.src0_subreg_nr = reg.subnr / 16;
+ insn->bits2.da16.src0_reg_nr = reg.nr;
+ }
+ } else {
+ insn->bits2.ia1.src0_subreg_nr = reg.subnr;
+
+ if (insn->header.access_mode == BRW_ALIGN_1) {
+ insn->bits2.ia1.src0_indirect_offset = reg.dw1.bits.indirect_offset;
+ } else {
+ insn->bits2.ia16.src0_subreg_nr = reg.dw1.bits.indirect_offset;
+ }
+ }
+
+ if (insn->header.access_mode == BRW_ALIGN_1) {
+ if (reg.width == BRW_WIDTH_1 &&
+ insn->header.execution_size == BRW_EXECUTE_1) {
+ insn->bits2.da1.src0_horiz_stride = BRW_HORIZONTAL_STRIDE_0;
+ insn->bits2.da1.src0_width = BRW_WIDTH_1;
+ insn->bits2.da1.src0_vert_stride = BRW_VERTICAL_STRIDE_0;
+ } else {
+ insn->bits2.da1.src0_horiz_stride = reg.hstride;
+ insn->bits2.da1.src0_width = reg.width;
+ insn->bits2.da1.src0_vert_stride = reg.vstride;
+ }
+ } else {
+ insn->bits2.da16.src0_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X);
+ insn->bits2.da16.src0_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y);
+ insn->bits2.da16.src0_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z);
+ insn->bits2.da16.src0_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W);
+
+ /* This is an oddity of the fact we're using the same
+ * descriptions for registers in align_16 as align_1:
+ */
+ if (reg.vstride == BRW_VERTICAL_STRIDE_8)
+ insn->bits2.da16.src0_vert_stride = BRW_VERTICAL_STRIDE_4;
+ else
+ insn->bits2.da16.src0_vert_stride = reg.vstride;
+ }
+ }
+}
+
+
+void brw_set_src1( struct brw_instruction *insn,
+ struct brw_reg reg )
+{
+ assert(reg.file != BRW_MESSAGE_REGISTER_FILE);
+
+ insn->bits1.da1.src1_reg_file = reg.file;
+ insn->bits1.da1.src1_reg_type = reg.type;
+ insn->bits3.da1.src1_abs = reg.abs;
+ insn->bits3.da1.src1_negate = reg.negate;
+
+ /* Only src1 can be immediate in two-argument instructions.
+ */
+ assert(insn->bits1.da1.src0_reg_file != BRW_IMMEDIATE_VALUE);
+
+ if (reg.file == BRW_IMMEDIATE_VALUE) {
+ insn->bits3.ud = reg.dw1.ud;
+ }
+ else {
+ /* This is a hardware restriction, which may or may not be lifted
+ * in the future:
+ */
+ assert (reg.address_mode == BRW_ADDRESS_DIRECT);
+ //assert (reg.file == BRW_GENERAL_REGISTER_FILE);
+
+ if (insn->header.access_mode == BRW_ALIGN_1) {
+ insn->bits3.da1.src1_subreg_nr = reg.subnr;
+ insn->bits3.da1.src1_reg_nr = reg.nr;
+ }
+ else {
+ insn->bits3.da16.src1_subreg_nr = reg.subnr / 16;
+ insn->bits3.da16.src1_reg_nr = reg.nr;
+ }
+
+ if (insn->header.access_mode == BRW_ALIGN_1) {
+ if (reg.width == BRW_WIDTH_1 &&
+ insn->header.execution_size == BRW_EXECUTE_1) {
+ insn->bits3.da1.src1_horiz_stride = BRW_HORIZONTAL_STRIDE_0;
+ insn->bits3.da1.src1_width = BRW_WIDTH_1;
+ insn->bits3.da1.src1_vert_stride = BRW_VERTICAL_STRIDE_0;
+ }
+ else {
+ insn->bits3.da1.src1_horiz_stride = reg.hstride;
+ insn->bits3.da1.src1_width = reg.width;
+ insn->bits3.da1.src1_vert_stride = reg.vstride;
+ }
+ }
+ else {
+ insn->bits3.da16.src1_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X);
+ insn->bits3.da16.src1_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y);
+ insn->bits3.da16.src1_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z);
+ insn->bits3.da16.src1_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W);
+
+ /* This is an oddity of the fact we're using the same
+ * descriptions for registers in align_16 as align_1:
+ */
+ if (reg.vstride == BRW_VERTICAL_STRIDE_8)
+ insn->bits3.da16.src1_vert_stride = BRW_VERTICAL_STRIDE_4;
+ else
+ insn->bits3.da16.src1_vert_stride = reg.vstride;
+ }
+ }
+}
+
+
+
+static void brw_set_math_message( struct brw_instruction *insn,
+ uint32_t msg_length,
+ uint32_t response_length,
+ uint32_t function,
+ uint32_t integer_type,
+ int low_precision,
+ int saturate,
+ uint32_t dataType )
+{
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->bits3.math.function = function;
+ insn->bits3.math.int_type = integer_type;
+ insn->bits3.math.precision = low_precision;
+ insn->bits3.math.saturate = saturate;
+ insn->bits3.math.data_type = dataType;
+ insn->bits3.math.response_length = response_length;
+ insn->bits3.math.msg_length = msg_length;
+ insn->bits3.math.msg_target = BRW_MESSAGE_TARGET_MATH;
+ insn->bits3.math.end_of_thread = 0;
+}
+
+static void brw_set_urb_message( struct brw_instruction *insn,
+ int allocate,
+ int used,
+ uint32_t msg_length,
+ uint32_t response_length,
+ int end_of_thread,
+ int complete,
+ uint32_t offset,
+ uint32_t swizzle_control )
+{
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->bits3.urb.opcode = 0; /* ? */
+ insn->bits3.urb.offset = offset;
+ insn->bits3.urb.swizzle_control = swizzle_control;
+ insn->bits3.urb.allocate = allocate;
+ insn->bits3.urb.used = used; /* ? */
+ insn->bits3.urb.complete = complete;
+ insn->bits3.urb.response_length = response_length;
+ insn->bits3.urb.msg_length = msg_length;
+ insn->bits3.urb.msg_target = BRW_MESSAGE_TARGET_URB;
+ insn->bits3.urb.end_of_thread = end_of_thread;
+}
+
+void
+brw_instruction_set_dp_write_message (struct brw_instruction *insn,
+ uint32_t binding_table_index,
+ uint32_t msg_control,
+ uint32_t msg_type,
+ uint32_t msg_length,
+ uint32_t pixel_scoreboard_clear,
+ uint32_t response_length,
+ uint32_t end_of_thread)
+{
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->bits3.dp_write.binding_table_index = binding_table_index;
+ insn->bits3.dp_write.msg_control = msg_control;
+ insn->bits3.dp_write.pixel_scoreboard_clear = pixel_scoreboard_clear;
+ insn->bits3.dp_write.msg_type = msg_type;
+ insn->bits3.dp_write.send_commit_msg = 0;
+ insn->bits3.dp_write.response_length = response_length;
+ insn->bits3.dp_write.msg_length = msg_length;
+ insn->bits3.dp_write.msg_target = BRW_MESSAGE_TARGET_DATAPORT_WRITE;
+ insn->bits3.urb.end_of_thread = end_of_thread;
+}
+
+static void brw_set_dp_read_message( struct brw_instruction *insn,
+ uint32_t binding_table_index,
+ uint32_t msg_control,
+ uint32_t msg_type,
+ uint32_t target_cache,
+ uint32_t msg_length,
+ uint32_t response_length,
+ uint32_t end_of_thread )
+{
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->bits3.dp_read.binding_table_index = binding_table_index;
+ insn->bits3.dp_read.msg_control = msg_control;
+ insn->bits3.dp_read.msg_type = msg_type;
+ insn->bits3.dp_read.target_cache = target_cache;
+ insn->bits3.dp_read.response_length = response_length;
+ insn->bits3.dp_read.msg_length = msg_length;
+ insn->bits3.dp_read.msg_target = BRW_MESSAGE_TARGET_DATAPORT_READ;
+ insn->bits3.dp_read.end_of_thread = end_of_thread;
+}
+
+static void
+brw_set_sampler_message (struct brw_instruction *insn,
+ cairo_bool_t is_g4x,
+ uint32_t binding_table_index,
+ uint32_t sampler,
+ uint32_t msg_type,
+ uint32_t response_length,
+ uint32_t msg_length,
+ cairo_bool_t eot)
+{
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ if (is_g4x) {
+ /* XXX presume the driver is sane! */
+ insn->bits3.sampler_g4x.binding_table_index = binding_table_index;
+ insn->bits3.sampler_g4x.sampler = sampler;
+ insn->bits3.sampler_g4x.msg_type = msg_type;
+ insn->bits3.sampler_g4x.response_length = response_length;
+ insn->bits3.sampler_g4x.msg_length = msg_length;
+ insn->bits3.sampler_g4x.end_of_thread = eot;
+ insn->bits3.sampler_g4x.msg_target = BRW_MESSAGE_TARGET_SAMPLER;
+ } else {
+ insn->bits3.sampler.binding_table_index = binding_table_index;
+ insn->bits3.sampler.sampler = sampler;
+ insn->bits3.sampler.msg_type = msg_type;
+ insn->bits3.sampler.return_format = BRW_SAMPLER_RETURN_FORMAT_FLOAT32;
+ insn->bits3.sampler.response_length = response_length;
+ insn->bits3.sampler.msg_length = msg_length;
+ insn->bits3.sampler.end_of_thread = eot;
+ insn->bits3.sampler.msg_target = BRW_MESSAGE_TARGET_SAMPLER;
+ }
+}
+
+struct brw_instruction *
+brw_next_instruction (struct brw_compile *p,
+ uint32_t opcode)
+{
+ struct brw_instruction *insn;
+
+ assert(p->nr_insn + 1 < BRW_EU_MAX_INSN);
+
+ insn = &p->store[p->nr_insn++];
+ memcpy(insn, p->current, sizeof(*insn));
+
+ /* Reset this one-shot flag: */
+ if (p->current->header.destreg__conditonalmod) {
+ p->current->header.destreg__conditonalmod = 0;
+ p->current->header.predicate_control = BRW_PREDICATE_NORMAL;
+ }
+
+ insn->header.opcode = opcode;
+ return insn;
+}
+
+static struct brw_instruction *brw_alu1( struct brw_compile *p,
+ uint32_t opcode,
+ struct brw_reg dest,
+ struct brw_reg src )
+{
+ struct brw_instruction *insn = brw_next_instruction(p, opcode);
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src);
+ return insn;
+}
+
+static struct brw_instruction *brw_alu2(struct brw_compile *p,
+ uint32_t opcode,
+ struct brw_reg dest,
+ struct brw_reg src0,
+ struct brw_reg src1 )
+{
+ struct brw_instruction *insn = brw_next_instruction(p, opcode);
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src0);
+ brw_set_src1(insn, src1);
+ return insn;
+}
+
+
+/***********************************************************************
+ * Convenience routines.
+ */
+#define ALU1(OP) \
+ struct brw_instruction *brw_##OP(struct brw_compile *p, \
+ struct brw_reg dest, \
+ struct brw_reg src0) \
+{ \
+ return brw_alu1(p, BRW_OPCODE_##OP, dest, src0); \
+}
+
+#define ALU2(OP) \
+ struct brw_instruction *brw_##OP(struct brw_compile *p, \
+ struct brw_reg dest, \
+ struct brw_reg src0, \
+ struct brw_reg src1) \
+{ \
+ return brw_alu2(p, BRW_OPCODE_##OP, dest, src0, src1); \
+}
+
+
+ ALU1(MOV)
+ ALU2(SEL)
+ ALU1(NOT)
+ ALU2(AND)
+ ALU2(OR)
+ ALU2(XOR)
+ ALU2(SHR)
+ ALU2(SHL)
+ ALU2(RSR)
+ ALU2(RSL)
+ ALU2(ASR)
+ ALU2(ADD)
+ ALU2(MUL)
+ ALU1(FRC)
+ ALU1(RNDD)
+ ALU1(RNDZ)
+ ALU2(MAC)
+ ALU2(MACH)
+ ALU1(LZD)
+ ALU2(DP4)
+ ALU2(DPH)
+ ALU2(DP3)
+ ALU2(DP2)
+ALU2(LINE)
+
+
+
+
+void brw_NOP(struct brw_compile *p)
+{
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_NOP);
+ brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+ brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+ brw_set_src1(insn, brw_imm_ud(0x0));
+}
+
+
+
+
+
+/***********************************************************************
+ * Comparisons, if/else/endif
+ */
+
+struct brw_instruction *brw_JMPI(struct brw_compile *p,
+ struct brw_reg dest,
+ struct brw_reg src0,
+ struct brw_reg src1)
+{
+ struct brw_instruction *insn = brw_alu2(p, BRW_OPCODE_JMPI, dest, src0, src1);
+
+ p->current->header.predicate_control = BRW_PREDICATE_NONE;
+
+ return insn;
+}
+
+/* EU takes the value from the flag register and pushes it onto some
+ * sort of a stack (presumably merging with any flag value already on
+ * the stack). Within an if block, the flags at the top of the stack
+ * control execution on each channel of the unit, eg. on each of the
+ * 16 pixel values in our wm programs.
+ *
+ * When the matching 'else' instruction is reached (presumably by
+ * countdown of the instruction count patched in by our ELSE/ENDIF
+ * functions), the relevant flags are inverted.
+ *
+ * When the matching 'endif' instruction is reached, the flags are
+ * popped off. If the stack is now empty, normal execution resumes.
+ *
+ * No attempt is made to deal with stack overflow (14 elements?).
+ */
+struct brw_instruction *brw_IF(struct brw_compile *p, uint32_t execute_size)
+{
+ struct brw_instruction *insn;
+
+ if (p->single_program_flow) {
+ assert(execute_size == BRW_EXECUTE_1);
+
+ insn = brw_next_instruction(p, BRW_OPCODE_ADD);
+ insn->header.predicate_inverse = 1;
+ } else {
+ insn = brw_next_instruction(p, BRW_OPCODE_IF);
+ }
+
+ /* Override the defaults for this instruction:
+ */
+ brw_instruction_set_destination (insn, brw_ip_reg ());
+ brw_instruction_set_source0 (insn, brw_ip_reg ());
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->header.execution_size = execute_size;
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.predicate_control = BRW_PREDICATE_NORMAL;
+ insn->header.mask_control = BRW_MASK_ENABLE;
+ if (!p->single_program_flow)
+ insn->header.thread_control = BRW_THREAD_SWITCH;
+
+ p->current->header.predicate_control = BRW_PREDICATE_NONE;
+
+ return insn;
+}
+
+
+struct brw_instruction *brw_ELSE(struct brw_compile *p,
+ struct brw_instruction *if_insn)
+{
+ struct brw_instruction *insn;
+
+ if (p->single_program_flow) {
+ insn = brw_next_instruction(p, BRW_OPCODE_ADD);
+ } else {
+ insn = brw_next_instruction(p, BRW_OPCODE_ELSE);
+ }
+
+ brw_instruction_set_destination (insn, brw_ip_reg ());
+ brw_instruction_set_source0 (insn, brw_ip_reg ());
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.execution_size = if_insn->header.execution_size;
+ insn->header.mask_control = BRW_MASK_ENABLE;
+ if (!p->single_program_flow)
+ insn->header.thread_control = BRW_THREAD_SWITCH;
+
+ /* Patch the if instruction to point at this instruction.
+ */
+ if (p->single_program_flow) {
+ assert(if_insn->header.opcode == BRW_OPCODE_ADD);
+
+ if_insn->bits3.ud = (insn - if_insn + 1) * 16;
+ } else {
+ assert(if_insn->header.opcode == BRW_OPCODE_IF);
+
+ if_insn->bits3.if_else.jump_count = insn - if_insn;
+ if_insn->bits3.if_else.pop_count = 1;
+ if_insn->bits3.if_else.pad0 = 0;
+ }
+
+ return insn;
+}
+
+void brw_ENDIF(struct brw_compile *p,
+ struct brw_instruction *patch_insn)
+{
+ if (p->single_program_flow) {
+ /* In single program flow mode, there's no need to execute an ENDIF,
+ * since we don't need to do any stack operations, and if we're executing
+ * currently, we want to just continue executing.
+ */
+ struct brw_instruction *next = &p->store[p->nr_insn];
+
+ assert(patch_insn->header.opcode == BRW_OPCODE_ADD);
+
+ patch_insn->bits3.ud = (next - patch_insn) * 16;
+ } else {
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_ENDIF);
+
+ brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+ brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.execution_size = patch_insn->header.execution_size;
+ insn->header.mask_control = BRW_MASK_ENABLE;
+ insn->header.thread_control = BRW_THREAD_SWITCH;
+
+ assert(patch_insn->bits3.if_else.jump_count == 0);
+
+ /* Patch the if or else instructions to point at this or the next
+ * instruction respectively.
+ */
+ if (patch_insn->header.opcode == BRW_OPCODE_IF) {
+ /* Automagically turn it into an IFF:
+ */
+ patch_insn->header.opcode = BRW_OPCODE_IFF;
+ patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1;
+ patch_insn->bits3.if_else.pop_count = 0;
+ patch_insn->bits3.if_else.pad0 = 0;
+ } else if (patch_insn->header.opcode == BRW_OPCODE_ELSE) {
+ patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1;
+ patch_insn->bits3.if_else.pop_count = 1;
+ patch_insn->bits3.if_else.pad0 = 0;
+ } else {
+ assert(0);
+ }
+
+ /* Also pop item off the stack in the endif instruction:
+ */
+ insn->bits3.if_else.jump_count = 0;
+ insn->bits3.if_else.pop_count = 1;
+ insn->bits3.if_else.pad0 = 0;
+ }
+}
+
+struct brw_instruction *brw_BREAK(struct brw_compile *p)
+{
+ struct brw_instruction *insn;
+ insn = brw_next_instruction(p, BRW_OPCODE_BREAK);
+ brw_instruction_set_destination(insn, brw_ip_reg());
+ brw_instruction_set_source0(insn, brw_ip_reg());
+ brw_set_src1(insn, brw_imm_d (0));
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.execution_size = BRW_EXECUTE_8;
+ /* insn->header.mask_control = BRW_MASK_DISABLE; */
+ insn->bits3.if_else.pad0 = 0;
+ return insn;
+}
+
+struct brw_instruction *brw_CONT(struct brw_compile *p)
+{
+ struct brw_instruction *insn;
+ insn = brw_next_instruction(p, BRW_OPCODE_CONTINUE);
+ brw_instruction_set_destination(insn, brw_ip_reg());
+ brw_instruction_set_source0(insn, brw_ip_reg());
+ brw_set_src1 (insn, brw_imm_d (0));
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.execution_size = BRW_EXECUTE_8;
+ /* insn->header.mask_control = BRW_MASK_DISABLE; */
+ insn->bits3.if_else.pad0 = 0;
+ return insn;
+}
+
+/* DO/WHILE loop:
+*/
+struct brw_instruction *brw_DO(struct brw_compile *p, uint32_t execute_size)
+{
+ if (p->single_program_flow) {
+ return &p->store[p->nr_insn];
+ } else {
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_DO);
+
+ /* Override the defaults for this instruction:
+ */
+ brw_instruction_set_destination(insn, brw_null_reg());
+ brw_instruction_set_source0(insn, brw_null_reg());
+ brw_set_src1(insn, brw_null_reg());
+
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.execution_size = execute_size;
+ insn->header.predicate_control = BRW_PREDICATE_NONE;
+ /* insn->header.mask_control = BRW_MASK_ENABLE; */
+ /* insn->header.mask_control = BRW_MASK_DISABLE; */
+
+ return insn;
+ }
+}
+
+
+
+struct brw_instruction *brw_WHILE(struct brw_compile *p,
+ struct brw_instruction *do_insn)
+{
+ struct brw_instruction *insn;
+
+ if (p->single_program_flow)
+ insn = brw_next_instruction(p, BRW_OPCODE_ADD);
+ else
+ insn = brw_next_instruction(p, BRW_OPCODE_WHILE);
+
+ brw_instruction_set_destination(insn, brw_ip_reg());
+ brw_instruction_set_source0(insn, brw_ip_reg());
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+
+ if (p->single_program_flow) {
+ insn->header.execution_size = BRW_EXECUTE_1;
+
+ insn->bits3.d = (do_insn - insn) * 16;
+ } else {
+ insn->header.execution_size = do_insn->header.execution_size;
+
+ assert(do_insn->header.opcode == BRW_OPCODE_DO);
+ insn->bits3.if_else.jump_count = do_insn - insn + 1;
+ insn->bits3.if_else.pop_count = 0;
+ insn->bits3.if_else.pad0 = 0;
+ }
+
+ /* insn->header.mask_control = BRW_MASK_ENABLE; */
+
+ /* insn->header.mask_control = BRW_MASK_DISABLE; */
+ p->current->header.predicate_control = BRW_PREDICATE_NONE;
+ return insn;
+}
+
+
+/* FORWARD JUMPS:
+*/
+void brw_land_fwd_jump(struct brw_compile *p,
+ struct brw_instruction *jmp_insn)
+{
+ struct brw_instruction *landing = &p->store[p->nr_insn];
+
+ assert(jmp_insn->header.opcode == BRW_OPCODE_JMPI);
+ assert(jmp_insn->bits1.da1.src1_reg_file = BRW_IMMEDIATE_VALUE);
+
+ jmp_insn->bits3.ud = (landing - jmp_insn) - 1;
+}
+
+
+
+/* To integrate with the above, it makes sense that the comparison
+ * instruction should populate the flag register. It might be simpler
+ * just to use the flag reg for most WM tasks?
+ */
+void brw_CMP(struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t conditional,
+ struct brw_reg src0,
+ struct brw_reg src1)
+{
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_CMP);
+
+ insn->header.destreg__conditonalmod = conditional;
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src0);
+ brw_set_src1(insn, src1);
+
+ /* guess_execution_size(insn, src0); */
+
+
+ /* Make it so that future instructions will use the computed flag
+ * value until brw_set_predicate_control_flag_value() is called
+ * again.
+ */
+ if (dest.file == BRW_ARCHITECTURE_REGISTER_FILE &&
+ dest.nr == 0) {
+ p->current->header.predicate_control = BRW_PREDICATE_NORMAL;
+ p->flag_value = 0xff;
+ }
+}
+
+
+
+/***********************************************************************
+ * Helpers for the various SEND message types:
+ */
+
+/* Invert 8 values
+*/
+void brw_math( struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t function,
+ uint32_t saturate,
+ uint32_t msg_reg_nr,
+ struct brw_reg src,
+ uint32_t data_type,
+ uint32_t precision )
+{
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+ uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1;
+ uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1;
+
+ /* Example code doesn't set predicate_control for send
+ * instructions.
+ */
+ insn->header.predicate_control = 0;
+ insn->header.destreg__conditonalmod = msg_reg_nr;
+
+ response_length = 1;
+
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src);
+ brw_set_math_message(insn,
+ msg_length, response_length,
+ function,
+ BRW_MATH_INTEGER_UNSIGNED,
+ precision,
+ saturate,
+ data_type);
+}
+
+/* Use 2 send instructions to invert 16 elements
+*/
+void brw_math_16( struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t function,
+ uint32_t saturate,
+ uint32_t msg_reg_nr,
+ struct brw_reg src,
+ uint32_t precision )
+{
+ struct brw_instruction *insn;
+ uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1;
+ uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1;
+
+ /* First instruction:
+ */
+ brw_push_insn_state(p);
+ brw_set_predicate_control_flag_value(p, 0xff);
+ brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+
+ insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+ insn->header.destreg__conditonalmod = msg_reg_nr;
+
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src);
+ brw_set_math_message(insn,
+ msg_length, response_length,
+ function,
+ BRW_MATH_INTEGER_UNSIGNED,
+ precision,
+ saturate,
+ BRW_MATH_DATA_VECTOR);
+
+ /* Second instruction:
+ */
+ insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+ insn->header.compression_control = BRW_COMPRESSION_2NDHALF;
+ insn->header.destreg__conditonalmod = msg_reg_nr+1;
+
+ brw_instruction_set_destination(insn, offset(dest,1));
+ brw_instruction_set_source0(insn, src);
+ brw_set_math_message(insn,
+ msg_length, response_length,
+ function,
+ BRW_MATH_INTEGER_UNSIGNED,
+ precision,
+ saturate,
+ BRW_MATH_DATA_VECTOR);
+
+ brw_pop_insn_state(p);
+}
+
+
+
+
+void brw_dp_WRITE_16( struct brw_compile *p,
+ struct brw_reg src,
+ uint32_t msg_reg_nr,
+ uint32_t scratch_offset )
+{
+ {
+ brw_push_insn_state(p);
+ brw_set_mask_control(p, BRW_MASK_DISABLE);
+ brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+
+ brw_MOV (p,
+ retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D),
+ brw_imm_d (scratch_offset));
+
+ brw_pop_insn_state(p);
+ }
+
+ {
+ uint32_t msg_length = 3;
+ struct brw_reg dest = retype(brw_null_reg(), BRW_REGISTER_TYPE_UW);
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+ insn->header.predicate_control = 0; /* XXX */
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.destreg__conditonalmod = msg_reg_nr;
+
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src);
+
+ brw_instruction_set_dp_write_message(insn,
+ 255, /* bti */
+ BRW_DATAPORT_OWORD_BLOCK_4_OWORDS, /* msg_control */
+ BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE, /* msg_type */
+ msg_length,
+ 0, /* pixel scoreboard */
+ 0, /* response_length */
+ 0); /* eot */
+ }
+
+}
+
+
+void brw_dp_READ_16( struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ uint32_t scratch_offset )
+{
+ {
+ brw_push_insn_state(p);
+ brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+ brw_set_mask_control(p, BRW_MASK_DISABLE);
+
+ brw_MOV (p,
+ retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D),
+ brw_imm_d (scratch_offset));
+
+ brw_pop_insn_state(p);
+ }
+
+ {
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+ insn->header.predicate_control = 0; /* XXX */
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.destreg__conditonalmod = msg_reg_nr;
+
+ brw_instruction_set_destination(insn, dest); /* UW? */
+ brw_instruction_set_source0(insn, retype(brw_vec8_grf(0), BRW_REGISTER_TYPE_UW));
+
+ brw_set_dp_read_message(insn,
+ 255, /* bti */
+ 3, /* msg_control */
+ BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ, /* msg_type */
+ 1, /* target cache */
+ 1, /* msg_length */
+ 2, /* response_length */
+ 0); /* eot */
+ }
+}
+
+
+void brw_fb_WRITE(struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ struct brw_reg src0,
+ uint32_t binding_table_index,
+ uint32_t msg_length,
+ uint32_t response_length,
+ int eot)
+{
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+ insn->header.predicate_control = 0; /* XXX */
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.destreg__conditonalmod = msg_reg_nr;
+
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src0);
+ brw_instruction_set_dp_write_message(insn,
+ binding_table_index,
+ BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE, /* msg_control */
+ BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */
+ msg_length,
+ 1, /* pixel scoreboard */
+ response_length,
+ eot);
+}
+
+
+
+void brw_SAMPLE (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ struct brw_reg src0,
+ uint32_t binding_table_index,
+ uint32_t sampler,
+ uint32_t writemask,
+ uint32_t msg_type,
+ uint32_t response_length,
+ uint32_t msg_length,
+ cairo_bool_t eot)
+{
+ int need_stall = 0;
+
+ if(writemask == 0) {
+ /* printf("%s: zero writemask??\n", __FUNCTION__); */
+ return;
+ }
+
+ /* Hardware doesn't do destination dependency checking on send
+ * instructions properly. Add a workaround which generates the
+ * dependency by other means. In practice it seems like this bug
+ * only crops up for texture samples, and only where registers are
+ * written by the send and then written again later without being
+ * read in between. Luckily for us, we already track that
+ * information and use it to modify the writemask for the
+ * instruction, so that is a guide for whether a workaround is
+ * needed.
+ */
+ if (writemask != WRITEMASK_XYZW) {
+ uint32_t dst_offset = 0;
+ uint32_t i, newmask = 0, len = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (writemask & (1<<i))
+ break;
+ dst_offset += 2;
+ }
+ for (; i < 4; i++) {
+ if (!(writemask & (1<<i)))
+ break;
+ newmask |= 1<<i;
+ len++;
+ }
+
+ if (newmask != writemask) {
+ need_stall = 1;
+ /* printf("need stall %x %x\n", newmask , writemask); */
+ }
+ else {
+ struct brw_reg m1 = brw_message_reg(msg_reg_nr);
+
+ newmask = ~newmask & WRITEMASK_XYZW;
+
+ brw_push_insn_state(p);
+
+ brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+ brw_set_mask_control(p, BRW_MASK_DISABLE);
+
+ brw_MOV(p, m1, brw_vec8_grf(0));
+ brw_MOV(p, get_element_ud(m1, 2), brw_imm_ud(newmask << 12));
+
+ brw_pop_insn_state(p);
+
+ src0 = retype(brw_null_reg(), BRW_REGISTER_TYPE_UW);
+ dest = offset(dest, dst_offset);
+ response_length = len * 2;
+ }
+ }
+
+ {
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+ insn->header.predicate_control = 0; /* XXX */
+ insn->header.compression_control = BRW_COMPRESSION_NONE;
+ insn->header.destreg__conditonalmod = msg_reg_nr;
+
+ brw_instruction_set_destination(insn, dest);
+ brw_instruction_set_source0(insn, src0);
+ brw_set_sampler_message (insn, p->is_g4x,
+ binding_table_index,
+ sampler,
+ msg_type,
+ response_length,
+ msg_length,
+ eot);
+ }
+
+ if (need_stall)
+ {
+ struct brw_reg reg = vec8(offset(dest, response_length-1));
+
+ /* mov (8) r9.0<1>:f r9.0<8;8,1>:f { Align1 }
+ */
+ brw_push_insn_state(p);
+ brw_set_compression_control(p, 0);
+ brw_MOV(p, reg, reg);
+ brw_pop_insn_state(p);
+ }
+}
+
+/* All these variables are pretty confusing - we might be better off
+ * using bitmasks and macros for this, in the old style. Or perhaps
+ * just having the caller instantiate the fields in dword3 itself.
+ */
+void brw_urb_WRITE(struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ struct brw_reg src0,
+ int allocate,
+ int used,
+ uint32_t msg_length,
+ uint32_t response_length,
+ int eot,
+ int writes_complete,
+ uint32_t offset,
+ uint32_t swizzle)
+{
+ struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+ assert(msg_length < 16);
+
+ brw_instruction_set_destination (insn, dest);
+ brw_instruction_set_source0 (insn, src0);
+ brw_set_src1 (insn, brw_imm_d (0));
+
+ insn->header.destreg__conditonalmod = msg_reg_nr;
+
+ brw_set_urb_message (insn,
+ allocate,
+ used,
+ msg_length,
+ response_length,
+ eot,
+ writes_complete,
+ offset,
+ swizzle);
+}
diff --git a/src/drm/cairo-drm-intel-brw-eu-util.c b/src/drm/cairo-drm-intel-brw-eu-util.c
new file mode 100755
index 000000000..592235b12
--- /dev/null
+++ b/src/drm/cairo-drm-intel-brw-eu-util.c
@@ -0,0 +1,121 @@
+/*
+ Copyright (C) Intel Corp. 2006. All Rights Reserved.
+ Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+ develop this 3D driver.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial
+ portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ * Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+
+#include "cairoint.h"
+#include "cairo-drm-intel-brw-eu.h"
+
+
+void brw_math_invert( struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_reg src)
+{
+ brw_math( p,
+ dst,
+ BRW_MATH_FUNCTION_INV,
+ BRW_MATH_SATURATE_NONE,
+ 0,
+ src,
+ BRW_MATH_PRECISION_FULL,
+ BRW_MATH_DATA_VECTOR );
+}
+
+
+
+void brw_copy4(struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_reg src,
+ uint32_t count)
+{
+ uint32_t i;
+
+ dst = vec4(dst);
+ src = vec4(src);
+
+ for (i = 0; i < count; i++)
+ {
+ uint32_t delta = i*32;
+ brw_MOV(p, byte_offset(dst, delta), byte_offset(src, delta));
+ brw_MOV(p, byte_offset(dst, delta+16), byte_offset(src, delta+16));
+ }
+}
+
+
+void brw_copy8(struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_reg src,
+ uint32_t count)
+{
+ uint32_t i;
+
+ dst = vec8(dst);
+ src = vec8(src);
+
+ for (i = 0; i < count; i++)
+ {
+ uint32_t delta = i*32;
+ brw_MOV(p, byte_offset(dst, delta), byte_offset(src, delta));
+ }
+}
+
+
+void brw_copy_indirect_to_indirect(struct brw_compile *p,
+ struct brw_indirect dst_ptr,
+ struct brw_indirect src_ptr,
+ uint32_t count)
+{
+ uint32_t i;
+
+ for (i = 0; i < count; i++)
+ {
+ uint32_t delta = i*32;
+ brw_MOV(p, deref_4f(dst_ptr, delta), deref_4f(src_ptr, delta));
+ brw_MOV(p, deref_4f(dst_ptr, delta+16), deref_4f(src_ptr, delta+16));
+ }
+}
+
+
+void brw_copy_from_indirect(struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_indirect ptr,
+ uint32_t count)
+{
+ uint32_t i;
+
+ dst = vec4(dst);
+
+ for (i = 0; i < count; i++)
+ {
+ uint32_t delta = i*32;
+ brw_MOV(p, byte_offset(dst, delta), deref_4f(ptr, delta));
+ brw_MOV(p, byte_offset(dst, delta+16), deref_4f(ptr, delta+16));
+ }
+}
diff --git a/src/drm/cairo-drm-intel-brw-eu.c b/src/drm/cairo-drm-intel-brw-eu.c
new file mode 100755
index 000000000..2b47d8c37
--- /dev/null
+++ b/src/drm/cairo-drm-intel-brw-eu.c
@@ -0,0 +1,250 @@
+/*
+ Copyright (C) Intel Corp. 2006. All Rights Reserved.
+ Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+ develop this 3D driver.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial
+ portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ * Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-drm-intel-brw-eu.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* How does predicate control work when execution_size != 8? Do I
+ * need to test/set for 0xffff when execution_size is 16?
+ */
+void brw_set_predicate_control_flag_value( struct brw_compile *p, uint32_t value )
+{
+ p->current->header.predicate_control = BRW_PREDICATE_NONE;
+
+ if (value != 0xff) {
+ if (value != p->flag_value) {
+ brw_push_insn_state(p);
+ brw_MOV(p, brw_flag_reg(), brw_imm_uw(value));
+ p->flag_value = value;
+ brw_pop_insn_state(p);
+ }
+
+ p->current->header.predicate_control = BRW_PREDICATE_NORMAL;
+ }
+}
+
+void brw_set_predicate_control( struct brw_compile *p, uint32_t pc )
+{
+ p->current->header.predicate_control = pc;
+}
+
+void brw_set_conditionalmod( struct brw_compile *p, uint32_t conditional )
+{
+ p->current->header.destreg__conditonalmod = conditional;
+}
+
+void brw_set_access_mode( struct brw_compile *p, uint32_t access_mode )
+{
+ p->current->header.access_mode = access_mode;
+}
+
+void brw_set_compression_control( struct brw_compile *p, int compression_control )
+{
+ p->current->header.compression_control = compression_control;
+}
+
+void brw_set_mask_control( struct brw_compile *p, uint32_t value )
+{
+ p->current->header.mask_control = value;
+}
+
+void brw_set_saturate( struct brw_compile *p, uint32_t value )
+{
+ p->current->header.saturate = value;
+}
+
+void brw_push_insn_state( struct brw_compile *p )
+{
+ assert(p->current != &p->stack[BRW_EU_MAX_INSN_STACK-1]);
+ memcpy(p->current+1, p->current, sizeof(struct brw_instruction));
+ p->current++;
+}
+
+void brw_pop_insn_state( struct brw_compile *p )
+{
+ assert(p->current != p->stack);
+ p->current--;
+}
+
+/************************************************************************/
+void
+brw_compile_init (struct brw_compile *p,
+ cairo_bool_t is_g4x)
+{
+ p->nr_insn = 0;
+ p->current = p->stack;
+ memset (p->current, 0, sizeof (p->current[0]));
+
+ p->is_g4x = is_g4x;
+
+ /* Some defaults? */
+ brw_set_mask_control (p, BRW_MASK_ENABLE); /* what does this do? */
+ brw_set_saturate (p, 0);
+ brw_set_compression_control (p, BRW_COMPRESSION_NONE);
+ brw_set_predicate_control_flag_value (p, 0xff);
+}
+
+const uint32_t *
+brw_get_program (struct brw_compile *p,
+ uint32_t *sz)
+{
+ *sz = p->nr_insn * sizeof (struct brw_instruction);
+ return (const uint32_t *)p->store;
+}
+
+
+
+/*
+ * Subroutine calls require special attention.
+ * Mesa instructions may be expanded into multiple hardware instructions
+ * so the prog_instruction::BranchTarget field can't be used as an index
+ * into the hardware instructions.
+ *
+ * The BranchTarget field isn't needed, however. Mesa's GLSL compiler
+ * emits CAL and BGNSUB instructions with labels that can be used to map
+ * subroutine calls to actual subroutine code blocks.
+ *
+ * The structures and function here implement patching of CAL instructions
+ * so they jump to the right subroutine code...
+ */
+
+
+/*
+ * For each OPCODE_BGNSUB we create one of these.
+ */
+struct brw_glsl_label
+{
+ const char *name; /*< the label string */
+ uint32_t position; /*< the position of the brw instruction for this label */
+ struct brw_glsl_label *next; /*< next in linked list */
+};
+
+
+/*
+ * For each OPCODE_CAL we create one of these.
+ */
+struct brw_glsl_call
+{
+ uint32_t call_inst_pos; /*< location of the CAL instruction */
+ const char *sub_name; /*< name of subroutine to call */
+ struct brw_glsl_call *next; /*< next in linked list */
+};
+
+
+/*
+ * Called for each OPCODE_BGNSUB.
+ */
+ void
+brw_save_label(struct brw_compile *c, const char *name, uint32_t position)
+{
+ struct brw_glsl_label *label = calloc(1, sizeof *label);
+ label->name = name;
+ label->position = position;
+ label->next = c->first_label;
+ c->first_label = label;
+}
+
+
+/*
+ * Called for each OPCODE_CAL.
+ */
+ void
+brw_save_call(struct brw_compile *c, const char *name, uint32_t call_pos)
+{
+ struct brw_glsl_call *call = calloc(1, sizeof *call);
+ call->call_inst_pos = call_pos;
+ call->sub_name = name;
+ call->next = c->first_call;
+ c->first_call = call;
+}
+
+
+/*
+ * Lookup a label, return label's position/offset.
+ */
+ static uint32_t
+brw_lookup_label(struct brw_compile *c, const char *name)
+{
+ const struct brw_glsl_label *label;
+ for (label = c->first_label; label; label = label->next) {
+ if (strcmp(name, label->name) == 0) {
+ return label->position;
+ }
+ }
+ abort(); /* should never happen */
+ return ~0;
+}
+
+
+/*
+ * When we're done generating code, this function is called to resolve
+ * subroutine calls.
+ */
+ void
+brw_resolve_cals(struct brw_compile *c)
+{
+ const struct brw_glsl_call *call;
+
+ for (call = c->first_call; call; call = call->next) {
+ const uint32_t sub_loc = brw_lookup_label(c, call->sub_name);
+ struct brw_instruction *brw_call_inst = &c->store[call->call_inst_pos];
+ struct brw_instruction *brw_sub_inst = &c->store[sub_loc];
+ int32_t offset = brw_sub_inst - brw_call_inst;
+
+ /* patch brw_inst1 to point to brw_inst2 */
+ brw_set_src1(brw_call_inst, brw_imm_d(offset * 16));
+ }
+
+ /* free linked list of calls */
+ {
+ struct brw_glsl_call *call, *next;
+ for (call = c->first_call; call; call = next) {
+ next = call->next;
+ free(call);
+ }
+ c->first_call = NULL;
+ }
+
+ /* free linked list of labels */
+ {
+ struct brw_glsl_label *label, *next;
+ for (label = c->first_label; label; label = next) {
+ next = label->next;
+ free(label);
+ }
+ c->first_label = NULL;
+ }
+}
diff --git a/src/drm/cairo-drm-intel-brw-eu.h b/src/drm/cairo-drm-intel-brw-eu.h
new file mode 100755
index 000000000..2662a2e7f
--- /dev/null
+++ b/src/drm/cairo-drm-intel-brw-eu.h
@@ -0,0 +1,1043 @@
+/*
+ Copyright (C) Intel Corp. 2006. All Rights Reserved.
+ Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+ develop this 3D driver.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial
+ portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ * Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#ifndef CAIRO_DRM_INTEL_BRW_EU_H
+#define CAIRO_DRM_INTEL_BRW_EU_H
+
+#include "cairo-drm-intel-brw-structs.h"
+#include "cairo-drm-intel-brw-defines.h"
+
+#include <assert.h>
+
+
+/*
+ * Writemask values, 1 bit per component.
+ */
+#define WRITEMASK_X 0x1
+#define WRITEMASK_Y 0x2
+#define WRITEMASK_Z 0x4
+#define WRITEMASK_W 0x8
+#define WRITEMASK_XY (WRITEMASK_X | WRITEMASK_Y)
+#define WRITEMASK_XZ (WRITEMASK_X | WRITEMASK_Z)
+#define WRITEMASK_YZ (WRITEMASK_Y | WRITEMASK_Z)
+#define WRITEMASK_XYZ (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z)
+#define WRITEMASK_XW (WRITEMASK_X | WRITEMASK_W)
+#define WRITEMASK_YW (WRITEMASK_Y | WRITEMASK_W)
+#define WRITEMASK_XYW (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_W)
+#define WRITEMASK_ZW (WRITEMASK_Z | WRITEMASK_W)
+#define WRITEMASK_XZW (WRITEMASK_X | WRITEMASK_Z | WRITEMASK_W)
+#define WRITEMASK_YZW (WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W)
+#define WRITEMASK_XYZW (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W)
+
+#define BRW_SWIZZLE4(a,b,c,d) (((a)<<0) | ((b)<<2) | ((c)<<4) | ((d)<<6))
+#define BRW_GET_SWZ(swz, idx) (((swz) >> ((idx)*2)) & 0x3)
+
+#define BRW_SWIZZLE_NOOP BRW_SWIZZLE4 (0,1,2,3)
+#define BRW_SWIZZLE_XYZW BRW_SWIZZLE4 (0,1,2,3)
+#define BRW_SWIZZLE_XXXX BRW_SWIZZLE4 (0,0,0,0)
+#define BRW_SWIZZLE_XYXY BRW_SWIZZLE4 (0,1,0,1)
+
+#define REG_SIZE (8*4)
+
+/* These aren't hardware structs, just something useful for us to pass around:
+ *
+ * Align1 operation has a lot of control over input ranges. Used in
+ * WM programs to implement shaders decomposed into "channel serial"
+ * or "structure of array" form:
+ */
+struct brw_reg {
+ uint32_t type:4;
+ uint32_t file:2;
+ uint32_t nr:8;
+ uint32_t subnr:5; /* :1 in align16 */
+ uint32_t negate:1; /* source only */
+ uint32_t abs:1; /* source only */
+ uint32_t vstride:4; /* source only */
+ uint32_t width:3; /* src only, align1 only */
+ uint32_t hstride:2; /* align1 only */
+ uint32_t address_mode:1; /* relative addressing, hopefully! */
+ uint32_t pad0:1;
+
+ union {
+ struct {
+ uint32_t swizzle:8; /* src only, align16 only */
+ uint32_t writemask:4; /* dest only, align16 only */
+ int32_t indirect_offset:10; /* relative addressing offset */
+ uint32_t pad1:10; /* two dwords total */
+ } bits;
+
+ float f;
+ int32_t d;
+ uint32_t ud;
+ } dw1;
+};
+
+struct brw_indirect {
+ uint32_t addr_subnr:4;
+ int32_t addr_offset:10;
+ uint32_t pad:18;
+};
+
+struct brw_glsl_label;
+struct brw_glsl_call;
+
+#define BRW_EU_MAX_INSN_STACK 5
+#define BRW_EU_MAX_INSN 200
+
+struct brw_compile {
+ struct brw_instruction store[BRW_EU_MAX_INSN];
+ uint32_t nr_insn;
+
+ cairo_bool_t is_g4x;
+
+ /* Allow clients to push/pop instruction state:
+ */
+ struct brw_instruction stack[BRW_EU_MAX_INSN_STACK];
+ struct brw_instruction *current;
+
+ uint32_t flag_value;
+ int single_program_flow;
+ struct brw_context *brw;
+
+ struct brw_glsl_label *first_label; /*< linked list of labels */
+ struct brw_glsl_call *first_call; /*< linked list of CALs */
+};
+
+cairo_private void
+brw_save_label (struct brw_compile *c,
+ const char *name,
+ uint32_t position);
+
+cairo_private void
+brw_save_call (struct brw_compile *c,
+ const char *name,
+ uint32_t call_pos);
+
+cairo_private void
+brw_resolve_cals (struct brw_compile *c);
+
+static cairo_always_inline int
+type_sz (uint32_t type)
+{
+ switch (type) {
+ case BRW_REGISTER_TYPE_UD:
+ case BRW_REGISTER_TYPE_D:
+ case BRW_REGISTER_TYPE_F:
+ return 4;
+ case BRW_REGISTER_TYPE_HF:
+ case BRW_REGISTER_TYPE_UW:
+ case BRW_REGISTER_TYPE_W:
+ return 2;
+ case BRW_REGISTER_TYPE_UB:
+ case BRW_REGISTER_TYPE_B:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Construct a brw_reg.
+ * \param file one of the BRW_x_REGISTER_FILE values
+ * \param nr register number/index
+ * \param subnr register sub number
+ * \param type one of BRW_REGISTER_TYPE_x
+ * \param vstride one of BRW_VERTICAL_STRIDE_x
+ * \param width one of BRW_WIDTH_x
+ * \param hstride one of BRW_HORIZONTAL_STRIDE_x
+ * \param swizzle one of BRW_SWIZZLE_x
+ * \param writemask WRITEMASK_X/Y/Z/W bitfield
+ */
+static cairo_always_inline struct brw_reg
+brw_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr,
+ uint32_t type,
+ uint32_t vstride,
+ uint32_t width,
+ uint32_t hstride,
+ uint32_t swizzle,
+ uint32_t writemask)
+{
+ struct brw_reg reg;
+
+ if (type == BRW_GENERAL_REGISTER_FILE)
+ assert(nr < 128);
+ else if (type == BRW_MESSAGE_REGISTER_FILE)
+ assert(nr < 9);
+ else if (type == BRW_ARCHITECTURE_REGISTER_FILE)
+ assert(nr <= BRW_ARF_IP);
+
+ reg.type = type;
+ reg.file = file;
+ reg.nr = nr;
+ reg.subnr = subnr * type_sz(type);
+ reg.negate = 0;
+ reg.abs = 0;
+ reg.vstride = vstride;
+ reg.width = width;
+ reg.hstride = hstride;
+ reg.address_mode = BRW_ADDRESS_DIRECT;
+ reg.pad0 = 0;
+
+ /* Could do better: If the reg is r5.3<0;1,0>, we probably want to
+ * set swizzle and writemask to W, as the lower bits of subnr will
+ * be lost when converted to align16. This is probably too much to
+ * keep track of as you'd want it adjusted by suboffset(), etc.
+ * Perhaps fix up when converting to align16?
+ */
+ reg.dw1.bits.swizzle = swizzle;
+ reg.dw1.bits.writemask = writemask;
+ reg.dw1.bits.indirect_offset = 0;
+ reg.dw1.bits.pad1 = 0;
+
+ return reg;
+}
+
+/* Construct float[16] register */
+static cairo_always_inline struct brw_reg
+brw_vec16_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return brw_reg (file, nr, subnr,
+ BRW_REGISTER_TYPE_F,
+ BRW_VERTICAL_STRIDE_16,
+ BRW_WIDTH_16,
+ BRW_HORIZONTAL_STRIDE_1,
+ BRW_SWIZZLE_XYZW,
+ WRITEMASK_XYZW);
+}
+
+/* Construct float[8] register */
+static cairo_always_inline struct brw_reg
+brw_vec8_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return brw_reg (file, nr, subnr,
+ BRW_REGISTER_TYPE_F,
+ BRW_VERTICAL_STRIDE_8,
+ BRW_WIDTH_8,
+ BRW_HORIZONTAL_STRIDE_1,
+ BRW_SWIZZLE_XYZW,
+ WRITEMASK_XYZW);
+}
+
+/* Construct float[4] register */
+static cairo_always_inline struct brw_reg
+brw_vec4_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return brw_reg (file, nr, subnr,
+ BRW_REGISTER_TYPE_F,
+ BRW_VERTICAL_STRIDE_4,
+ BRW_WIDTH_4,
+ BRW_HORIZONTAL_STRIDE_1,
+ BRW_SWIZZLE_XYZW,
+ WRITEMASK_XYZW);
+}
+
+/* Construct float[2] register */
+static cairo_always_inline struct brw_reg
+brw_vec2_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return brw_reg (file, nr, subnr,
+ BRW_REGISTER_TYPE_F,
+ BRW_VERTICAL_STRIDE_2,
+ BRW_WIDTH_2,
+ BRW_HORIZONTAL_STRIDE_1,
+ BRW_SWIZZLE_XYXY,
+ WRITEMASK_XY);
+}
+
+/* Construct float[1] register */
+static cairo_always_inline struct brw_reg
+brw_vec1_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return brw_reg (file, nr, subnr,
+ BRW_REGISTER_TYPE_F,
+ BRW_VERTICAL_STRIDE_0,
+ BRW_WIDTH_1,
+ BRW_HORIZONTAL_STRIDE_0,
+ BRW_SWIZZLE_XXXX,
+ WRITEMASK_X);
+}
+
+static cairo_always_inline struct brw_reg
+retype (struct brw_reg reg,
+ uint32_t type)
+{
+ reg.type = type;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+suboffset (struct brw_reg reg,
+ uint32_t delta)
+{
+ reg.subnr += delta * type_sz (reg.type);
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+offset (struct brw_reg reg,
+ uint32_t delta)
+{
+ reg.nr += delta;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+byte_offset (struct brw_reg reg,
+ uint32_t bytes)
+{
+ uint32_t newoffset = reg.nr * REG_SIZE + reg.subnr + bytes;
+ reg.nr = newoffset / REG_SIZE;
+ reg.subnr = newoffset % REG_SIZE;
+ return reg;
+}
+
+/* Construct unsigned word[16] register */
+static cairo_always_inline struct brw_reg
+brw_uw16_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return suboffset (retype (brw_vec16_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+/* Construct unsigned word[8] register */
+static cairo_always_inline struct brw_reg
+brw_uw8_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return suboffset (retype (brw_vec8_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+/* Construct unsigned word[2] register */
+static cairo_always_inline struct brw_reg
+brw_uw2_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return suboffset (retype (brw_vec2_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+/* Construct unsigned word[1] register */
+static cairo_always_inline struct brw_reg
+brw_uw1_reg (uint32_t file,
+ uint32_t nr,
+ uint32_t subnr)
+{
+ return suboffset (retype (brw_vec1_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+static cairo_always_inline struct brw_reg
+brw_imm_reg (uint32_t type)
+{
+ return brw_reg (BRW_IMMEDIATE_VALUE,
+ 0,
+ 0,
+ type,
+ BRW_VERTICAL_STRIDE_0,
+ BRW_WIDTH_1,
+ BRW_HORIZONTAL_STRIDE_0,
+ 0,
+ 0);
+}
+
+/* Construct float immediate register */
+static cairo_always_inline struct brw_reg brw_imm_f( float f )
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_F);
+ imm.dw1.f = f;
+ return imm;
+}
+
+/* Construct integer immediate register */
+static cairo_always_inline struct brw_reg brw_imm_d( int32_t d )
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_D);
+ imm.dw1.d = d;
+ return imm;
+}
+
+/* Construct uint immediate register */
+static cairo_always_inline struct brw_reg brw_imm_ud( uint32_t ud )
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UD);
+ imm.dw1.ud = ud;
+ return imm;
+}
+
+/* Construct ushort immediate register */
+static cairo_always_inline struct brw_reg brw_imm_uw( uint16_t uw )
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UW);
+ imm.dw1.ud = uw | (uw << 16);
+ return imm;
+}
+
+/* Construct short immediate register */
+static cairo_always_inline struct brw_reg brw_imm_w( int16_t w )
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_W);
+ imm.dw1.d = w | (w << 16);
+ return imm;
+}
+
+/* brw_imm_b and brw_imm_ub aren't supported by hardware - the type
+ * numbers alias with _V and _VF below:
+ */
+
+/* Construct vector of eight signed half-byte values */
+static cairo_always_inline
+struct brw_reg brw_imm_v (uint32_t v)
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_V);
+ imm.vstride = BRW_VERTICAL_STRIDE_0;
+ imm.width = BRW_WIDTH_8;
+ imm.hstride = BRW_HORIZONTAL_STRIDE_1;
+ imm.dw1.ud = v;
+ return imm;
+}
+
+/* Construct vector of four 8-bit float values */
+static cairo_always_inline struct brw_reg
+brw_imm_vf (uint32_t v)
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF);
+ imm.vstride = BRW_VERTICAL_STRIDE_0;
+ imm.width = BRW_WIDTH_4;
+ imm.hstride = BRW_HORIZONTAL_STRIDE_1;
+ imm.dw1.ud = v;
+ return imm;
+}
+
+#define VF_ZERO 0x0
+#define VF_ONE 0x30
+#define VF_NEG (1<<7)
+
+static cairo_always_inline struct brw_reg
+brw_imm_vf4 (uint32_t v0,
+ uint32_t v1,
+ uint32_t v2,
+ uint32_t v3)
+{
+ struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF);
+ imm.vstride = BRW_VERTICAL_STRIDE_0;
+ imm.width = BRW_WIDTH_4;
+ imm.hstride = BRW_HORIZONTAL_STRIDE_1;
+ imm.dw1.ud = ((v0 << 0) |
+ (v1 << 8) |
+ (v2 << 16) |
+ (v3 << 24));
+ return imm;
+}
+
+static cairo_always_inline struct brw_reg
+brw_address (struct brw_reg reg)
+{
+ return brw_imm_uw (reg.nr * REG_SIZE + reg.subnr);
+}
+
+/* Construct float[1] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec1_grf (uint32_t nr, uint32_t subnr)
+{
+ return brw_vec1_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/* Construct float[2] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec2_grf (uint32_t nr, uint32_t subnr)
+{
+ return brw_vec2_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/* Construct float[4] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec4_grf (uint32_t nr, uint32_t subnr)
+{
+ return brw_vec4_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/* Construct float[8] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec8_grf (uint32_t nr)
+{
+ return brw_vec8_reg (BRW_GENERAL_REGISTER_FILE, nr, 0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_uw8_grf (uint32_t nr, uint32_t subnr)
+{
+ return brw_uw8_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+static cairo_always_inline struct brw_reg
+brw_uw16_grf (uint32_t nr, uint32_t subnr)
+{
+ return brw_uw16_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/* Construct null register (usually used for setting condition codes) */
+static cairo_always_inline struct brw_reg
+brw_null_reg (void)
+{
+ return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+ BRW_ARF_NULL,
+ 0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_address_reg (uint32_t subnr)
+{
+ return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+ BRW_ARF_ADDRESS,
+ subnr);
+}
+
+/* If/else instructions break in align16 mode if writemask & swizzle
+ * aren't xyzw. This goes against the convention for other scalar
+ * regs:
+ */
+static cairo_always_inline struct brw_reg
+brw_ip_reg (void)
+{
+ return brw_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+ BRW_ARF_IP,
+ 0,
+ BRW_REGISTER_TYPE_UD,
+ BRW_VERTICAL_STRIDE_4, /* ? */
+ BRW_WIDTH_1,
+ BRW_HORIZONTAL_STRIDE_0,
+ BRW_SWIZZLE_XYZW,
+ WRITEMASK_XYZW);
+}
+
+static cairo_always_inline struct brw_reg
+brw_acc_reg (void)
+{
+ return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+ BRW_ARF_ACCUMULATOR,
+ 0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_flag_reg (void)
+{
+ return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+ BRW_ARF_FLAG,
+ 0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_mask_reg (uint32_t subnr)
+{
+ return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+ BRW_ARF_MASK,
+ subnr);
+}
+
+static cairo_always_inline struct brw_reg
+brw_message4_reg (uint32_t nr)
+{
+ return brw_vec4_reg (BRW_MESSAGE_REGISTER_FILE,
+ nr,
+ 0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_message_reg (uint32_t nr)
+{
+ return brw_vec8_reg (BRW_MESSAGE_REGISTER_FILE,
+ nr,
+ 0);
+}
+
+/* This is almost always called with a numeric constant argument, so
+ * make things easy to evaluate at compile time:
+ */
+static cairo_always_inline uint32_t
+cvt (uint32_t val)
+{
+ switch (val) {
+ case 0: return 0;
+ case 1: return 1;
+ case 2: return 2;
+ case 4: return 3;
+ case 8: return 4;
+ case 16: return 5;
+ case 32: return 6;
+ }
+ return 0;
+}
+
+static cairo_always_inline struct brw_reg
+stride (struct brw_reg reg,
+ uint32_t vstride,
+ uint32_t width,
+ uint32_t hstride)
+{
+ reg.vstride = cvt (vstride);
+ reg.width = cvt (width) - 1;
+ reg.hstride = cvt (hstride);
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+vec16 (struct brw_reg reg)
+{
+ return stride (reg, 16,16,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec8 (struct brw_reg reg)
+{
+ return stride (reg, 8,8,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec4 (struct brw_reg reg)
+{
+ return stride (reg, 4,4,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec2 (struct brw_reg reg)
+{
+ return stride (reg, 2,2,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec1 (struct brw_reg reg)
+{
+ return stride (reg, 0,1,0);
+}
+
+static cairo_always_inline struct brw_reg
+get_element (struct brw_reg reg, uint32_t elt)
+{
+ return vec1 (suboffset (reg, elt));
+}
+
+static cairo_always_inline struct brw_reg
+get_element_ud (struct brw_reg reg, uint32_t elt)
+{
+ return vec1 (suboffset (retype (reg, BRW_REGISTER_TYPE_UD), elt));
+}
+
+static cairo_always_inline struct brw_reg
+brw_swizzle (struct brw_reg reg,
+ uint32_t x,
+ uint32_t y,
+ uint32_t z,
+ uint32_t w)
+{
+ reg.dw1.bits.swizzle = BRW_SWIZZLE4 (BRW_GET_SWZ (reg.dw1.bits.swizzle, x),
+ BRW_GET_SWZ (reg.dw1.bits.swizzle, y),
+ BRW_GET_SWZ (reg.dw1.bits.swizzle, z),
+ BRW_GET_SWZ (reg.dw1.bits.swizzle, w));
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_swizzle1 (struct brw_reg reg,
+ uint32_t x)
+{
+ return brw_swizzle (reg, x, x, x, x);
+}
+
+static cairo_always_inline struct brw_reg
+brw_writemask (struct brw_reg reg,
+ uint32_t mask)
+{
+ reg.dw1.bits.writemask &= mask;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_set_writemask (struct brw_reg reg,
+ uint32_t mask)
+{
+ reg.dw1.bits.writemask = mask;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+negate (struct brw_reg reg)
+{
+ reg.negate ^= 1;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_abs (struct brw_reg reg)
+{
+ reg.abs = 1;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_vec4_indirect (uint32_t subnr,
+ int32_t offset)
+{
+ struct brw_reg reg = brw_vec4_grf (0, 0);
+ reg.subnr = subnr;
+ reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER;
+ reg.dw1.bits.indirect_offset = offset;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_vec1_indirect (uint32_t subnr,
+ int32_t offset)
+{
+ struct brw_reg reg = brw_vec1_grf (0, 0);
+ reg.subnr = subnr;
+ reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER;
+ reg.dw1.bits.indirect_offset = offset;
+ return reg;
+}
+
+static cairo_always_inline struct brw_reg
+deref_4f (struct brw_indirect ptr, int32_t offset)
+{
+ return brw_vec4_indirect (ptr.addr_subnr, ptr.addr_offset + offset);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1f(struct brw_indirect ptr, int32_t offset)
+{
+ return brw_vec1_indirect (ptr.addr_subnr, ptr.addr_offset + offset);
+}
+
+static cairo_always_inline struct brw_reg
+deref_4b(struct brw_indirect ptr, int32_t offset)
+{
+ return retype (deref_4f (ptr, offset), BRW_REGISTER_TYPE_B);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1uw(struct brw_indirect ptr, int32_t offset)
+{
+ return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UW);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1d (struct brw_indirect ptr, int32_t offset)
+{
+ return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_D);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1ud (struct brw_indirect ptr, int32_t offset)
+{
+ return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UD);
+}
+
+static cairo_always_inline struct brw_reg
+get_addr_reg (struct brw_indirect ptr)
+{
+ return brw_address_reg (ptr.addr_subnr);
+}
+
+static cairo_always_inline struct brw_indirect
+brw_indirect_offset (struct brw_indirect ptr, int32_t offset)
+{
+ ptr.addr_offset += offset;
+ return ptr;
+}
+
+static cairo_always_inline struct brw_indirect
+brw_indirect (uint32_t addr_subnr, int32_t offset)
+{
+ struct brw_indirect ptr;
+ ptr.addr_subnr = addr_subnr;
+ ptr.addr_offset = offset;
+ ptr.pad = 0;
+ return ptr;
+}
+
+static cairo_always_inline struct brw_instruction *
+current_insn (struct brw_compile *p)
+{
+ return &p->store[p->nr_insn];
+}
+
+cairo_private void brw_pop_insn_state (struct brw_compile *p);
+cairo_private void brw_push_insn_state (struct brw_compile *p);
+cairo_private void brw_set_mask_control (struct brw_compile *p, uint32_t value);
+cairo_private void brw_set_saturate (struct brw_compile *p, uint32_t value);
+cairo_private void brw_set_access_mode (struct brw_compile *p, uint32_t access_mode);
+cairo_private void brw_set_compression_control (struct brw_compile *p, int control);
+cairo_private void brw_set_predicate_control_flag_value (struct brw_compile *p, uint32_t value);
+cairo_private void brw_set_predicate_control (struct brw_compile *p, uint32_t pc);
+cairo_private void brw_set_conditionalmod (struct brw_compile *p, uint32_t conditional);
+
+cairo_private void
+brw_compile_init (struct brw_compile *p,
+ cairo_bool_t is_g4x);
+cairo_private const uint32_t *brw_get_program (struct brw_compile *p, uint32_t *sz);
+
+/* Helpers for regular instructions:
+ */
+#define ALU1(OP) \
+cairo_private_no_warn struct brw_instruction * \
+brw_##OP(struct brw_compile *p, \
+ struct brw_reg dest, \
+ struct brw_reg src0);
+
+#define ALU2(OP) \
+cairo_private_no_warn struct brw_instruction * \
+brw_##OP(struct brw_compile *p, \
+ struct brw_reg dest, \
+ struct brw_reg src0, \
+ struct brw_reg src1);
+
+ALU1(MOV)
+ALU2(SEL)
+ALU1(NOT)
+ALU2(AND)
+ALU2(OR)
+ALU2(XOR)
+ALU2(SHR)
+ALU2(SHL)
+ALU2(RSR)
+ALU2(RSL)
+ALU2(ASR)
+ALU2(JMPI)
+ALU2(ADD)
+ALU2(MUL)
+ALU1(FRC)
+ALU1(RNDD)
+ALU1(RNDZ)
+ALU2(MAC)
+ALU2(MACH)
+ALU1(LZD)
+ALU2(DP4)
+ALU2(DPH)
+ALU2(DP3)
+ALU2(DP2)
+ALU2(LINE)
+
+#undef ALU1
+#undef ALU2
+
+/* Helpers for SEND instruction: */
+cairo_private void
+brw_urb_WRITE (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ struct brw_reg src0,
+ int allocate,
+ int used,
+ uint32_t msg_length,
+ uint32_t response_length,
+ int eot,
+ int writes_complete,
+ uint32_t offset,
+ uint32_t swizzle);
+
+cairo_private void
+brw_fb_WRITE (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ struct brw_reg src0,
+ uint32_t binding_table_index,
+ uint32_t msg_length,
+ uint32_t response_length,
+ int eot);
+
+cairo_private void
+brw_SAMPLE (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ struct brw_reg src0,
+ uint32_t binding_table_index,
+ uint32_t sampler,
+ uint32_t writemask,
+ uint32_t msg_type,
+ uint32_t response_length,
+ uint32_t msg_length,
+ cairo_bool_t eot);
+
+cairo_private void
+brw_math_16 (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t function,
+ uint32_t saturate,
+ uint32_t msg_reg_nr,
+ struct brw_reg src,
+ uint32_t precision);
+
+cairo_private void
+brw_math (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t function,
+ uint32_t saturate,
+ uint32_t msg_reg_nr,
+ struct brw_reg src,
+ uint32_t data_type,
+ uint32_t precision);
+
+cairo_private void
+brw_dp_READ_16 (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t msg_reg_nr,
+ uint32_t scratch_offset);
+
+cairo_private void
+brw_dp_WRITE_16 (struct brw_compile *p,
+ struct brw_reg src,
+ uint32_t msg_reg_nr,
+ uint32_t scratch_offset);
+
+/* If/else/endif. Works by manipulating the execution flags on each
+ * channel.
+ */
+cairo_private struct brw_instruction *
+brw_IF (struct brw_compile *p,
+ uint32_t execute_size);
+
+cairo_private struct brw_instruction *
+brw_ELSE (struct brw_compile *p,
+ struct brw_instruction *if_insn);
+
+cairo_private void
+brw_ENDIF (struct brw_compile *p,
+ struct brw_instruction *if_or_else_insn);
+
+
+/* DO/WHILE loops: */
+cairo_private struct brw_instruction *
+brw_DO (struct brw_compile *p,
+ uint32_t execute_size);
+
+cairo_private struct brw_instruction *
+brw_WHILE (struct brw_compile *p,
+ struct brw_instruction *patch_insn);
+
+cairo_private struct brw_instruction *
+brw_BREAK (struct brw_compile *p);
+
+cairo_private struct brw_instruction *
+brw_CONT (struct brw_compile *p);
+
+/* Forward jumps: */
+cairo_private void
+brw_land_fwd_jump (struct brw_compile *p,
+ struct brw_instruction *jmp_insn);
+
+cairo_private void
+brw_NOP (struct brw_compile *p);
+
+/* Special case: there is never a destination, execution size will be
+ * taken from src0:
+ */
+cairo_private void
+brw_CMP (struct brw_compile *p,
+ struct brw_reg dest,
+ uint32_t conditional,
+ struct brw_reg src0,
+ struct brw_reg src1);
+
+cairo_private void
+brw_print_reg (struct brw_reg reg);
+
+cairo_private struct brw_instruction *
+brw_next_instruction (struct brw_compile *p,
+ uint32_t opcode);
+
+cairo_private void
+brw_instruction_set_destination (struct brw_instruction *insn,
+ struct brw_reg dest);
+
+cairo_private void
+brw_instruction_set_source0 (struct brw_instruction *insn,
+ struct brw_reg reg);
+
+cairo_private void
+brw_instruction_set_dp_write_message (struct brw_instruction *insn,
+ uint32_t binding_table_index,
+ uint32_t msg_control,
+ uint32_t msg_type,
+ uint32_t msg_length,
+ uint32_t pixel_scoreboard_clear,
+ uint32_t response_length,
+ uint32_t end_of_thread);
+
+/***********************************************************************
+ * brw_eu_util.c:
+ */
+
+cairo_private void
+brw_copy_indirect_to_indirect (struct brw_compile *p,
+ struct brw_indirect dst_ptr,
+ struct brw_indirect src_ptr,
+ uint32_t count);
+
+cairo_private void
+brw_copy_from_indirect (struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_indirect ptr,
+ uint32_t count);
+
+cairo_private void
+brw_copy4 (struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_reg src,
+ uint32_t count);
+
+cairo_private void
+brw_copy8 (struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_reg src,
+ uint32_t count);
+
+cairo_private void
+brw_math_invert (struct brw_compile *p,
+ struct brw_reg dst,
+ struct brw_reg src);
+
+cairo_private void
+brw_set_src1 (struct brw_instruction *insn,
+ struct brw_reg reg);
+
+#endif
diff --git a/src/drm/cairo-drm-intel-brw-structs.h b/src/drm/cairo-drm-intel-brw-structs.h
new file mode 100755
index 000000000..f42483ed1
--- /dev/null
+++ b/src/drm/cairo-drm-intel-brw-structs.h
@@ -0,0 +1,1328 @@
+/**************************************************************************
+ *
+ * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef CAIRO_DRM_INTEL_BRW_STRUCTS_H
+#define CAIRO_DRM_INTEL_BRW_STRUCTS_H
+
+#include "cairo-types-private.h"
+
+/* Command packets:
+*/
+struct header {
+ unsigned int length:16;
+ unsigned int opcode:16;
+};
+
+union header_union {
+ struct header bits;
+ unsigned int dword;
+};
+
+struct brw_3d_control {
+ struct {
+ unsigned int length:8;
+ unsigned int notify_enable:1;
+ unsigned int pad:3;
+ unsigned int wc_flush_enable:1;
+ unsigned int depth_stall_enable:1;
+ unsigned int operation:2;
+ unsigned int opcode:16;
+ } header;
+
+ struct {
+ unsigned int pad:2;
+ unsigned int dest_addr_type:1;
+ unsigned int dest_addr:29;
+ } dest;
+
+ unsigned int dword2;
+ unsigned int dword3;
+};
+
+
+struct brw_3d_primitive {
+ struct {
+ unsigned int length:8;
+ unsigned int pad:2;
+ unsigned int topology:5;
+ unsigned int indexed:1;
+ unsigned int opcode:16;
+ } header;
+
+ unsigned int verts_per_instance;
+ unsigned int start_vert_location;
+ unsigned int instance_count;
+ unsigned int start_instance_location;
+ unsigned int base_vert_location;
+};
+
+/* These seem to be passed around as function args, so it works out
+ * better to keep them as #defines:
+ */
+#define BRW_FLUSH_READ_CACHE 0x1
+#define BRW_FLUSH_STATE_CACHE 0x2
+#define BRW_INHIBIT_FLUSH_RENDER_CACHE 0x4
+#define BRW_FLUSH_SNAPSHOT_COUNTERS 0x8
+
+struct brw_mi_flush {
+ unsigned int flags:4;
+ unsigned int pad:12;
+ unsigned int opcode:16;
+};
+
+struct brw_vf_statistics {
+ unsigned int statistics_enable:1;
+ unsigned int pad:15;
+ unsigned int opcode:16;
+};
+
+
+struct brw_binding_table_pointers {
+ struct header header;
+ unsigned int vs;
+ unsigned int gs;
+ unsigned int clp;
+ unsigned int sf;
+ unsigned int wm;
+};
+
+struct brw_blend_constant_color {
+ struct header header;
+ float blend_constant_color[4];
+};
+
+struct brw_depthbuffer {
+ union header_union header;
+
+ union {
+ struct {
+ unsigned int pitch:18;
+ unsigned int format:3;
+ unsigned int pad:4;
+ unsigned int depth_offset_disable:1;
+ unsigned int tile_walk:1;
+ unsigned int tiled_surface:1;
+ unsigned int pad2:1;
+ unsigned int surface_type:3;
+ } bits;
+ unsigned int dword;
+ } dword1;
+
+ unsigned int dword2_base_addr;
+
+ union {
+ struct {
+ unsigned int pad:1;
+ unsigned int mipmap_layout:1;
+ unsigned int lod:4;
+ unsigned int width:13;
+ unsigned int height:13;
+ } bits;
+ unsigned int dword;
+ } dword3;
+
+ union {
+ struct {
+ unsigned int pad:12;
+ unsigned int min_array_element:9;
+ unsigned int depth:11;
+ } bits;
+ unsigned int dword;
+ } dword4;
+};
+
+struct brw_drawrect {
+ struct header header;
+ unsigned int xmin:16;
+ unsigned int ymin:16;
+ unsigned int xmax:16;
+ unsigned int ymax:16;
+ unsigned int xorg:16;
+ unsigned int yorg:16;
+};
+
+struct brw_global_depth_offset_clamp {
+ struct header header;
+ float depth_offset_clamp;
+};
+
+struct brw_indexbuffer {
+ union {
+ struct {
+ unsigned int length:8;
+ unsigned int index_format:2;
+ unsigned int cut_index_enable:1;
+ unsigned int pad:5;
+ unsigned int opcode:16;
+ } bits;
+ unsigned int dword;
+ } header;
+ unsigned int buffer_start;
+ unsigned int buffer_end;
+};
+
+
+struct brw_line_stipple {
+ struct header header;
+
+ struct {
+ unsigned int pattern:16;
+ unsigned int pad:16;
+ } bits0;
+
+ struct {
+ unsigned int repeat_count:9;
+ unsigned int pad:7;
+ unsigned int inverse_repeat_count:16;
+ } bits1;
+};
+
+struct brw_pipelined_state_pointers {
+ struct header header;
+
+ struct {
+ unsigned int pad:5;
+ unsigned int offset:27;
+ } vs;
+
+ struct {
+ unsigned int enable:1;
+ unsigned int pad:4;
+ unsigned int offset:27;
+ } gs;
+
+ struct {
+ unsigned int enable:1;
+ unsigned int pad:4;
+ unsigned int offset:27;
+ } clp;
+
+ struct {
+ unsigned int pad:5;
+ unsigned int offset:27;
+ } sf;
+
+ struct {
+ unsigned int pad:5;
+ unsigned int offset:27;
+ } wm;
+
+ struct {
+ unsigned int pad:6;
+ unsigned int offset:26;
+ } cc;
+};
+
+struct brw_polygon_stipple_offset {
+ struct header header;
+
+ struct {
+ unsigned int y_offset:5;
+ unsigned int pad:3;
+ unsigned int x_offset:5;
+ unsigned int pad0:19;
+ } bits0;
+};
+
+struct brw_polygon_stipple {
+ struct header header;
+ unsigned int stipple[32];
+};
+
+struct brw_pipeline_select {
+ struct {
+ unsigned int pipeline_select:1;
+ unsigned int pad:15;
+ unsigned int opcode:16;
+ } header;
+};
+
+struct brw_pipe_control {
+ struct {
+ unsigned int length:8;
+ unsigned int notify_enable:1;
+ unsigned int pad:2;
+ unsigned int instruction_state_cache_flush_enable:1;
+ unsigned int write_cache_flush_enable:1;
+ unsigned int depth_stall_enable:1;
+ unsigned int post_sync_operation:2;
+
+ unsigned int opcode:16;
+ } header;
+
+ struct {
+ unsigned int pad:2;
+ unsigned int dest_addr_type:1;
+ unsigned int dest_addr:29;
+ } bits1;
+
+ unsigned int data0;
+ unsigned int data1;
+};
+
+
+struct brw_urb_fence {
+ struct {
+ unsigned int length:8;
+ unsigned int vs_realloc:1;
+ unsigned int gs_realloc:1;
+ unsigned int clp_realloc:1;
+ unsigned int sf_realloc:1;
+ unsigned int vfe_realloc:1;
+ unsigned int cs_realloc:1;
+ unsigned int pad:2;
+ unsigned int opcode:16;
+ } header;
+
+ struct {
+ unsigned int vs_fence:10;
+ unsigned int gs_fence:10;
+ unsigned int clp_fence:10;
+ unsigned int pad:2;
+ } bits0;
+
+ struct {
+ unsigned int sf_fence:10;
+ unsigned int vf_fence:10;
+ unsigned int cs_fence:10;
+ unsigned int pad:2;
+ } bits1;
+};
+
+struct brw_constant_buffer_state {
+ struct header header;
+
+ struct {
+ unsigned int nr_urb_entries:3;
+ unsigned int pad:1;
+ unsigned int urb_entry_size:5;
+ unsigned int pad0:23;
+ } bits0;
+};
+
+struct brw_constant_buffer {
+ struct {
+ unsigned int length:8;
+ unsigned int valid:1;
+ unsigned int pad:7;
+ unsigned int opcode:16;
+ } header;
+
+ struct {
+ unsigned int buffer_length:6;
+ unsigned int buffer_address:26;
+ } bits0;
+};
+
+struct brw_state_base_address {
+ struct header header;
+
+ struct {
+ unsigned int modify_enable:1;
+ unsigned int pad:4;
+ unsigned int general_state_address:27;
+ } bits0;
+
+ struct {
+ unsigned int modify_enable:1;
+ unsigned int pad:4;
+ unsigned int surface_state_address:27;
+ } bits1;
+
+ struct {
+ unsigned int modify_enable:1;
+ unsigned int pad:4;
+ unsigned int indirect_object_state_address:27;
+ } bits2;
+
+ struct {
+ unsigned int modify_enable:1;
+ unsigned int pad:11;
+ unsigned int general_state_upper_bound:20;
+ } bits3;
+
+ struct {
+ unsigned int modify_enable:1;
+ unsigned int pad:11;
+ unsigned int indirect_object_state_upper_bound:20;
+ } bits4;
+};
+
+struct brw_state_prefetch {
+ struct header header;
+
+ struct {
+ unsigned int prefetch_count:3;
+ unsigned int pad:3;
+ unsigned int prefetch_pointer:26;
+ } bits0;
+};
+
+struct brw_system_instruction_pointer {
+ struct header header;
+
+ struct {
+ unsigned int pad:4;
+ unsigned int system_instruction_pointer:28;
+ } bits0;
+};
+
+
+/* State structs for the various fixed function units:
+*/
+
+struct thread0 {
+ unsigned int pad0:1;
+ unsigned int grf_reg_count:3;
+ unsigned int pad1:2;
+ unsigned int kernel_start_pointer:26;
+};
+
+struct thread1 {
+ unsigned int ext_halt_exception_enable:1;
+ unsigned int sw_exception_enable:1;
+ unsigned int mask_stack_exception_enable:1;
+ unsigned int timeout_exception_enable:1;
+ unsigned int illegal_op_exception_enable:1;
+ unsigned int pad0:3;
+ unsigned int depth_coef_urb_read_offset:6; /* WM only */
+ unsigned int pad1:2;
+ unsigned int floating_point_mode:1;
+ unsigned int thread_priority:1;
+ unsigned int binding_table_entry_count:8;
+ unsigned int pad3:5;
+ unsigned int single_program_flow:1;
+};
+
+struct thread2 {
+ unsigned int per_thread_scratch_space:4;
+ unsigned int pad0:6;
+ unsigned int scratch_space_base_pointer:22;
+};
+
+struct thread3 {
+ unsigned int dispatch_grf_start_reg:4;
+ unsigned int urb_entry_read_offset:6;
+ unsigned int pad0:1;
+ unsigned int urb_entry_read_length:6;
+ unsigned int pad1:1;
+ unsigned int const_urb_entry_read_offset:6;
+ unsigned int pad2:1;
+ unsigned int const_urb_entry_read_length:6;
+ unsigned int pad3:1;
+};
+
+struct brw_clip_unit_state {
+ struct thread0 thread0;
+ struct thread1 thread1;
+ struct thread2 thread2;
+ struct thread3 thread3;
+
+ struct {
+ unsigned int pad0:9;
+ unsigned int gs_output_stats:1; /* not always */
+ unsigned int stats_enable:1;
+ unsigned int nr_urb_entries:7;
+ unsigned int pad1:1;
+ unsigned int urb_entry_allocation_size:5;
+ unsigned int pad2:1;
+ unsigned int max_threads:6; /* may be less */
+ unsigned int pad3:1;
+ } thread4;
+
+ struct {
+ unsigned int pad0:13;
+ unsigned int clip_mode:3;
+ unsigned int userclip_enable_flags:8;
+ unsigned int userclip_must_clip:1;
+ unsigned int pad1:1;
+ unsigned int guard_band_enable:1;
+ unsigned int viewport_z_clip_enable:1;
+ unsigned int viewport_xy_clip_enable:1;
+ unsigned int vertex_position_space:1;
+ unsigned int api_mode:1;
+ unsigned int pad2:1;
+ } clip5;
+
+ struct {
+ unsigned int pad0:5;
+ unsigned int clipper_viewport_state_ptr:27;
+ } clip6;
+
+ float viewport_xmin;
+ float viewport_xmax;
+ float viewport_ymin;
+ float viewport_ymax;
+};
+
+struct brw_cc_unit_state {
+ struct {
+ unsigned int pad0:3;
+ unsigned int bf_stencil_pass_depth_pass_op:3;
+ unsigned int bf_stencil_pass_depth_fail_op:3;
+ unsigned int bf_stencil_fail_op:3;
+ unsigned int bf_stencil_func:3;
+ unsigned int bf_stencil_enable:1;
+ unsigned int pad1:2;
+ unsigned int stencil_write_enable:1;
+ unsigned int stencil_pass_depth_pass_op:3;
+ unsigned int stencil_pass_depth_fail_op:3;
+ unsigned int stencil_fail_op:3;
+ unsigned int stencil_func:3;
+ unsigned int stencil_enable:1;
+ } cc0;
+
+ struct {
+ unsigned int bf_stencil_ref:8;
+ unsigned int stencil_write_mask:8;
+ unsigned int stencil_test_mask:8;
+ unsigned int stencil_ref:8;
+ } cc1;
+
+ struct {
+ unsigned int logicop_enable:1;
+ unsigned int pad0:10;
+ unsigned int depth_write_enable:1;
+ unsigned int depth_test_function:3;
+ unsigned int depth_test:1;
+ unsigned int bf_stencil_write_mask:8;
+ unsigned int bf_stencil_test_mask:8;
+ } cc2;
+
+ struct {
+ unsigned int pad0:8;
+ unsigned int alpha_test_func:3;
+ unsigned int alpha_test:1;
+ unsigned int blend_enable:1;
+ unsigned int ia_blend_enable:1;
+ unsigned int pad1:1;
+ unsigned int alpha_test_format:1;
+ unsigned int pad2:16;
+ } cc3;
+
+ struct {
+ unsigned int pad0:5;
+ unsigned int cc_viewport_state_offset:27;
+ } cc4;
+
+ struct {
+ unsigned int pad0:2;
+ unsigned int ia_dest_blend_factor:5;
+ unsigned int ia_src_blend_factor:5;
+ unsigned int ia_blend_function:3;
+ unsigned int statistics_enable:1;
+ unsigned int logicop_func:4;
+ unsigned int pad1:11;
+ unsigned int dither_enable:1;
+ } cc5;
+
+ struct {
+ unsigned int clamp_post_alpha_blend:1;
+ unsigned int clamp_pre_alpha_blend:1;
+ unsigned int clamp_range:2;
+ unsigned int pad0:11;
+ unsigned int y_dither_offset:2;
+ unsigned int x_dither_offset:2;
+ unsigned int dest_blend_factor:5;
+ unsigned int src_blend_factor:5;
+ unsigned int blend_function:3;
+ } cc6;
+
+ struct {
+ union {
+ float f;
+ unsigned char ub[4];
+ } alpha_ref;
+ } cc7;
+};
+
+struct brw_sf_unit_state {
+ struct thread0 thread0;
+ struct {
+ unsigned int pad0:7;
+ unsigned int sw_exception_enable:1;
+ unsigned int pad1:3;
+ unsigned int mask_stack_exception_enable:1;
+ unsigned int pad2:1;
+ unsigned int illegal_op_exception_enable:1;
+ unsigned int pad3:2;
+ unsigned int floating_point_mode:1;
+ unsigned int thread_priority:1;
+ unsigned int binding_table_entry_count:8;
+ unsigned int pad4:5;
+ unsigned int single_program_flow:1;
+ } sf1;
+
+ struct thread2 thread2;
+ struct thread3 thread3;
+
+ struct {
+ unsigned int pad0:10;
+ unsigned int stats_enable:1;
+ unsigned int nr_urb_entries:7;
+ unsigned int pad1:1;
+ unsigned int urb_entry_allocation_size:5;
+ unsigned int pad2:1;
+ unsigned int max_threads:6;
+ unsigned int pad3:1;
+ } thread4;
+
+ struct {
+ unsigned int front_winding:1;
+ unsigned int viewport_transform:1;
+ unsigned int pad0:3;
+ unsigned int sf_viewport_state_offset:27;
+ } sf5;
+
+ struct {
+ unsigned int pad0:9;
+ unsigned int dest_org_vbias:4;
+ unsigned int dest_org_hbias:4;
+ unsigned int scissor:1;
+ unsigned int disable_2x2_trifilter:1;
+ unsigned int disable_zero_pix_trifilter:1;
+ unsigned int point_rast_rule:2;
+ unsigned int line_endcap_aa_region_width:2;
+ unsigned int line_width:4;
+ unsigned int fast_scissor_disable:1;
+ unsigned int cull_mode:2;
+ unsigned int aa_enable:1;
+ } sf6;
+
+ struct {
+ unsigned int point_size:11;
+ unsigned int use_point_size_state:1;
+ unsigned int subpixel_precision:1;
+ unsigned int sprite_point:1;
+ unsigned int pad0:11;
+ unsigned int trifan_pv:2;
+ unsigned int linestrip_pv:2;
+ unsigned int tristrip_pv:2;
+ unsigned int line_last_pixel_enable:1;
+ } sf7;
+};
+
+struct brw_gs_unit_state {
+ struct thread0 thread0;
+ struct thread1 thread1;
+ struct thread2 thread2;
+ struct thread3 thread3;
+
+ struct {
+ unsigned int pad0:10;
+ unsigned int stats_enable:1;
+ unsigned int nr_urb_entries:7;
+ unsigned int pad1:1;
+ unsigned int urb_entry_allocation_size:5;
+ unsigned int pad2:1;
+ unsigned int max_threads:1;
+ unsigned int pad3:6;
+ } thread4;
+
+ struct {
+ unsigned int sampler_count:3;
+ unsigned int pad0:2;
+ unsigned int sampler_state_pointer:27;
+ } gs5;
+
+ struct {
+ unsigned int max_vp_index:4;
+ unsigned int pad0:26;
+ unsigned int reorder_enable:1;
+ unsigned int pad1:1;
+ } gs6;
+};
+
+struct brw_vs_unit_state {
+ struct thread0 thread0;
+ struct thread1 thread1;
+ struct thread2 thread2;
+ struct thread3 thread3;
+
+ struct {
+ unsigned int pad0:10;
+ unsigned int stats_enable:1;
+ unsigned int nr_urb_entries:7;
+ unsigned int pad1:1;
+ unsigned int urb_entry_allocation_size:5;
+ unsigned int pad2:1;
+ unsigned int max_threads:4;
+ unsigned int pad3:3;
+ } thread4;
+
+ struct {
+ unsigned int sampler_count:3;
+ unsigned int pad0:2;
+ unsigned int sampler_state_pointer:27;
+ } vs5;
+
+ struct {
+ unsigned int vs_enable:1;
+ unsigned int vert_cache_disable:1;
+ unsigned int pad0:30;
+ } vs6;
+};
+
+struct brw_wm_unit_state {
+ struct thread0 thread0;
+ struct thread1 thread1;
+ struct thread2 thread2;
+ struct thread3 thread3;
+
+ struct {
+ unsigned int stats_enable:1;
+ unsigned int pad0:1;
+ unsigned int sampler_count:3;
+ unsigned int sampler_state_pointer:27;
+ } wm4;
+
+ struct {
+ unsigned int enable_8_pix:1;
+ unsigned int enable_16_pix:1;
+ unsigned int enable_32_pix:1;
+ unsigned int pad0:7;
+ unsigned int legacy_global_depth_bias:1;
+ unsigned int line_stipple:1;
+ unsigned int depth_offset:1;
+ unsigned int polygon_stipple:1;
+ unsigned int line_aa_region_width:2;
+ unsigned int line_endcap_aa_region_width:2;
+ unsigned int early_depth_test:1;
+ unsigned int thread_dispatch_enable:1;
+ unsigned int program_uses_depth:1;
+ unsigned int program_computes_depth:1;
+ unsigned int program_uses_killpixel:1;
+ unsigned int legacy_line_rast: 1;
+ unsigned int transposed_urb_read:1;
+ unsigned int max_threads:7;
+ } wm5;
+
+ float global_depth_offset_constant;
+ float global_depth_offset_scale;
+};
+
+/* The hardware supports two different modes for border color. The
+ * default (OpenGL) mode uses floating-point color channels, while the
+ * legacy mode uses 4 bytes.
+ *
+ * More significantly, the legacy mode respects the components of the
+ * border color for channels not present in the source, (whereas the
+ * default mode will ignore the border color's alpha channel and use
+ * alpha==1 for an RGB source, for example).
+ *
+ * The legacy mode matches the semantics specified by the Render
+ * extension.
+ */
+struct brw_sampler_default_border_color {
+ float color[4];
+};
+
+struct brw_sampler_legacy_border_color {
+ uint8_t color[4];
+};
+
+struct brw_sampler_state {
+ struct {
+ unsigned int shadow_function:3;
+ unsigned int lod_bias:11;
+ unsigned int min_filter:3;
+ unsigned int mag_filter:3;
+ unsigned int mip_filter:2;
+ unsigned int base_level:5;
+ unsigned int pad:1;
+ unsigned int lod_preclamp:1;
+ unsigned int border_color_mode:1;
+ unsigned int pad0:1;
+ unsigned int disable:1;
+ } ss0;
+
+ struct {
+ unsigned int r_wrap_mode:3;
+ unsigned int t_wrap_mode:3;
+ unsigned int s_wrap_mode:3;
+ unsigned int pad:3;
+ unsigned int max_lod:10;
+ unsigned int min_lod:10;
+ } ss1;
+
+ struct {
+ unsigned int pad:5;
+ unsigned int border_color_pointer:27;
+ } ss2;
+
+ struct {
+ unsigned int pad:19;
+ unsigned int max_aniso:3;
+ unsigned int chroma_key_mode:1;
+ unsigned int chroma_key_index:2;
+ unsigned int chroma_key_enable:1;
+ unsigned int monochrome_filter_width:3;
+ unsigned int monochrome_filter_height:3;
+ } ss3;
+};
+
+struct brw_clipper_viewport {
+ float xmin;
+ float xmax;
+ float ymin;
+ float ymax;
+};
+
+struct brw_cc_viewport {
+ float min_depth;
+ float max_depth;
+};
+
+struct brw_sf_viewport {
+ struct {
+ float m00;
+ float m11;
+ float m22;
+ float m30;
+ float m31;
+ float m32;
+ } viewport;
+
+ struct {
+ short xmin;
+ short ymin;
+ short xmax;
+ short ymax;
+ } scissor;
+};
+
+/* Documented in the subsystem/shared-functions/sampler chapter...
+*/
+struct brw_surface_state {
+ struct {
+ unsigned int cube_pos_z:1;
+ unsigned int cube_neg_z:1;
+ unsigned int cube_pos_y:1;
+ unsigned int cube_neg_y:1;
+ unsigned int cube_pos_x:1;
+ unsigned int cube_neg_x:1;
+ unsigned int pad:3;
+ unsigned int render_cache_read_mode:1;
+ unsigned int mipmap_layout_mode:1;
+ unsigned int vert_line_stride_ofs:1;
+ unsigned int vert_line_stride:1;
+ unsigned int color_blend:1;
+ unsigned int writedisable_blue:1;
+ unsigned int writedisable_green:1;
+ unsigned int writedisable_red:1;
+ unsigned int writedisable_alpha:1;
+ unsigned int surface_format:9;
+ unsigned int data_return_format:1;
+ unsigned int pad0:1;
+ unsigned int surface_type:3;
+ } ss0;
+
+ struct {
+ unsigned int base_addr;
+ } ss1;
+
+ struct {
+ unsigned int render_target_rotation:2;
+ unsigned int mip_count:4;
+ unsigned int width:13;
+ unsigned int height:13;
+ } ss2;
+
+ struct {
+ unsigned int tile_walk:1;
+ unsigned int tiled_surface:1;
+ unsigned int pad:1;
+ unsigned int pitch:18;
+ unsigned int depth:11;
+ } ss3;
+
+ struct {
+ unsigned int pad:19;
+ unsigned int min_array_elt:9;
+ unsigned int min_lod:4;
+ } ss4;
+
+ struct {
+ unsigned int pad:20;
+ unsigned int y_offset:4;
+ unsigned int pad2:1;
+ unsigned int x_offset:7;
+ } ss5;
+};
+
+struct brw_vertex_buffer_state {
+ struct {
+ unsigned int pitch:11;
+ unsigned int pad:15;
+ unsigned int access_type:1;
+ unsigned int vb_index:5;
+ } vb0;
+
+ unsigned int start_addr;
+ unsigned int max_index;
+#if 1
+ unsigned int instance_data_step_rate; /* not included for sequential/random vertices? */
+#endif
+};
+
+#define BRW_VBP_MAX 17
+
+struct brw_vb_array_state {
+ struct header header;
+ struct brw_vertex_buffer_state vb[BRW_VBP_MAX];
+};
+
+struct brw_vertex_element_state {
+ struct {
+ unsigned int src_offset:11;
+ unsigned int pad:5;
+ unsigned int src_format:9;
+ unsigned int pad0:1;
+ unsigned int valid:1;
+ unsigned int vertex_buffer_index:5;
+ } ve0;
+
+ struct {
+ unsigned int dst_offset:8;
+ unsigned int pad:8;
+ unsigned int vfcomponent3:4;
+ unsigned int vfcomponent2:4;
+ unsigned int vfcomponent1:4;
+ unsigned int vfcomponent0:4;
+ } ve1;
+};
+
+#define BRW_VEP_MAX 18
+
+struct brw_vertex_element_packet {
+ struct header header;
+ struct brw_vertex_element_state ve[BRW_VEP_MAX];
+};
+
+struct brw_urb_immediate {
+ unsigned int opcode:4;
+ unsigned int offset:6;
+ unsigned int swizzle_control:2;
+ unsigned int pad:1;
+ unsigned int allocate:1;
+ unsigned int used:1;
+ unsigned int complete:1;
+ unsigned int response_length:4;
+ unsigned int msg_length:4;
+ unsigned int msg_target:4;
+ unsigned int pad1:3;
+ unsigned int end_of_thread:1;
+};
+
+/* Instruction format for the execution units: */
+
+struct brw_instruction {
+ struct {
+ unsigned int opcode:7;
+ unsigned int pad:1;
+ unsigned int access_mode:1;
+ unsigned int mask_control:1;
+ unsigned int dependency_control:2;
+ unsigned int compression_control:2;
+ unsigned int thread_control:2;
+ unsigned int predicate_control:4;
+ unsigned int predicate_inverse:1;
+ unsigned int execution_size:3;
+ unsigned int destreg__conditonalmod:4; /* destreg - send, conditionalmod - others */
+ unsigned int pad0:2;
+ unsigned int debug_control:1;
+ unsigned int saturate:1;
+ } header;
+
+ union {
+ struct {
+ unsigned int dest_reg_file:2;
+ unsigned int dest_reg_type:3;
+ unsigned int src0_reg_file:2;
+ unsigned int src0_reg_type:3;
+ unsigned int src1_reg_file:2;
+ unsigned int src1_reg_type:3;
+ unsigned int pad:1;
+ unsigned int dest_subreg_nr:5;
+ unsigned int dest_reg_nr:8;
+ unsigned int dest_horiz_stride:2;
+ unsigned int dest_address_mode:1;
+ } da1;
+
+ struct {
+ unsigned int dest_reg_file:2;
+ unsigned int dest_reg_type:3;
+ unsigned int src0_reg_file:2;
+ unsigned int src0_reg_type:3;
+ unsigned int pad:6;
+ int dest_indirect_offset:10; /* offset against the deref'd address reg */
+ unsigned int dest_subreg_nr:3; /* subnr for the address reg a0.x */
+ unsigned int dest_horiz_stride:2;
+ unsigned int dest_address_mode:1;
+ } ia1;
+
+ struct {
+ unsigned int dest_reg_file:2;
+ unsigned int dest_reg_type:3;
+ unsigned int src0_reg_file:2;
+ unsigned int src0_reg_type:3;
+ unsigned int src1_reg_file:2;
+ unsigned int src1_reg_type:3;
+ unsigned int pad0:1;
+ unsigned int dest_writemask:4;
+ unsigned int dest_subreg_nr:1;
+ unsigned int dest_reg_nr:8;
+ unsigned int pad1:2;
+ unsigned int dest_address_mode:1;
+ } da16;
+
+ struct {
+ unsigned int dest_reg_file:2;
+ unsigned int dest_reg_type:3;
+ unsigned int src0_reg_file:2;
+ unsigned int src0_reg_type:3;
+ unsigned int pad0:6;
+ unsigned int dest_writemask:4;
+ int dest_indirect_offset:6;
+ unsigned int dest_subreg_nr:3;
+ unsigned int pad1:2;
+ unsigned int dest_address_mode:1;
+ } ia16;
+ } bits1;
+
+
+ union {
+ struct {
+ unsigned int src0_subreg_nr:5;
+ unsigned int src0_reg_nr:8;
+ unsigned int src0_abs:1;
+ unsigned int src0_negate:1;
+ unsigned int src0_address_mode:1;
+ unsigned int src0_horiz_stride:2;
+ unsigned int src0_width:3;
+ unsigned int src0_vert_stride:4;
+ unsigned int flag_reg_nr:1;
+ unsigned int pad:6;
+ } da1;
+
+ struct {
+ int src0_indirect_offset:10;
+ unsigned int src0_subreg_nr:3;
+ unsigned int src0_abs:1;
+ unsigned int src0_negate:1;
+ unsigned int src0_address_mode:1;
+ unsigned int src0_horiz_stride:2;
+ unsigned int src0_width:3;
+ unsigned int src0_vert_stride:4;
+ unsigned int flag_reg_nr:1;
+ unsigned int pad:6;
+ } ia1;
+
+ struct {
+ unsigned int src0_swz_x:2;
+ unsigned int src0_swz_y:2;
+ unsigned int src0_subreg_nr:1;
+ unsigned int src0_reg_nr:8;
+ unsigned int src0_abs:1;
+ unsigned int src0_negate:1;
+ unsigned int src0_address_mode:1;
+ unsigned int src0_swz_z:2;
+ unsigned int src0_swz_w:2;
+ unsigned int pad0:1;
+ unsigned int src0_vert_stride:4;
+ unsigned int flag_reg_nr:1;
+ unsigned int pad1:6;
+ } da16;
+
+ struct {
+ unsigned int src0_swz_x:2;
+ unsigned int src0_swz_y:2;
+ int src0_indirect_offset:6;
+ unsigned int src0_subreg_nr:3;
+ unsigned int src0_abs:1;
+ unsigned int src0_negate:1;
+ unsigned int src0_address_mode:1;
+ unsigned int src0_swz_z:2;
+ unsigned int src0_swz_w:2;
+ unsigned int pad0:1;
+ unsigned int src0_vert_stride:4;
+ unsigned int flag_reg_nr:1;
+ unsigned int pad1:6;
+ } ia16;
+
+ } bits2;
+
+ union {
+ struct {
+ unsigned int src1_subreg_nr:5;
+ unsigned int src1_reg_nr:8;
+ unsigned int src1_abs:1;
+ unsigned int src1_negate:1;
+ unsigned int pad:1;
+ unsigned int src1_horiz_stride:2;
+ unsigned int src1_width:3;
+ unsigned int src1_vert_stride:4;
+ unsigned int pad0:7;
+ } da1;
+
+ struct {
+ unsigned int src1_swz_x:2;
+ unsigned int src1_swz_y:2;
+ unsigned int src1_subreg_nr:1;
+ unsigned int src1_reg_nr:8;
+ unsigned int src1_abs:1;
+ unsigned int src1_negate:1;
+ unsigned int pad0:1;
+ unsigned int src1_swz_z:2;
+ unsigned int src1_swz_w:2;
+ unsigned int pad1:1;
+ unsigned int src1_vert_stride:4;
+ unsigned int pad2:7;
+ } da16;
+
+ struct {
+ int src1_indirect_offset:10;
+ unsigned int src1_subreg_nr:3;
+ unsigned int src1_abs:1;
+ unsigned int src1_negate:1;
+ unsigned int pad0:1;
+ unsigned int src1_horiz_stride:2;
+ unsigned int src1_width:3;
+ unsigned int src1_vert_stride:4;
+ unsigned int flag_reg_nr:1;
+ unsigned int pad1:6;
+ } ia1;
+
+ struct {
+ unsigned int src1_swz_x:2;
+ unsigned int src1_swz_y:2;
+ int src1_indirect_offset:6;
+ unsigned int src1_subreg_nr:3;
+ unsigned int src1_abs:1;
+ unsigned int src1_negate:1;
+ unsigned int pad0:1;
+ unsigned int src1_swz_z:2;
+ unsigned int src1_swz_w:2;
+ unsigned int pad1:1;
+ unsigned int src1_vert_stride:4;
+ unsigned int flag_reg_nr:1;
+ unsigned int pad2:6;
+ } ia16;
+
+ struct {
+ int jump_count:16; /* note: signed */
+ unsigned int pop_count:4;
+ unsigned int pad0:12;
+ } if_else;
+
+ struct {
+ unsigned int function:4;
+ unsigned int int_type:1;
+ unsigned int precision:1;
+ unsigned int saturate:1;
+ unsigned int data_type:1;
+ unsigned int pad0:8;
+ unsigned int response_length:4;
+ unsigned int msg_length:4;
+ unsigned int msg_target:4;
+ unsigned int pad1:3;
+ unsigned int end_of_thread:1;
+ } math;
+
+ struct {
+ unsigned int binding_table_index:8;
+ unsigned int sampler:4;
+ unsigned int return_format:2;
+ unsigned int msg_type:2;
+ unsigned int response_length:4;
+ unsigned int msg_length:4;
+ unsigned int msg_target:4;
+ unsigned int pad1:3;
+ unsigned int end_of_thread:1;
+ } sampler;
+
+ struct {
+ uint32_t binding_table_index:8;
+ uint32_t sampler:4;
+ uint32_t msg_type:4;
+ uint32_t response_length:4;
+ uint32_t msg_length:4;
+ uint32_t msg_target:4;
+ uint32_t pad1:3;
+ uint32_t end_of_thread:1;
+ } sampler_g4x;
+
+ struct brw_urb_immediate urb;
+
+ struct {
+ unsigned int binding_table_index:8;
+ unsigned int msg_control:4;
+ unsigned int msg_type:2;
+ unsigned int target_cache:2;
+ unsigned int response_length:4;
+ unsigned int msg_length:4;
+ unsigned int msg_target:4;
+ unsigned int pad1:3;
+ unsigned int end_of_thread:1;
+ } dp_read;
+
+ struct {
+ unsigned int binding_table_index:8;
+ unsigned int msg_control:3;
+ unsigned int pixel_scoreboard_clear:1;
+ unsigned int msg_type:3;
+ unsigned int send_commit_msg:1;
+ unsigned int response_length:4;
+ unsigned int msg_length:4;
+ unsigned int msg_target:4;
+ unsigned int pad1:3;
+ unsigned int end_of_thread:1;
+ } dp_write;
+
+ struct {
+ unsigned int pad:16;
+ unsigned int response_length:4;
+ unsigned int msg_length:4;
+ unsigned int msg_target:4;
+ unsigned int pad1:3;
+ unsigned int end_of_thread:1;
+ } generic;
+
+ uint32_t ud;
+ int32_t d;
+ } bits3;
+};
+
+/* media pipeline */
+
+struct brw_vfe_state {
+ struct {
+ unsigned int per_thread_scratch_space:4;
+ unsigned int pad3:3;
+ unsigned int extend_vfe_state_present:1;
+ unsigned int pad2:2;
+ unsigned int scratch_base:22;
+ } vfe0;
+
+ struct {
+ unsigned int debug_counter_control:2;
+ unsigned int children_present:1;
+ unsigned int vfe_mode:4;
+ unsigned int pad2:2;
+ unsigned int num_urb_entries:7;
+ unsigned int urb_entry_alloc_size:9;
+ unsigned int max_threads:7;
+ } vfe1;
+
+ struct {
+ unsigned int pad4:4;
+ unsigned int interface_descriptor_base:28;
+ } vfe2;
+};
+
+struct brw_vld_state {
+ struct {
+ unsigned int pad6:6;
+ unsigned int scan_order:1;
+ unsigned int intra_vlc_format:1;
+ unsigned int quantizer_scale_type:1;
+ unsigned int concealment_motion_vector:1;
+ unsigned int frame_predict_frame_dct:1;
+ unsigned int top_field_first:1;
+ unsigned int picture_structure:2;
+ unsigned int intra_dc_precision:2;
+ unsigned int f_code_0_0:4;
+ unsigned int f_code_0_1:4;
+ unsigned int f_code_1_0:4;
+ unsigned int f_code_1_1:4;
+ } vld0;
+
+ struct {
+ unsigned int pad2:9;
+ unsigned int picture_coding_type:2;
+ unsigned int pad:21;
+ } vld1;
+
+ struct {
+ unsigned int index_0:4;
+ unsigned int index_1:4;
+ unsigned int index_2:4;
+ unsigned int index_3:4;
+ unsigned int index_4:4;
+ unsigned int index_5:4;
+ unsigned int index_6:4;
+ unsigned int index_7:4;
+ } desc_remap_table0;
+
+ struct {
+ unsigned int index_8:4;
+ unsigned int index_9:4;
+ unsigned int index_10:4;
+ unsigned int index_11:4;
+ unsigned int index_12:4;
+ unsigned int index_13:4;
+ unsigned int index_14:4;
+ unsigned int index_15:4;
+ } desc_remap_table1;
+};
+
+struct brw_interface_descriptor {
+ struct {
+ unsigned int grf_reg_blocks:4;
+ unsigned int pad:2;
+ unsigned int kernel_start_pointer:26;
+ } desc0;
+
+ struct {
+ unsigned int pad:7;
+ unsigned int software_exception:1;
+ unsigned int pad2:3;
+ unsigned int maskstack_exception:1;
+ unsigned int pad3:1;
+ unsigned int illegal_opcode_exception:1;
+ unsigned int pad4:2;
+ unsigned int floating_point_mode:1;
+ unsigned int thread_priority:1;
+ unsigned int single_program_flow:1;
+ unsigned int pad5:1;
+ unsigned int const_urb_entry_read_offset:6;
+ unsigned int const_urb_entry_read_len:6;
+ } desc1;
+
+ struct {
+ unsigned int pad:2;
+ unsigned int sampler_count:3;
+ unsigned int sampler_state_pointer:27;
+ } desc2;
+
+ struct {
+ unsigned int binding_table_entry_count:5;
+ unsigned int binding_table_pointer:27;
+ } desc3;
+};
+
+#endif
diff --git a/src/drm/cairo-drm-intel-command-private.h b/src/drm/cairo-drm-intel-command-private.h
new file mode 100755
index 000000000..a93ac12ab
--- /dev/null
+++ b/src/drm/cairo-drm-intel-command-private.h
@@ -0,0 +1,909 @@
+/**************************************************************************
+ *
+ * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef CAIRO_DRM_INTEL_COMMAND_PRIVATE_H
+#define CAIRO_DRM_INTEL_COMMAND_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#define CMD_MI (0x0 << 29)
+#define CMD_misc (0x1 << 29)
+#define CMD_2D (0x2 << 29)
+#define CMD_3D (0x3 << 29)
+/* 4-7 reserved */
+
+#define MI_NOOP (CMD_MI | 0)
+/* Batch */
+#define MI_BATCH_BUFFER (CMD_MI | (0x30 << 23) | 1)
+#define MI_BATCH_BUFFER_START (CMD_MI | (0x31 << 23))
+#define MI_BATCH_BUFFER_END (CMD_MI | (0x0a << 23))
+#define MI_BATCH_NON_SECURE (1)
+#define MI_BATCH_NON_SECURE_I965 (1 << 8)
+/* Flush */
+#define MI_FLUSH (CMD_MI | (0x04 << 23))
+#define MI_WRITE_DIRTY_STATE (1<<4)
+#define MI_END_SCENE (1<<3)
+#define MI_GLOBAL_SNAPSHOT_COUNT_RESET (1<<3)
+#define MI_INHIBIT_RENDER_CACHE_FLUSH (1<<2)
+#define MI_STATE_INSTRUCTION_CACHE_FLUSH (1<<1)
+#define MI_INVALIDATE_MAP_CACHE (1<<0)
+
+#define PRIM3D (CMD_3D | (0x1f<<24))
+#define PRIM3D_TRILIST (PRIM3D | (0x0<<18))
+#define PRIM3D_TRISTRIP (PRIM3D | (0x1<<18))
+#define PRIM3D_TRISTRIP_RVRSE (PRIM3D | (0x2<<18))
+#define PRIM3D_TRIFAN (PRIM3D | (0x3<<18))
+#define PRIM3D_POLY (PRIM3D | (0x4<<18))
+#define PRIM3D_LINELIST (PRIM3D | (0x5<<18))
+#define PRIM3D_LINESTRIP (PRIM3D | (0x6<<18))
+#define PRIM3D_RECTLIST (PRIM3D | (0x7<<18))
+#define PRIM3D_POINTLIST (PRIM3D | (0x8<<18))
+#define PRIM3D_DIB (PRIM3D | (0x9<<18))
+#define PRIM3D_CLEAR_RECT (PRIM3D | (0xa<<18))
+#define PRIM3D_ZONE_INIT (PRIM3D | (0xd<<18))
+#define PRIM3D_MASK (0x1f<<18)
+#define PRIM3D_INDIRECT_SEQUENTIAL ((1<<23) | (0<<17))
+#define PRIM3D_INDIRECT_ELTS ((1<<23) | (1<<17))
+
+/* p137 */
+#define _3DSTATE_AA_CMD (CMD_3D | (0x06<<24))
+#define AA_LINE_ECAAR_WIDTH_ENABLE (1<<16)
+#define AA_LINE_ECAAR_WIDTH_0_5 0
+#define AA_LINE_ECAAR_WIDTH_1_0 (1<<14)
+#define AA_LINE_ECAAR_WIDTH_2_0 (2<<14)
+#define AA_LINE_ECAAR_WIDTH_4_0 (3<<14)
+#define AA_LINE_REGION_WIDTH_ENABLE (1<<8)
+#define AA_LINE_REGION_WIDTH_0_5 0
+#define AA_LINE_REGION_WIDTH_1_0 (1<<6)
+#define AA_LINE_REGION_WIDTH_2_0 (2<<6)
+#define AA_LINE_REGION_WIDTH_4_0 (3<<6)
+
+/* 3DSTATE_BACKFACE_STENCIL_OPS, p138*/
+#define _3DSTATE_BACKFACE_STENCIL_OPS (CMD_3D | (0x8<<24))
+#define BFO_ENABLE_STENCIL_REF (1<<23)
+#define BFO_STENCIL_REF_SHIFT 15
+#define BFO_STENCIL_REF_MASK (0xff<<15)
+#define BFO_ENABLE_STENCIL_FUNCS (1<<14)
+#define BFO_STENCIL_TEST_SHIFT 11
+#define BFO_STENCIL_TEST_MASK (0x7<<11)
+#define BFO_STENCIL_FAIL_SHIFT 8
+#define BFO_STENCIL_FAIL_MASK (0x7<<8)
+#define BFO_STENCIL_PASS_Z_FAIL_SHIFT 5
+#define BFO_STENCIL_PASS_Z_FAIL_MASK (0x7<<5)
+#define BFO_STENCIL_PASS_Z_PASS_SHIFT 2
+#define BFO_STENCIL_PASS_Z_PASS_MASK (0x7<<2)
+#define BFO_ENABLE_STENCIL_TWO_SIDE (1<<1)
+#define BFO_STENCIL_TWO_SIDE (1<<0)
+
+/* 3DSTATE_BACKFACE_STENCIL_MASKS, p140 */
+#define _3DSTATE_BACKFACE_STENCIL_MASKS (CMD_3D | (0x9<<24))
+#define BFM_ENABLE_STENCIL_TEST_MASK (1<<17)
+#define BFM_ENABLE_STENCIL_WRITE_MASK (1<<16)
+#define BFM_STENCIL_TEST_MASK_SHIFT 8
+#define BFM_STENCIL_TEST_MASK_MASK (0xff<<8)
+#define BFM_STENCIL_WRITE_MASK_SHIFT 0
+#define BFM_STENCIL_WRITE_MASK_MASK (0xff<<0)
+
+/* 3DSTATE_BIN_CONTROL p141 */
+
+/* p143 */
+#define _3DSTATE_BUF_INFO_CMD (CMD_3D | (0x1d<<24) | (0x8e<<16) | 1)
+/* Dword 1 */
+#define BUF_3D_ID_COLOR_BACK (0x3<<24)
+#define BUF_3D_ID_DEPTH (0x7<<24)
+#define BUF_3D_USE_FENCE (1<<23)
+#define BUF_3D_TILED_SURFACE (1<<22)
+#define BUF_3D_TILE_WALK_X 0
+#define BUF_3D_TILE_WALK_Y (1<<21)
+#define BUF_3D_PITCH(x) (x)
+/* Dword 2 */
+#define BUF_3D_ADDR(x) ((x) & ~0x3)
+
+/* 3DSTATE_CHROMA_KEY */
+
+/* 3DSTATE_CLEAR_PARAMETERS, p150 */
+#define _3DSTATE_CLEAR_PARAMETERS (CMD_3D | (0x1d<<24) | (0x9c<<16) | 5)
+/* Dword 1 */
+#define CLEARPARAM_CLEAR_RECT (1 << 16)
+#define CLEARPARAM_ZONE_INIT (0 << 16)
+#define CLEARPARAM_WRITE_COLOR (1 << 2)
+#define CLEARPARAM_WRITE_DEPTH (1 << 1)
+#define CLEARPARAM_WRITE_STENCIL (1 << 0)
+
+/* 3DSTATE_CONSTANT_BLEND_COLOR, p153 */
+#define _3DSTATE_CONST_BLEND_COLOR_CMD (CMD_3D | (0x1d<<24) | (0x88<<16))
+
+/* 3DSTATE_COORD_SET_BINDINGS, p154 */
+#define _3DSTATE_COORD_SET_BINDINGS (CMD_3D | (0x16<<24))
+#define CSB_TCB(iunit, eunit) ((eunit)<<(iunit*3))
+
+/* p156 */
+#define _3DSTATE_DFLT_DIFFUSE_CMD (CMD_3D | (0x1d<<24) | (0x99<<16))
+
+/* p157 */
+#define _3DSTATE_DFLT_SPEC_CMD (CMD_3D | (0x1d<<24) | (0x9a<<16))
+
+/* p158 */
+#define _3DSTATE_DFLT_Z_CMD (CMD_3D | (0x1d<<24) | (0x98<<16))
+
+/* 3DSTATE_DEPTH_OFFSET_SCALE, p159 */
+#define _3DSTATE_DEPTH_OFFSET_SCALE (CMD_3D | (0x1d<<24) | (0x97<<16))
+/* scale in dword 1 */
+
+/* The depth subrectangle is not supported, but must be disabled. */
+/* 3DSTATE_DEPTH_SUBRECT_DISABLE, p160 */
+#define _3DSTATE_DEPTH_SUBRECT_DISABLE (CMD_3D | (0x1c<<24) | (0x11<<19) | (1 << 1) | (0 << 0))
+
+/* p161 */
+#define _3DSTATE_DST_BUF_VARS_CMD (CMD_3D | (0x1d<<24) | (0x85<<16))
+/* Dword 1 */
+#define TEX_DEFAULT_COLOR_OGL (0<<30)
+#define TEX_DEFAULT_COLOR_D3D (1<<30)
+#define ZR_EARLY_DEPTH (1<<29)
+#define LOD_PRECLAMP_OGL (1<<28)
+#define LOD_PRECLAMP_D3D (0<<28)
+#define DITHER_FULL_ALWAYS (0<<26)
+#define DITHER_FULL_ON_FB_BLEND (1<<26)
+#define DITHER_CLAMPED_ALWAYS (2<<26)
+#define LINEAR_GAMMA_BLEND_32BPP (1<<25)
+#define DEBUG_DISABLE_ENH_DITHER (1<<24)
+#define DSTORG_HORT_BIAS(x) ((x)<<20)
+#define DSTORG_VERT_BIAS(x) ((x)<<16)
+#define COLOR_4_2_2_CHNL_WRT_ALL 0
+#define COLOR_4_2_2_CHNL_WRT_Y (1<<12)
+#define COLOR_4_2_2_CHNL_WRT_CR (2<<12)
+#define COLOR_4_2_2_CHNL_WRT_CB (3<<12)
+#define COLOR_4_2_2_CHNL_WRT_CRCB (4<<12)
+#define COLR_BUF_8BIT 0
+#define COLR_BUF_RGB555 (1<<8)
+#define COLR_BUF_RGB565 (2<<8)
+#define COLR_BUF_ARGB8888 (3<<8)
+#define COLR_BUF_ARGB4444 (8<<8)
+#define COLR_BUF_ARGB1555 (9<<8)
+#define COLR_BUF_ARGB2AAA (0xa<<8)
+#define DEPTH_FRMT_16_FIXED 0
+#define DEPTH_FRMT_16_FLOAT (1<<2)
+#define DEPTH_FRMT_24_FIXED_8_OTHER (2<<2)
+#define VERT_LINE_STRIDE_1 (1<<1)
+#define VERT_LINE_STRIDE_0 (0<<1)
+#define VERT_LINE_STRIDE_OFS_1 1
+#define VERT_LINE_STRIDE_OFS_0 0
+
+/* p166 */
+#define _3DSTATE_DRAW_RECT_CMD (CMD_3D|(0x1d<<24)|(0x80<<16)|3)
+/* Dword 1 */
+#define DRAW_RECT_DIS_DEPTH_OFS (1<<30)
+#define DRAW_DITHER_OFS_X(x) ((x)<<26)
+#define DRAW_DITHER_OFS_Y(x) ((x)<<24)
+/* Dword 2 */
+#define DRAW_YMIN(x) ((x)<<16)
+#define DRAW_XMIN(x) (x)
+/* Dword 3 */
+#define DRAW_YMAX(x) ((x-1)<<16)
+#define DRAW_XMAX(x) (x-1)
+/* Dword 4 */
+#define DRAW_YORG(x) ((x)<<16)
+#define DRAW_XORG(x) (x)
+
+/* 3DSTATE_FILTER_COEFFICIENTS_4X4, p170 */
+
+/* 3DSTATE_FILTER_COEFFICIENTS_6X5, p172 */
+
+/* _3DSTATE_FOG_COLOR, p173 */
+#define _3DSTATE_FOG_COLOR_CMD (CMD_3D|(0x15<<24))
+#define FOG_COLOR_RED(x) ((x)<<16)
+#define FOG_COLOR_GREEN(x) ((x)<<8)
+#define FOG_COLOR_BLUE(x) (x)
+
+/* _3DSTATE_FOG_MODE, p174 */
+#define _3DSTATE_FOG_MODE_CMD (CMD_3D|(0x1d<<24)|(0x89<<16)|2)
+/* Dword 1 */
+#define FMC1_FOGFUNC_MODIFY_ENABLE (1<<31)
+#define FMC1_FOGFUNC_VERTEX (0<<28)
+#define FMC1_FOGFUNC_PIXEL_EXP (1<<28)
+#define FMC1_FOGFUNC_PIXEL_EXP2 (2<<28)
+#define FMC1_FOGFUNC_PIXEL_LINEAR (3<<28)
+#define FMC1_FOGFUNC_MASK (3<<28)
+#define FMC1_FOGINDEX_MODIFY_ENABLE (1<<27)
+#define FMC1_FOGINDEX_Z (0<<25)
+#define FMC1_FOGINDEX_W (1<<25)
+#define FMC1_C1_C2_MODIFY_ENABLE (1<<24)
+#define FMC1_DENSITY_MODIFY_ENABLE (1<<23)
+#define FMC1_C1_ONE (1<<13)
+#define FMC1_C1_MASK (0xffff<<4)
+/* Dword 2 */
+#define FMC2_C2_ONE (1<<16)
+/* Dword 3 */
+#define FMC3_D_ONE (1<<16)
+
+/* _3DSTATE_INDEPENDENT_ALPHA_BLEND, p177 */
+#define _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD (CMD_3D|(0x0b<<24))
+#define IAB_MODIFY_ENABLE (1<<23)
+#define IAB_ENABLE (1<<22)
+#define IAB_MODIFY_FUNC (1<<21)
+#define IAB_FUNC_SHIFT 16
+#define IAB_MODIFY_SRC_FACTOR (1<<11)
+#define IAB_SRC_FACTOR_SHIFT 6
+#define IAB_SRC_FACTOR_MASK (BLENDFACT_MASK<<6)
+#define IAB_MODIFY_DST_FACTOR (1<<5)
+#define IAB_DST_FACTOR_SHIFT 0
+#define IAB_DST_FACTOR_MASK (BLENDFACT_MASK<<0)
+
+#define BLENDFACT_ZERO 0x01
+#define BLENDFACT_ONE 0x02
+#define BLENDFACT_SRC_COLR 0x03
+#define BLENDFACT_INV_SRC_COLR 0x04
+#define BLENDFACT_SRC_ALPHA 0x05
+#define BLENDFACT_INV_SRC_ALPHA 0x06
+#define BLENDFACT_DST_ALPHA 0x07
+#define BLENDFACT_INV_DST_ALPHA 0x08
+#define BLENDFACT_DST_COLR 0x09
+#define BLENDFACT_INV_DST_COLR 0x0a
+#define BLENDFACT_SRC_ALPHA_SATURATE 0x0b
+#define BLENDFACT_CONST_COLOR 0x0c
+#define BLENDFACT_INV_CONST_COLOR 0x0d
+#define BLENDFACT_CONST_ALPHA 0x0e
+#define BLENDFACT_INV_CONST_ALPHA 0x0f
+#define BLENDFACT_MASK 0x0f
+
+#define BLENDFUNC_ADD 0x0
+#define BLENDFUNC_SUBTRACT 0x1
+#define BLENDFUNC_REVERSE_SUBTRACT 0x2
+#define BLENDFUNC_MIN 0x3
+#define BLENDFUNC_MAX 0x4
+#define BLENDFUNC_MASK 0x7
+
+/* 3DSTATE_LOAD_INDIRECT, p180 */
+
+#define _3DSTATE_LOAD_INDIRECT (CMD_3D|(0x1d<<24)|(0x7<<16))
+#define LI0_STATE_STATIC_INDIRECT (0x01<<8)
+#define LI0_STATE_DYNAMIC_INDIRECT (0x02<<8)
+#define LI0_STATE_SAMPLER (0x04<<8)
+#define LI0_STATE_MAP (0x08<<8)
+#define LI0_STATE_PROGRAM (0x10<<8)
+#define LI0_STATE_CONSTANTS (0x20<<8)
+
+#define SIS0_BUFFER_ADDRESS(x) ((x)&~0x3)
+#define SIS0_FORCE_LOAD (1<<1)
+#define SIS0_BUFFER_VALID (1<<0)
+#define SIS1_BUFFER_LENGTH(x) ((x)&0xff)
+
+#define DIS0_BUFFER_ADDRESS(x) ((x)&~0x3)
+#define DIS0_BUFFER_RESET (1<<1)
+#define DIS0_BUFFER_VALID (1<<0)
+
+#define SSB0_BUFFER_ADDRESS(x) ((x)&~0x3)
+#define SSB0_FORCE_LOAD (1<<1)
+#define SSB0_BUFFER_VALID (1<<0)
+#define SSB1_BUFFER_LENGTH(x) ((x)&0xff)
+
+#define MSB0_BUFFER_ADDRESS(x) ((x)&~0x3)
+#define MSB0_FORCE_LOAD (1<<1)
+#define MSB0_BUFFER_VALID (1<<0)
+#define MSB1_BUFFER_LENGTH(x) ((x)&0xff)
+
+#define PSP0_BUFFER_ADDRESS(x) ((x)&~0x3)
+#define PSP0_FORCE_LOAD (1<<1)
+#define PSP0_BUFFER_VALID (1<<0)
+#define PSP1_BUFFER_LENGTH(x) ((x)&0xff)
+
+#define PSC0_BUFFER_ADDRESS(x) ((x)&~0x3)
+#define PSC0_FORCE_LOAD (1<<1)
+#define PSC0_BUFFER_VALID (1<<0)
+#define PSC1_BUFFER_LENGTH(x) ((x)&0xff)
+
+/* _3DSTATE_RASTERIZATION_RULES */
+#define _3DSTATE_RASTER_RULES_CMD (CMD_3D|(0x07<<24))
+#define ENABLE_POINT_RASTER_RULE (1<<15)
+#define OGL_POINT_RASTER_RULE (1<<13)
+#define ENABLE_TEXKILL_3D_4D (1<<10)
+#define TEXKILL_3D (0<<9)
+#define TEXKILL_4D (1<<9)
+#define ENABLE_LINE_STRIP_PROVOKE_VRTX (1<<8)
+#define ENABLE_TRI_FAN_PROVOKE_VRTX (1<<5)
+#define LINE_STRIP_PROVOKE_VRTX(x) ((x)<<6)
+#define TRI_FAN_PROVOKE_VRTX(x) ((x)<<3)
+
+/* _3DSTATE_SCISSOR_ENABLE, p256 */
+#define _3DSTATE_SCISSOR_ENABLE_CMD (CMD_3D|(0x1c<<24)|(0x10<<19))
+#define ENABLE_SCISSOR_RECT ((1<<1) | 1)
+#define DISABLE_SCISSOR_RECT (1<<1)
+
+/* _3DSTATE_SCISSOR_RECTANGLE_0, p257 */
+#define _3DSTATE_SCISSOR_RECT_0_CMD (CMD_3D|(0x1d<<24)|(0x81<<16)|1)
+/* Dword 1 */
+#define SCISSOR_RECT_0_YMIN(x) ((x)<<16)
+#define SCISSOR_RECT_0_XMIN(x) (x)
+/* Dword 2 */
+#define SCISSOR_RECT_0_YMAX(x) ((x)<<16)
+#define SCISSOR_RECT_0_XMAX(x) (x)
+
+/* p189 */
+#define _3DSTATE_LOAD_STATE_IMMEDIATE_1 (CMD_3D | (0x1d<<24) | (0x04<<16))
+#define I1_LOAD_S(n) (1<<(4+n))
+
+#define S0_VB_OFFSET_MASK 0xffffffc
+#define S0_AUTO_CACHE_INV_DISABLE (1<<0)
+
+#define S1_VERTEX_WIDTH_SHIFT 24
+#define S1_VERTEX_WIDTH_MASK (0x3f<<24)
+#define S1_VERTEX_PITCH_SHIFT 16
+#define S1_VERTEX_PITCH_MASK (0x3f<<16)
+
+#define TEXCOORDFMT_2D 0x0
+#define TEXCOORDFMT_3D 0x1
+#define TEXCOORDFMT_4D 0x2
+#define TEXCOORDFMT_1D 0x3
+#define TEXCOORDFMT_2D_16 0x4
+#define TEXCOORDFMT_4D_16 0x5
+#define TEXCOORDFMT_NOT_PRESENT 0xf
+#define S2_TEXCOORD_FMT0_MASK 0xf
+#define S2_TEXCOORD_FMT1_SHIFT 4
+#define S2_TEXCOORD_FMT(unit, type) ((type)<<(unit*4))
+#define S2_TEXCOORD_NONE (~0U)
+
+#define TEXCOORD_WRAP_SHORTEST_TCX 8
+#define TEXCOORD_WRAP_SHORTEST_TCY 4
+#define TEXCOORD_WRAP_SHORTEST_TCZ 2
+#define TEXCOORD_PERSPECTIVE_DISABLE 1
+
+#define S3_WRAP_SHORTEST_TCX(unit) (TEXCOORD_WRAP_SHORTEST_TCX << ((unit) * 4))
+#define S3_WRAP_SHORTEST_TCY(unit) (TEXCOORD_WRAP_SHORTEST_TCY << ((unit) * 4))
+#define S3_WRAP_SHORTEST_TCZ(unit) (TEXCOORD_WRAP_SHORTEST_TCZ << ((unit) * 4))
+#define S3_PERSPECTIVE_DISABLE(unit) (TEXCOORD_PERSPECTIVE_DISABLE << ((unit) * 4))
+
+/* S3 not interesting */
+
+#define S4_POINT_WIDTH_SHIFT 23
+#define S4_POINT_WIDTH_MASK (0x1ff<<23)
+#define S4_LINE_WIDTH_SHIFT 19
+#define S4_LINE_WIDTH_ONE (0x2<<19)
+#define S4_LINE_WIDTH_MASK (0xf<<19)
+#define S4_FLATSHADE_ALPHA (1<<18)
+#define S4_FLATSHADE_FOG (1<<17)
+#define S4_FLATSHADE_SPECULAR (1<<16)
+#define S4_FLATSHADE_COLOR (1<<15)
+#define S4_CULLMODE_BOTH (0<<13)
+#define S4_CULLMODE_NONE (1<<13)
+#define S4_CULLMODE_CW (2<<13)
+#define S4_CULLMODE_CCW (3<<13)
+#define S4_CULLMODE_MASK (3<<13)
+#define S4_VFMT_POINT_WIDTH (1<<12)
+#define S4_VFMT_SPEC_FOG (1<<11)
+#define S4_VFMT_COLOR (1<<10)
+#define S4_VFMT_DEPTH_OFFSET (1<<9)
+#define S4_VFMT_XYZ (1<<6)
+#define S4_VFMT_XYZW (2<<6)
+#define S4_VFMT_XY (3<<6)
+#define S4_VFMT_XYW (4<<6)
+#define S4_VFMT_XYZW_MASK (7<<6)
+#define S4_FORCE_DEFAULT_DIFFUSE (1<<5)
+#define S4_FORCE_DEFAULT_SPECULAR (1<<4)
+#define S4_LOCAL_DEPTH_OFFSET_ENABLE (1<<3)
+#define S4_VFMT_FOG_PARAM (1<<2)
+#define S4_SPRITE_POINT_ENABLE (1<<1)
+#define S4_LINE_ANTIALIAS_ENABLE (1<<0)
+
+#define S4_VFMT_MASK (S4_VFMT_POINT_WIDTH | \
+ S4_VFMT_SPEC_FOG | \
+ S4_VFMT_COLOR | \
+ S4_VFMT_DEPTH_OFFSET | \
+ S4_VFMT_XYZW_MASK | \
+ S4_VFMT_FOG_PARAM)
+
+#define S5_WRITEDISABLE_ALPHA (1<<31)
+#define S5_WRITEDISABLE_RED (1<<30)
+#define S5_WRITEDISABLE_GREEN (1<<29)
+#define S5_WRITEDISABLE_BLUE (1<<28)
+#define S5_WRITEDISABLE_MASK (0xf<<28)
+#define S5_FORCE_DEFAULT_POINT_SIZE (1<<27)
+#define S5_LAST_PIXEL_ENABLE (1<<26)
+#define S5_GLOBAL_DEPTH_OFFSET_ENABLE (1<<25)
+#define S5_FOG_ENABLE (1<<24)
+#define S5_STENCIL_REF_SHIFT 16
+#define S5_STENCIL_REF_MASK (0xff<<16)
+#define S5_STENCIL_TEST_FUNC_SHIFT 13
+#define S5_STENCIL_TEST_FUNC_MASK (0x7<<13)
+#define S5_STENCIL_FAIL_SHIFT 10
+#define S5_STENCIL_FAIL_MASK (0x7<<10)
+#define S5_STENCIL_PASS_Z_FAIL_SHIFT 7
+#define S5_STENCIL_PASS_Z_FAIL_MASK (0x7<<7)
+#define S5_STENCIL_PASS_Z_PASS_SHIFT 4
+#define S5_STENCIL_PASS_Z_PASS_MASK (0x7<<4)
+#define S5_STENCIL_WRITE_ENABLE (1<<3)
+#define S5_STENCIL_TEST_ENABLE (1<<2)
+#define S5_COLOR_DITHER_ENABLE (1<<1)
+#define S5_LOGICOP_ENABLE (1<<0)
+
+#define COMPAREFUNC_ALWAYS 0
+#define COMPAREFUNC_NEVER 0x1
+#define COMPAREFUNC_LESS 0x2
+#define COMPAREFUNC_EQUAL 0x3
+#define COMPAREFUNC_LEQUAL 0x4
+#define COMPAREFUNC_GREATER 0x5
+#define COMPAREFUNC_NOTEQUAL 0x6
+#define COMPAREFUNC_GEQUAL 0x7
+
+#define STENCILOP_KEEP 0
+#define STENCILOP_ZERO 0x1
+#define STENCILOP_REPLACE 0x2
+#define STENCILOP_INCRSAT 0x3
+#define STENCILOP_DECRSAT 0x4
+#define STENCILOP_INCR 0x5
+#define STENCILOP_DECR 0x6
+#define STENCILOP_INVERT 0x7
+
+#define S6_ALPHA_TEST_ENABLE (1<<31)
+#define S6_ALPHA_TEST_FUNC_SHIFT 28
+#define S6_ALPHA_TEST_FUNC_MASK (0x7<<28)
+#define S6_ALPHA_REF_SHIFT 20
+#define S6_ALPHA_REF_MASK (0xff<<20)
+#define S6_DEPTH_TEST_ENABLE (1<<19)
+#define S6_DEPTH_TEST_FUNC_SHIFT 16
+#define S6_DEPTH_TEST_FUNC_MASK (0x7<<16)
+#define S6_CBUF_BLEND_ENABLE (1<<15)
+#define S6_CBUF_BLEND_FUNC_SHIFT 12
+#define S6_CBUF_BLEND_FUNC_MASK (0x7<<12)
+#define S6_CBUF_SRC_BLEND_FACT_SHIFT 8
+#define S6_CBUF_SRC_BLEND_FACT_MASK (0xf<<8)
+#define S6_CBUF_DST_BLEND_FACT_SHIFT 4
+#define S6_CBUF_DST_BLEND_FACT_MASK (0xf<<4)
+#define S6_DEPTH_WRITE_ENABLE (1<<3)
+#define S6_COLOR_WRITE_ENABLE (1<<2)
+#define S6_TRISTRIP_PV_SHIFT 0
+#define S6_TRISTRIP_PV_MASK (0x3<<0)
+
+#define S7_DEPTH_OFFSET_CONST_MASK ~0
+
+/* 3DSTATE_MAP_DEINTERLACER_PARAMETERS */
+/* 3DSTATE_MAP_PALETTE_LOAD_32, p206 */
+
+/* _3DSTATE_MODES_4, p218 */
+#define _3DSTATE_MODES_4_CMD (CMD_3D|(0x0d<<24))
+#define ENABLE_LOGIC_OP_FUNC (1<<23)
+#define LOGIC_OP_FUNC(x) ((x)<<18)
+#define LOGICOP_MASK (0xf<<18)
+#define LOGICOP_COPY 0xc
+#define MODE4_ENABLE_STENCIL_TEST_MASK ((1<<17)|(0xff00))
+#define ENABLE_STENCIL_TEST_MASK (1<<17)
+#define STENCIL_TEST_MASK(x) ((x)<<8)
+#define MODE4_ENABLE_STENCIL_WRITE_MASK ((1<<16)|(0x00ff))
+#define ENABLE_STENCIL_WRITE_MASK (1<<16)
+#define STENCIL_WRITE_MASK(x) ((x)&0xff)
+
+/* _3DSTATE_MODES_5, p220 */
+#define _3DSTATE_MODES_5_CMD (CMD_3D|(0x0c<<24))
+#define PIPELINE_FLUSH_RENDER_CACHE (1<<18)
+#define PIPELINE_FLUSH_TEXTURE_CACHE (1<<16)
+
+/* p221 */
+#define _3DSTATE_PIXEL_SHADER_CONSTANTS (CMD_3D|(0x1d<<24)|(0x6<<16))
+#define PS1_REG(n) (1<<(n))
+#define PS2_CONST_X(n) (n)
+#define PS3_CONST_Y(n) (n)
+#define PS4_CONST_Z(n) (n)
+#define PS5_CONST_W(n) (n)
+
+/* p222 */
+
+#define I915_MAX_TEX_INDIRECT 4
+#define I915_MAX_TEX_INSN 32
+#define I915_MAX_ALU_INSN 64
+#define I915_MAX_DECL_INSN 27
+#define I915_MAX_TEMPORARY 16
+
+/* Each instruction is 3 dwords long, though most don't require all
+ * this space. Maximum of 123 instructions. Smaller maxes per insn
+ * type.
+ */
+#define _3DSTATE_PIXEL_SHADER_PROGRAM (CMD_3D|(0x1d<<24)|(0x5<<16))
+
+#define REG_TYPE_R 0 /* temporary regs, no need to
+ * dcl, must be written before
+ * read -- Preserved between
+ * phases.
+ */
+#define REG_TYPE_T 1 /* Interpolated values, must be
+ * dcl'ed before use.
+ *
+ * 0..7: texture coord,
+ * 8: diffuse spec,
+ * 9: specular color,
+ * 10: fog parameter in w.
+ */
+#define REG_TYPE_CONST 2 /* Restriction: only one const
+ * can be referenced per
+ * instruction, though it may be
+ * selected for multiple inputs.
+ * Constants not initialized
+ * default to zero.
+ */
+#define REG_TYPE_S 3 /* sampler */
+#define REG_TYPE_OC 4 /* output color (rgba) */
+#define REG_TYPE_OD 5 /* output depth (w), xyz are
+ * temporaries. If not written,
+ * interpolated depth is used?
+ */
+#define REG_TYPE_U 6 /* unpreserved temporaries */
+#define REG_TYPE_MASK 0x7
+#define REG_NR_MASK 0xf
+
+/* REG_TYPE_T:
+ */
+#define T_TEX0 0
+#define T_TEX1 1
+#define T_TEX2 2
+#define T_TEX3 3
+#define T_TEX4 4
+#define T_TEX5 5
+#define T_TEX6 6
+#define T_TEX7 7
+#define T_DIFFUSE 8
+#define T_SPECULAR 9
+#define T_FOG_W 10 /* interpolated fog is in W coord */
+
+/* Arithmetic instructions */
+
+/* .replicate_swizzle == selection and replication of a particular
+ * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww
+ */
+#define A0_NOP (0x0<<24) /* no operation */
+#define A0_ADD (0x1<<24) /* dst = src0 + src1 */
+#define A0_MOV (0x2<<24) /* dst = src0 */
+#define A0_MUL (0x3<<24) /* dst = src0 * src1 */
+#define A0_MAD (0x4<<24) /* dst = src0 * src1 + src2 */
+#define A0_DP2ADD (0x5<<24) /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */
+#define A0_DP3 (0x6<<24) /* dst.xyzw = src0.xyz dot src1.xyz */
+#define A0_DP4 (0x7<<24) /* dst.xyzw = src0.xyzw dot src1.xyzw */
+#define A0_FRC (0x8<<24) /* dst = src0 - floor(src0) */
+#define A0_RCP (0x9<<24) /* dst.xyzw = 1/(src0.replicate_swizzle) */
+#define A0_RSQ (0xa<<24) /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */
+#define A0_EXP (0xb<<24) /* dst.xyzw = exp2(src0.replicate_swizzle) */
+#define A0_LOG (0xc<<24) /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */
+#define A0_CMP (0xd<<24) /* dst = (src0 >= 0.0) ? src1 : src2 */
+#define A0_MIN (0xe<<24) /* dst = (src0 < src1) ? src0 : src1 */
+#define A0_MAX (0xf<<24) /* dst = (src0 >= src1) ? src0 : src1 */
+#define A0_FLR (0x10<<24) /* dst = floor(src0) */
+#define A0_MOD (0x11<<24) /* dst = src0 fmod 1.0 */
+#define A0_TRC (0x12<<24) /* dst = int(src0) */
+#define A0_SGE (0x13<<24) /* dst = src0 >= src1 ? 1.0 : 0.0 */
+#define A0_SLT (0x14<<24) /* dst = src0 < src1 ? 1.0 : 0.0 */
+#define A0_DEST_SATURATE (1<<22)
+#define A0_DEST_TYPE_SHIFT 19
+/* Allow: R, OC, OD, U */
+#define A0_DEST_NR_SHIFT 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define A0_DEST_CHANNEL_X (1<<10)
+#define A0_DEST_CHANNEL_Y (2<<10)
+#define A0_DEST_CHANNEL_Z (4<<10)
+#define A0_DEST_CHANNEL_W (8<<10)
+#define A0_DEST_CHANNEL_ALL (0xf<<10)
+#define A0_DEST_CHANNEL_SHIFT 10
+#define A0_SRC0_TYPE_SHIFT 7
+#define A0_SRC0_NR_SHIFT 2
+
+#define A0_DEST_CHANNEL_XY (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y)
+#define A0_DEST_CHANNEL_XYZ (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z)
+
+#define SRC_X 0
+#define SRC_Y 1
+#define SRC_Z 2
+#define SRC_W 3
+#define SRC_ZERO 4
+#define SRC_ONE 5
+
+#define A1_SRC0_CHANNEL_X_NEGATE (1<<31)
+#define A1_SRC0_CHANNEL_X_SHIFT 28
+#define A1_SRC0_CHANNEL_Y_NEGATE (1<<27)
+#define A1_SRC0_CHANNEL_Y_SHIFT 24
+#define A1_SRC0_CHANNEL_Z_NEGATE (1<<23)
+#define A1_SRC0_CHANNEL_Z_SHIFT 20
+#define A1_SRC0_CHANNEL_W_NEGATE (1<<19)
+#define A1_SRC0_CHANNEL_W_SHIFT 16
+#define A1_SRC1_TYPE_SHIFT 13
+#define A1_SRC1_NR_SHIFT 8
+#define A1_SRC1_CHANNEL_X_NEGATE (1<<7)
+#define A1_SRC1_CHANNEL_X_SHIFT 4
+#define A1_SRC1_CHANNEL_Y_NEGATE (1<<3)
+#define A1_SRC1_CHANNEL_Y_SHIFT 0
+
+#define A2_SRC1_CHANNEL_Z_NEGATE (1<<31)
+#define A2_SRC1_CHANNEL_Z_SHIFT 28
+#define A2_SRC1_CHANNEL_W_NEGATE (1<<27)
+#define A2_SRC1_CHANNEL_W_SHIFT 24
+#define A2_SRC2_TYPE_SHIFT 21
+#define A2_SRC2_NR_SHIFT 16
+#define A2_SRC2_CHANNEL_X_NEGATE (1<<15)
+#define A2_SRC2_CHANNEL_X_SHIFT 12
+#define A2_SRC2_CHANNEL_Y_NEGATE (1<<11)
+#define A2_SRC2_CHANNEL_Y_SHIFT 8
+#define A2_SRC2_CHANNEL_Z_NEGATE (1<<7)
+#define A2_SRC2_CHANNEL_Z_SHIFT 4
+#define A2_SRC2_CHANNEL_W_NEGATE (1<<3)
+#define A2_SRC2_CHANNEL_W_SHIFT 0
+
+/* Texture instructions */
+#define T0_TEXLD (0x15<<24) /* Sample texture using predeclared
+ * sampler and address, and output
+ * filtered texel data to destination
+ * register */
+#define T0_TEXLDP (0x16<<24) /* Same as texld but performs a
+ * perspective divide of the texture
+ * coordinate .xyz values by .w before
+ * sampling. */
+#define T0_TEXLDB (0x17<<24) /* Same as texld but biases the
+ * computed LOD by w. Only S4.6 two's
+ * comp is used. This implies that a
+ * float to fixed conversion is
+ * done. */
+#define T0_TEXKILL (0x18<<24) /* Does not perform a sampling
+ * operation. Simply kills the pixel
+ * if any channel of the address
+ * register is < 0.0. */
+#define T0_DEST_TYPE_SHIFT 19
+/* Allow: R, OC, OD, U */
+/* Note: U (unpreserved) regs do not retain their values between
+ * phases (cannot be used for feedback)
+ *
+ * Note: oC and OD registers can only be used as the destination of a
+ * texture instruction once per phase (this is an implementation
+ * restriction).
+ */
+#define T0_DEST_NR_SHIFT 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define T0_SAMPLER_NR_SHIFT 0 /* This field ignored for TEXKILL */
+#define T0_SAMPLER_NR_MASK (0xf<<0)
+
+#define T1_ADDRESS_REG_TYPE_SHIFT 24 /* Reg to use as texture coord */
+/* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */
+#define T1_ADDRESS_REG_NR_SHIFT 17
+#define T2_MBZ 0
+
+/* Declaration instructions */
+#define D0_DCL (0x19<<24) /* Declare a t (interpolated attrib)
+ * register or an s (sampler)
+ * register. */
+#define D0_SAMPLE_TYPE_SHIFT 22
+#define D0_SAMPLE_TYPE_2D (0x0<<22)
+#define D0_SAMPLE_TYPE_CUBE (0x1<<22)
+#define D0_SAMPLE_TYPE_VOLUME (0x2<<22)
+#define D0_SAMPLE_TYPE_MASK (0x3<<22)
+
+#define D0_TYPE_SHIFT 19
+/* Allow: T, S */
+#define D0_NR_SHIFT 14
+/* Allow T: 0..10, S: 0..15 */
+#define D0_CHANNEL_X (1<<10)
+#define D0_CHANNEL_Y (2<<10)
+#define D0_CHANNEL_Z (4<<10)
+#define D0_CHANNEL_W (8<<10)
+#define D0_CHANNEL_ALL (0xf<<10)
+#define D0_CHANNEL_NONE (0<<10)
+
+#define D0_CHANNEL_XY (D0_CHANNEL_X|D0_CHANNEL_Y)
+#define D0_CHANNEL_XYZ (D0_CHANNEL_XY|D0_CHANNEL_Z)
+
+/* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse
+ * or specular declarations.
+ *
+ * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw)
+ *
+ * Must be zero for S (sampler) dcls
+ */
+#define D1_MBZ 0
+#define D2_MBZ 0
+
+/* p207.
+ * The DWORD count is 3 times the number of bits set in MS1_MAPMASK_MASK
+ */
+#define _3DSTATE_MAP_STATE (CMD_3D|(0x1d<<24)|(0x0<<16))
+
+#define MS1_MAPMASK_SHIFT 0
+#define MS1_MAPMASK_MASK (0x8fff<<0)
+
+#define MS2_UNTRUSTED_SURFACE (1<<31)
+#define MS2_ADDRESS_MASK 0xfffffffc
+#define MS2_VERTICAL_LINE_STRIDE (1<<1)
+#define MS2_VERTICAL_OFFSET (1<<1)
+
+#define MS3_HEIGHT_SHIFT 21
+#define MS3_WIDTH_SHIFT 10
+#define MS3_PALETTE_SELECT (1<<9)
+#define MS3_MAPSURF_FORMAT_SHIFT 7
+#define MS3_MAPSURF_FORMAT_MASK (0x7<<7)
+#define MAPSURF_8BIT (1<<7)
+#define MAPSURF_16BIT (2<<7)
+#define MAPSURF_32BIT (3<<7)
+#define MAPSURF_422 (5<<7)
+#define MAPSURF_COMPRESSED (6<<7)
+#define MAPSURF_4BIT_INDEXED (7<<7)
+#define MS3_MT_FORMAT_MASK (0x7 << 3)
+#define MS3_MT_FORMAT_SHIFT 3
+#define MT_4BIT_IDX_ARGB8888 (7<<3) /* SURFACE_4BIT_INDEXED */
+#define MT_8BIT_I8 (0<<3) /* SURFACE_8BIT */
+#define MT_8BIT_L8 (1<<3)
+#define MT_8BIT_A8 (4<<3)
+#define MT_8BIT_MONO8 (5<<3)
+#define MT_16BIT_RGB565 (0<<3) /* SURFACE_16BIT */
+#define MT_16BIT_ARGB1555 (1<<3)
+#define MT_16BIT_ARGB4444 (2<<3)
+#define MT_16BIT_AY88 (3<<3)
+#define MT_16BIT_88DVDU (5<<3)
+#define MT_16BIT_BUMP_655LDVDU (6<<3)
+#define MT_16BIT_I16 (7<<3)
+#define MT_16BIT_L16 (8<<3)
+#define MT_16BIT_A16 (9<<3)
+#define MT_32BIT_ARGB8888 (0<<3) /* SURFACE_32BIT */
+#define MT_32BIT_ABGR8888 (1<<3)
+#define MT_32BIT_XRGB8888 (2<<3)
+#define MT_32BIT_XBGR8888 (3<<3)
+#define MT_32BIT_QWVU8888 (4<<3)
+#define MT_32BIT_AXVU8888 (5<<3)
+#define MT_32BIT_LXVU8888 (6<<3)
+#define MT_32BIT_XLVU8888 (7<<3)
+#define MT_32BIT_ARGB2101010 (8<<3)
+#define MT_32BIT_ABGR2101010 (9<<3)
+#define MT_32BIT_AWVU2101010 (0xA<<3)
+#define MT_32BIT_GR1616 (0xB<<3)
+#define MT_32BIT_VU1616 (0xC<<3)
+#define MT_32BIT_xI824 (0xD<<3)
+#define MT_32BIT_xA824 (0xE<<3)
+#define MT_32BIT_xL824 (0xF<<3)
+#define MT_422_YCRCB_SWAPY (0<<3) /* SURFACE_422 */
+#define MT_422_YCRCB_NORMAL (1<<3)
+#define MT_422_YCRCB_SWAPUV (2<<3)
+#define MT_422_YCRCB_SWAPUVY (3<<3)
+#define MT_COMPRESS_DXT1 (0<<3) /* SURFACE_COMPRESSED */
+#define MT_COMPRESS_DXT2_3 (1<<3)
+#define MT_COMPRESS_DXT4_5 (2<<3)
+#define MT_COMPRESS_FXT1 (3<<3)
+#define MT_COMPRESS_DXT1_RGB (4<<3)
+#define MS3_USE_FENCE_REGS (1<<2)
+#define MS3_TILED_SURFACE (1<<1)
+#define MS3_TILE_WALK (1<<0)
+
+/* The pitch is the pitch measured in DWORDS, minus 1 */
+#define MS4_PITCH_SHIFT 21
+#define MS4_CUBE_FACE_ENA_NEGX (1<<20)
+#define MS4_CUBE_FACE_ENA_POSX (1<<19)
+#define MS4_CUBE_FACE_ENA_NEGY (1<<18)
+#define MS4_CUBE_FACE_ENA_POSY (1<<17)
+#define MS4_CUBE_FACE_ENA_NEGZ (1<<16)
+#define MS4_CUBE_FACE_ENA_POSZ (1<<15)
+#define MS4_CUBE_FACE_ENA_MASK (0x3f<<15)
+#define MS4_MAX_LOD_SHIFT 9
+#define MS4_MAX_LOD_MASK (0x3f<<9)
+#define MS4_MIP_LAYOUT_LEGACY (0<<8)
+#define MS4_MIP_LAYOUT_BELOW_LPT (0<<8)
+#define MS4_MIP_LAYOUT_RIGHT_LPT (1<<8)
+#define MS4_VOLUME_DEPTH_SHIFT 0
+#define MS4_VOLUME_DEPTH_MASK (0xff<<0)
+
+/* p244.
+ * The DWORD count is 3 times the number of bits set in SS1_MAPMASK_MASK.
+ */
+#define _3DSTATE_SAMPLER_STATE (CMD_3D|(0x1d<<24)|(0x1<<16))
+
+#define SS1_MAPMASK_SHIFT 0
+#define SS1_MAPMASK_MASK (0x8fff<<0)
+
+#define SS2_REVERSE_GAMMA_ENABLE (1<<31)
+#define SS2_PACKED_TO_PLANAR_ENABLE (1<<30)
+#define SS2_COLORSPACE_CONVERSION (1<<29)
+#define SS2_CHROMAKEY_SHIFT 27
+#define SS2_BASE_MIP_LEVEL_SHIFT 22
+#define SS2_BASE_MIP_LEVEL_MASK (0x1f<<22)
+#define SS2_MIP_FILTER_SHIFT 20
+#define SS2_MIP_FILTER_MASK (0x3<<20)
+#define MIPFILTER_NONE 0
+#define MIPFILTER_NEAREST 1
+#define MIPFILTER_LINEAR 3
+#define SS2_MAG_FILTER_SHIFT 17
+#define SS2_MAG_FILTER_MASK (0x7<<17)
+#define FILTER_NEAREST 0
+#define FILTER_LINEAR 1
+#define FILTER_ANISOTROPIC 2
+#define FILTER_4X4_1 3
+#define FILTER_4X4_2 4
+#define FILTER_4X4_FLAT 5
+#define FILTER_6X5_MONO 6 /* XXX - check */
+#define SS2_MIN_FILTER_SHIFT 14
+#define SS2_MIN_FILTER_MASK (0x7<<14)
+#define SS2_LOD_BIAS_SHIFT 5
+#define SS2_LOD_BIAS_ONE (0x10<<5)
+#define SS2_LOD_BIAS_MASK (0x1ff<<5)
+/* Shadow requires:
+ * MT_X8{I,L,A}24 or MT_{I,L,A}16 texture format
+ * FILTER_4X4_x MIN and MAG filters
+ */
+#define SS2_SHADOW_ENABLE (1<<4)
+#define SS2_MAX_ANISO_MASK (1<<3)
+#define SS2_MAX_ANISO_2 (0<<3)
+#define SS2_MAX_ANISO_4 (1<<3)
+#define SS2_SHADOW_FUNC_SHIFT 0
+#define SS2_SHADOW_FUNC_MASK (0x7<<0)
+/* SS2_SHADOW_FUNC values: see COMPAREFUNC_* */
+
+#define SS3_MIN_LOD_SHIFT 24
+#define SS3_MIN_LOD_ONE (0x10<<24)
+#define SS3_MIN_LOD_MASK (0xff<<24)
+#define SS3_KILL_PIXEL_ENABLE (1<<17)
+#define SS3_TCX_ADDR_MODE_SHIFT 12
+#define SS3_TCX_ADDR_MODE_MASK (0x7<<12)
+#define TEXCOORDMODE_WRAP 0
+#define TEXCOORDMODE_MIRROR 1
+#define TEXCOORDMODE_CLAMP_EDGE 2
+#define TEXCOORDMODE_CUBE 3
+#define TEXCOORDMODE_CLAMP_BORDER 4
+#define TEXCOORDMODE_MIRROR_ONCE 5
+#define SS3_TCY_ADDR_MODE_SHIFT 9
+#define SS3_TCY_ADDR_MODE_MASK (0x7<<9)
+#define SS3_TCZ_ADDR_MODE_SHIFT 6
+#define SS3_TCZ_ADDR_MODE_MASK (0x7<<6)
+#define SS3_NORMALIZED_COORDS (1<<5)
+#define SS3_TEXTUREMAP_INDEX_SHIFT 1
+#define SS3_TEXTUREMAP_INDEX_MASK (0xf<<1)
+#define SS3_DEINTERLACER_ENABLE (1<<0)
+
+#define SS4_BORDER_COLOR_MASK (~0)
+
+/* 3DSTATE_SPAN_STIPPLE, p258
+ */
+#define _3DSTATE_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16))
+#define ST1_ENABLE (1<<16)
+#define ST1_MASK (0xffff)
+
+#define FLUSH_MAP_CACHE (1<<0)
+#define FLUSH_RENDER_CACHE (1<<1)
+
+/* BLT commands */
+#define COLOR_BLT_CMD (CMD_2D | (0x40 << 22) | 3)
+#define XY_COLOR_BLT_CMD (CMD_2D | (0x50 << 22) | 4)
+#define XY_SETUP_CLIP_BLT_CMD (CMD_2D | (0x03 << 22) | 1)
+#define XY_SRC_COPY_BLT_CMD (CMD_2D | (0x53 << 22) | 6)
+#define SRC_COPY_BLT_CMD (CMD_2D | (0x43 << 22) | 4)
+
+#define XY_MONO_PAT_BLT_CMD (CMD_2D | (0x52<<22)|0x7)
+#define XY_MONO_PAT_VERT_SEED ((1<<10) | (1<<9)|(1<<8))
+#define XY_MONO_PAT_HORT_SEED ((1<<14) | (1<<13)|(1<<12))
+#define XY_MONO_SRC_BLT_CMD (CMD_2D | (0x54<<22)|(0x6))
+
+#define XY_SETUP_BLT_CMD (CMD_2D | (0x01 << 22) | 6)
+#define XY_TEXT_IMMEDIATE_BLIT_CMD (CMD_2D | (0x31 << 22))
+#define XY_TEXT_BYTE_PACKED (1 << 16)
+
+/* BR00 */
+#define XY_BLT_WRITE_ALPHA (1 << 21)
+#define XY_BLT_WRITE_RGB (1 << 20)
+#define XY_SRC_TILED (1 << 15)
+#define XY_DST_TILED (1 << 11)
+
+/* BR13 */
+#define BR13_565 (0x1 << 24)
+#define BR13_8888 (0x3 << 24)
+
+#endif /* CAIRO_DRM_INTEL_COMMAND_PRIVATE_H */
diff --git a/src/drm/cairo-drm-intel-debug.c b/src/drm/cairo-drm-intel-debug.c
new file mode 100755
index 000000000..7068c933e
--- /dev/null
+++ b/src/drm/cairo-drm-intel-debug.c
@@ -0,0 +1,1209 @@
+/**************************************************************************
+ *
+ * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "cairoint.h"
+#include "cairo-drm-intel-private.h"
+
+struct debug_stream {
+ unsigned offset; /* current gtt offset */
+ const char *ptr; /* pointer to gtt offset zero */
+ const char *end; /* pointer to gtt offset zero */
+};
+
+static cairo_bool_t
+debug (struct debug_stream *stream, const char *name, uint32_t len)
+{
+ uint32_t i;
+ const uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+
+ if (len == 0) {
+ fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]);
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+
+ fprintf (stderr, "%04x: ", stream->offset);
+
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ for (i = 0; i < len; i++)
+ fprintf (stderr, "\t0x%08x\n", ptr[i]);
+ fprintf (stderr, "\n");
+
+ stream->offset += len * sizeof(uint32_t);
+ return TRUE;
+}
+
+
+static const char *
+get_prim_name (uint32_t val)
+{
+ switch (val & PRIM3D_MASK) {
+ case PRIM3D_TRILIST: return "TRILIST";
+ case PRIM3D_TRISTRIP: return "TRISTRIP";
+ case PRIM3D_TRISTRIP_RVRSE: return "TRISTRIP_RVRSE";
+ case PRIM3D_TRIFAN: return "TRIFAN";
+ case PRIM3D_POLY: return "POLY";
+ case PRIM3D_LINELIST: return "LINELIST";
+ case PRIM3D_LINESTRIP: return "LINESTRIP";
+ case PRIM3D_RECTLIST: return "RECTLIST";
+ case PRIM3D_POINTLIST: return "POINTLIST";
+ case PRIM3D_DIB: return "DIB";
+ case PRIM3D_CLEAR_RECT: return "CLEAR_RECT";
+ case PRIM3D_ZONE_INIT: return "ZONE_INIT";
+ default: return "????";
+ }
+}
+
+static cairo_bool_t
+debug_prim (struct debug_stream *stream,
+ const char *name,
+ cairo_bool_t dump_floats,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ const char *prim = get_prim_name( ptr[0] );
+ uint32_t i;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s %s (%d dwords):\n", name, prim, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[0]);
+ for (i = 1; i < len; i++) {
+ if (dump_floats)
+ fprintf (stderr, "\t0x%08x // %f\n", ptr[i], *(float *)&ptr[i]);
+ else
+ fprintf (stderr, "\t0x%08x\n", ptr[i]);
+ }
+
+ fprintf (stderr, "\n");
+
+ stream->offset += len * sizeof(uint32_t);
+ return TRUE;
+}
+
+static const char *opcodes[] = {
+ "NOP",
+ "ADD",
+ "MOV",
+ "MUL",
+ "MAD",
+ "DP2ADD",
+ "DP3",
+ "DP4",
+ "FRC",
+ "RCP",
+ "RSQ",
+ "EXP",
+ "LOG",
+ "CMP",
+ "MIN",
+ "MAX",
+ "FLR",
+ "MOD",
+ "TRC",
+ "SGE",
+ "SLT",
+ "TEXLD",
+ "TEXLDP",
+ "TEXLDB",
+ "TEXKILL",
+ "DCL",
+ "0x1a",
+ "0x1b",
+ "0x1c",
+ "0x1d",
+ "0x1e",
+ "0x1f",
+};
+
+static const int args[] = {
+ 0, /* 0 nop */
+ 2, /* 1 add */
+ 1, /* 2 mov */
+ 2, /* 3 m ul */
+ 3, /* 4 mad */
+ 3, /* 5 dp2add */
+ 2, /* 6 dp3 */
+ 2, /* 7 dp4 */
+ 1, /* 8 frc */
+ 1, /* 9 rcp */
+ 1, /* a rsq */
+ 1, /* b exp */
+ 1, /* c log */
+ 3, /* d cmp */
+ 2, /* e min */
+ 2, /* f max */
+ 1, /* 10 flr */
+ 1, /* 11 mod */
+ 1, /* 12 trc */
+ 2, /* 13 sge */
+ 2, /* 14 slt */
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+static const char *regname[] = {
+ "R",
+ "T",
+ "CONST",
+ "S",
+ "OC",
+ "OD",
+ "U",
+ "UNKNOWN",
+};
+
+static void
+print_reg_type_nr(uint32_t type, uint32_t nr)
+{
+ switch (type) {
+ case REG_TYPE_T:
+ switch (nr) {
+ case T_DIFFUSE:
+ fprintf (stderr, "T_DIFFUSE");
+ return;
+ case T_SPECULAR:
+ fprintf (stderr, "T_SPECULAR");
+ return;
+ case T_FOG_W:
+ fprintf (stderr, "T_FOG_W");
+ return;
+ default:
+ fprintf (stderr, "T_TEX%d", nr);
+ return;
+ }
+ case REG_TYPE_OC:
+ if (nr == 0) {
+ fprintf (stderr, "oC");
+ return;
+ }
+ break;
+ case REG_TYPE_OD:
+ if (nr == 0) {
+ fprintf (stderr, "oD");
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ fprintf (stderr, "%s[%d]", regname[type], nr);
+}
+
+#define REG_SWIZZLE_MASK 0x7777
+#define REG_NEGATE_MASK 0x8888
+
+#define REG_SWIZZLE_XYZW ((SRC_X << A2_SRC2_CHANNEL_X_SHIFT) | \
+ (SRC_Y << A2_SRC2_CHANNEL_Y_SHIFT) | \
+ (SRC_Z << A2_SRC2_CHANNEL_Z_SHIFT) | \
+ (SRC_W << A2_SRC2_CHANNEL_W_SHIFT))
+
+static void
+print_reg_neg_swizzle(uint32_t reg)
+{
+ int i;
+
+ if ((reg & REG_SWIZZLE_MASK) == REG_SWIZZLE_XYZW &&
+ (reg & REG_NEGATE_MASK) == 0)
+ return;
+
+ fprintf (stderr, ".");
+
+ for (i = 12; i >= 0; i -= 4) {
+ if (reg & (8 << i))
+ fprintf (stderr, "-");
+
+ switch ((reg >> i) & 0x7) {
+ case 0:
+ fprintf (stderr, "x");
+ break;
+ case 1:
+ fprintf (stderr, "y");
+ break;
+ case 2:
+ fprintf (stderr, "z");
+ break;
+ case 3:
+ fprintf (stderr, "w");
+ break;
+ case 4:
+ fprintf (stderr, "0");
+ break;
+ case 5:
+ fprintf (stderr, "1");
+ break;
+ default:
+ fprintf (stderr, "?");
+ break;
+ }
+ }
+}
+
+static void
+print_src_reg(uint32_t dword)
+{
+ uint32_t nr = (dword >> A2_SRC2_NR_SHIFT) & REG_NR_MASK;
+ uint32_t type = (dword >> A2_SRC2_TYPE_SHIFT) & REG_TYPE_MASK;
+ print_reg_type_nr(type, nr);
+ print_reg_neg_swizzle(dword);
+}
+
+static void
+print_dest_reg(uint32_t dword)
+{
+ uint32_t nr = (dword >> A0_DEST_NR_SHIFT) & REG_NR_MASK;
+ uint32_t type = (dword >> A0_DEST_TYPE_SHIFT) & REG_TYPE_MASK;
+ print_reg_type_nr(type, nr);
+ if ((dword & A0_DEST_CHANNEL_ALL) == A0_DEST_CHANNEL_ALL)
+ return;
+ fprintf (stderr, ".");
+ if (dword & A0_DEST_CHANNEL_X)
+ fprintf (stderr, "x");
+ if (dword & A0_DEST_CHANNEL_Y)
+ fprintf (stderr, "y");
+ if (dword & A0_DEST_CHANNEL_Z)
+ fprintf (stderr, "z");
+ if (dword & A0_DEST_CHANNEL_W)
+ fprintf (stderr, "w");
+}
+
+#define GET_SRC0_REG(r0, r1) ((r0<<14)|(r1>>A1_SRC0_CHANNEL_W_SHIFT))
+#define GET_SRC1_REG(r0, r1) ((r0<<8)|(r1>>A2_SRC1_CHANNEL_W_SHIFT))
+#define GET_SRC2_REG(r) (r)
+
+static void
+print_arith_op(uint32_t opcode, const uint32_t * program)
+{
+ if (opcode != A0_NOP) {
+ print_dest_reg(program[0]);
+ if (program[0] & A0_DEST_SATURATE)
+ fprintf (stderr, " = SATURATE ");
+ else
+ fprintf (stderr, " = ");
+ }
+
+ fprintf (stderr, "%s ", opcodes[opcode]);
+
+ print_src_reg(GET_SRC0_REG(program[0], program[1]));
+ if (args[opcode] == 1) {
+ fprintf (stderr, "\n");
+ return;
+ }
+
+ fprintf (stderr, ", ");
+ print_src_reg(GET_SRC1_REG(program[1], program[2]));
+ if (args[opcode] == 2) {
+ fprintf (stderr, "\n");
+ return;
+ }
+
+ fprintf (stderr, ", ");
+ print_src_reg(GET_SRC2_REG(program[2]));
+ fprintf (stderr, "\n");
+ return;
+}
+
+static void
+print_tex_op(uint32_t opcode, const uint32_t * program)
+{
+ print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL);
+ fprintf (stderr, " = ");
+
+ fprintf (stderr, "%s ", opcodes[opcode]);
+
+ fprintf (stderr, "S[%d],", program[0] & T0_SAMPLER_NR_MASK);
+
+ print_reg_type_nr((program[1] >> T1_ADDRESS_REG_TYPE_SHIFT) &
+ REG_TYPE_MASK,
+ (program[1] >> T1_ADDRESS_REG_NR_SHIFT) & REG_NR_MASK);
+ fprintf (stderr, "\n");
+}
+
+static void
+print_dcl_op(uint32_t opcode, const uint32_t * program)
+{
+ fprintf (stderr, "%s ", opcodes[opcode]);
+ print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL);
+ fprintf (stderr, "\n");
+}
+
+static void
+i915_disassemble_program (const uint32_t * program, uint32_t sz)
+{
+ uint32_t size = program[0] & 0x1ff;
+ uint32_t i;
+
+ fprintf (stderr, "\tPROGRAM\n");
+
+ assert(size + 2 == sz);
+
+ program++;
+ for (i = 1; i < sz; i += 3, program += 3) {
+ uint32_t opcode = program[0] & (0x1f << 24);
+
+ fprintf (stderr, "\t\t");
+
+ if ((int) opcode >= A0_NOP && opcode <= A0_SLT)
+ print_arith_op(opcode >> 24, program);
+ else if (opcode >= T0_TEXLD && opcode <= T0_TEXKILL)
+ print_tex_op(opcode >> 24, program);
+ else if (opcode == D0_DCL)
+ print_dcl_op(opcode >> 24, program);
+ else
+ fprintf (stderr, "Unknown opcode 0x%x\n", opcode);
+ }
+
+ fprintf (stderr, "\tEND-PROGRAM\n\n");
+}
+
+static cairo_bool_t
+debug_program (struct debug_stream *stream, const char *name, uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+
+ if (len == 0) {
+ fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]);
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ i915_disassemble_program (ptr, len);
+
+ stream->offset += len * sizeof(uint32_t);
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_chain (struct debug_stream *stream, const char *name, uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t old_offset = stream->offset + len * sizeof(uint32_t);
+ uint32_t i;
+
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ for (i = 0; i < len; i++)
+ fprintf (stderr, "\t0x%08x\n", ptr[i]);
+
+ stream->offset = ptr[1] & ~0x3;
+
+ if (stream->offset < old_offset)
+ fprintf (stderr, "\n... skipping backwards from 0x%x --> 0x%x ...\n\n",
+ old_offset, stream->offset );
+ else
+ fprintf (stderr, "\n... skipping from 0x%x --> 0x%x ...\n\n",
+ old_offset, stream->offset );
+
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_variable_length_prim (struct debug_stream *stream)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ const char *prim = get_prim_name( ptr[0] );
+ uint32_t i, len;
+
+ uint16_t *idx = (uint16_t *)(ptr+1);
+ for (i = 0; idx[i] != 0xffff; i++)
+ ;
+
+ len = 1+(i+2)/2;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "3DPRIM, %s variable length %d indicies (%d dwords):\n", prim, i, len);
+ for (i = 0; i < len; i++)
+ fprintf (stderr, "\t0x%08x\n", ptr[i]);
+ fprintf (stderr, "\n");
+
+ stream->offset += len * sizeof(uint32_t);
+ return TRUE;
+}
+
+#define BITS(dw, hi, lo, ...) \
+ do { \
+ unsigned himask = 0xffffffffU >> (31 - (hi)); \
+ fprintf (stderr, "\t\t "); \
+ fprintf (stderr, __VA_ARGS__); \
+ fprintf (stderr, ": 0x%x\n", ((dw) & himask) >> (lo)); \
+ } while (0)
+
+#define MBZ(dw, hi, lo) do { \
+ unsigned x = (dw) >> (lo); \
+ unsigned lomask = (1 << (lo)) - 1; \
+ unsigned himask; \
+ himask = (1UL << (hi)) - 1; \
+ assert ((x & himask & ~lomask) == 0); \
+} while (0)
+
+#define FLAG(dw, bit, ... ) \
+ do { \
+ if (((dw) >> (bit)) & 1) { \
+ fprintf (stderr, "\t\t "); \
+ fprintf (stderr, __VA_ARGS__); \
+ fprintf (stderr, "\n"); \
+ } \
+ } while (0)
+
+static cairo_bool_t
+debug_load_immediate (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t bits = (ptr[0] >> 4) & 0xff;
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords, flags: %x):\n", name, len, bits);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ if (bits & (1<<0)) {
+ fprintf (stderr, "\t LIS0: 0x%08x\n", ptr[j]);
+ fprintf (stderr, "\t vb address: 0x%08x\n", (ptr[j] & ~0x3));
+ BITS (ptr[j], 0, 0, "vb invalidate disable");
+ j++;
+ }
+ if (bits & (1<<1)) {
+ fprintf (stderr, "\t LIS1: 0x%08x\n", ptr[j]);
+ BITS (ptr[j], 29, 24, "vb dword width");
+ BITS (ptr[j], 21, 16, "vb dword pitch");
+ BITS (ptr[j], 15, 0, "vb max index");
+ j++;
+ }
+ if (bits & (1<<2)) {
+ int i;
+ fprintf (stderr, "\t LIS2: 0x%08x\n", ptr[j]);
+ for (i = 0; i < 8; i++) {
+ unsigned tc = (ptr[j] >> (i * 4)) & 0xf;
+ if (tc != 0xf)
+ BITS (tc, 3, 0, "tex coord %d", i);
+ }
+ j++;
+ }
+ if (bits & (1<<3)) {
+ fprintf (stderr, "\t LIS3: 0x%08x\n", ptr[j]);
+ j++;
+ }
+ if (bits & (1<<4)) {
+ fprintf (stderr, "\t LIS4: 0x%08x\n", ptr[j]);
+ BITS (ptr[j], 31, 23, "point width");
+ BITS (ptr[j], 22, 19, "line width");
+ FLAG (ptr[j], 18, "alpha flatshade");
+ FLAG (ptr[j], 17, "fog flatshade");
+ FLAG (ptr[j], 16, "spec flatshade");
+ FLAG (ptr[j], 15, "rgb flatshade");
+ BITS (ptr[j], 14, 13, "cull mode");
+ FLAG (ptr[j], 12, "vfmt: point width");
+ FLAG (ptr[j], 11, "vfmt: specular/fog");
+ FLAG (ptr[j], 10, "vfmt: rgba");
+ FLAG (ptr[j], 9, "vfmt: depth offset");
+ BITS (ptr[j], 8, 6, "vfmt: position (2==xyzw)");
+ FLAG (ptr[j], 5, "force dflt diffuse");
+ FLAG (ptr[j], 4, "force dflt specular");
+ FLAG (ptr[j], 3, "local depth offset enable");
+ FLAG (ptr[j], 2, "vfmt: fp32 fog coord");
+ FLAG (ptr[j], 1, "sprite point");
+ FLAG (ptr[j], 0, "antialiasing");
+ j++;
+ }
+ if (bits & (1<<5)) {
+ fprintf (stderr, "\t LIS5: 0x%08x\n", ptr[j]);
+ BITS (ptr[j], 31, 28, "rgba write disables");
+ FLAG (ptr[j], 27, "force dflt point width");
+ FLAG (ptr[j], 26, "last pixel enable");
+ FLAG (ptr[j], 25, "global z offset enable");
+ FLAG (ptr[j], 24, "fog enable");
+ BITS (ptr[j], 23, 16, "stencil ref");
+ BITS (ptr[j], 15, 13, "stencil test");
+ BITS (ptr[j], 12, 10, "stencil fail op");
+ BITS (ptr[j], 9, 7, "stencil pass z fail op");
+ BITS (ptr[j], 6, 4, "stencil pass z pass op");
+ FLAG (ptr[j], 3, "stencil write enable");
+ FLAG (ptr[j], 2, "stencil test enable");
+ FLAG (ptr[j], 1, "color dither enable");
+ FLAG (ptr[j], 0, "logiop enable");
+ j++;
+ }
+ if (bits & (1<<6)) {
+ fprintf (stderr, "\t LIS6: 0x%08x\n", ptr[j]);
+ FLAG (ptr[j], 31, "alpha test enable");
+ BITS (ptr[j], 30, 28, "alpha func");
+ BITS (ptr[j], 27, 20, "alpha ref");
+ FLAG (ptr[j], 19, "depth test enable");
+ BITS (ptr[j], 18, 16, "depth func");
+ FLAG (ptr[j], 15, "blend enable");
+ BITS (ptr[j], 14, 12, "blend func");
+ BITS (ptr[j], 11, 8, "blend src factor");
+ BITS (ptr[j], 7, 4, "blend dst factor");
+ FLAG (ptr[j], 3, "depth write enable");
+ FLAG (ptr[j], 2, "color write enable");
+ BITS (ptr[j], 1, 0, "provoking vertex");
+ j++;
+ }
+
+ fprintf (stderr, "\n");
+
+ assert(j == len);
+
+ stream->offset += len * sizeof(uint32_t);
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_load_indirect (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t bits = (ptr[0] >> 8) & 0x3f;
+ uint32_t i, j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ for (i = 0; i < 6; i++) {
+ if (bits & (1<<i)) {
+ switch (1<<(8+i)) {
+ case LI0_STATE_STATIC_INDIRECT:
+ fprintf (stderr, " STATIC: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+ fprintf (stderr, " 0x%08x\n", ptr[j++]);
+ break;
+ case LI0_STATE_DYNAMIC_INDIRECT:
+ fprintf (stderr, " DYNAMIC: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+ break;
+ case LI0_STATE_SAMPLER:
+ fprintf (stderr, " SAMPLER: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+ fprintf (stderr, " 0x%08x\n", ptr[j++]);
+ break;
+ case LI0_STATE_MAP:
+ fprintf (stderr, " MAP: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+ fprintf (stderr, " 0x%08x\n", ptr[j++]);
+ break;
+ case LI0_STATE_PROGRAM:
+ fprintf (stderr, " PROGRAM: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+ fprintf (stderr, " 0x%08x\n", ptr[j++]);
+ break;
+ case LI0_STATE_CONSTANTS:
+ fprintf (stderr, " CONSTANTS: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+ fprintf (stderr, " 0x%08x\n", ptr[j++]);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ }
+ }
+
+ if (bits == 0) {
+ fprintf (stderr, "\t DUMMY: 0x%08x\n", ptr[j++]);
+ }
+
+ fprintf (stderr, "\n");
+
+ assert(j == len);
+ stream->offset += len * sizeof(uint32_t);
+ return TRUE;
+}
+
+static void
+BR13 (struct debug_stream *stream,
+ uint32_t val)
+{
+ fprintf (stderr, "\t0x%08x\n", val);
+ FLAG (val, 30, "clipping enable");
+ BITS (val, 25, 24, "color depth (3==32bpp)");
+ BITS (val, 23, 16, "raster op");
+ BITS (val, 15, 0, "dest pitch");
+}
+
+static void
+BR2223 (struct debug_stream *stream,
+ uint32_t val22, uint32_t val23)
+{
+ union { uint32_t val; short field[2]; } BR22, BR23;
+
+ BR22.val = val22;
+ BR23.val = val23;
+
+ fprintf (stderr, "\t0x%08x\n", val22);
+ BITS (val22, 31, 16, "dest y1");
+ BITS (val22, 15, 0, "dest x1");
+
+ fprintf (stderr, "\t0x%08x\n", val23);
+ BITS (val23, 31, 16, "dest y2");
+ BITS (val23, 15, 0, "dest x2");
+
+ /* The blit engine may produce unexpected results when these aren't met */
+ assert(BR22.field[0] < BR23.field[0]);
+ assert(BR22.field[1] < BR23.field[1]);
+}
+
+static void
+BR09 (struct debug_stream *stream,
+ uint32_t val)
+{
+ fprintf (stderr, "\t0x%08x -- dest address\n", val);
+}
+
+static void
+BR26 (struct debug_stream *stream,
+ uint32_t val)
+{
+ fprintf (stderr, "\t0x%08x\n", val);
+ BITS (val, 31, 16, "src y1");
+ BITS (val, 15, 0, "src x1");
+}
+
+static void
+BR11 (struct debug_stream *stream,
+ uint32_t val)
+{
+ fprintf (stderr, "\t0x%08x\n", val);
+ BITS (val, 15, 0, "src pitch");
+}
+
+static void
+BR12 (struct debug_stream *stream,
+ uint32_t val)
+{
+ fprintf (stderr, "\t0x%08x -- src address\n", val);
+}
+
+static void
+BR16 (struct debug_stream *stream,
+ uint32_t val)
+{
+ fprintf (stderr, "\t0x%08x -- color\n", val);
+}
+
+static cairo_bool_t
+debug_copy_blit (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ BR13(stream, ptr[j++]);
+ BR2223(stream, ptr[j], ptr[j+1]);
+ j += 2;
+ BR09(stream, ptr[j++]);
+ BR26(stream, ptr[j++]);
+ BR11(stream, ptr[j++]);
+ BR12(stream, ptr[j++]);
+
+ stream->offset += len * sizeof(uint32_t);
+ assert(j == len);
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_color_blit (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ BR13(stream, ptr[j++]);
+ BR2223(stream, ptr[j], ptr[j+1]);
+ j += 2;
+ BR09(stream, ptr[j++]);
+ BR16(stream, ptr[j++]);
+
+ stream->offset += len * sizeof(uint32_t);
+ assert(j == len);
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_modes4 (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j]);
+ BITS (ptr[j], 21, 18, "logicop func");
+ FLAG (ptr[j], 17, "stencil test mask modify-enable");
+ FLAG (ptr[j], 16, "stencil write mask modify-enable");
+ BITS (ptr[j], 15, 8, "stencil test mask");
+ BITS (ptr[j], 7, 0, "stencil write mask");
+ fprintf (stderr, "\n");
+ j++;
+
+ stream->offset += len * sizeof(uint32_t);
+ assert(j == len);
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_map_state (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ {
+ fprintf (stderr, "\t0x%08x\n", ptr[j]);
+ BITS (ptr[j], 15, 0, "map mask");
+ j++;
+ }
+
+ while (j < len) {
+ {
+ fprintf (stderr, "\t TMn.0: 0x%08x\n", ptr[j]);
+ fprintf (stderr, "\t map address: 0x%08x\n", (ptr[j] & ~0x3));
+ FLAG (ptr[j], 1, "vertical line stride");
+ FLAG (ptr[j], 0, "vertical line stride offset");
+ j++;
+ }
+
+ {
+ fprintf (stderr, "\t TMn.1: 0x%08x\n", ptr[j]);
+ BITS (ptr[j], 31, 21, "height");
+ BITS (ptr[j], 20, 10, "width");
+ BITS (ptr[j], 9, 7, "surface format");
+ BITS (ptr[j], 6, 3, "texel format");
+ FLAG (ptr[j], 2, "use fence regs");
+ FLAG (ptr[j], 1, "tiled surface");
+ FLAG (ptr[j], 0, "tile walk ymajor");
+ j++;
+ }
+ {
+ fprintf (stderr, "\t TMn.2: 0x%08x\n", ptr[j]);
+ BITS (ptr[j], 31, 21, "dword pitch");
+ BITS (ptr[j], 20, 15, "cube face enables");
+ BITS (ptr[j], 14, 9, "max lod");
+ FLAG (ptr[j], 8, "mip layout right");
+ BITS (ptr[j], 7, 0, "depth");
+ j++;
+ }
+ }
+
+ stream->offset += len * sizeof(uint32_t);
+ assert(j == len);
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_sampler_state (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ {
+ fprintf (stderr, "\t0x%08x\n", ptr[j]);
+ BITS (ptr[j], 15, 0, "sampler mask");
+ j++;
+ }
+
+ while (j < len) {
+ {
+ fprintf (stderr, "\t TSn.0: 0x%08x\n", ptr[j]);
+ FLAG (ptr[j], 31, "reverse gamma");
+ FLAG (ptr[j], 30, "planar to packed");
+ FLAG (ptr[j], 29, "yuv->rgb");
+ BITS (ptr[j], 28, 27, "chromakey index");
+ BITS (ptr[j], 26, 22, "base mip level");
+ BITS (ptr[j], 21, 20, "mip mode filter");
+ BITS (ptr[j], 19, 17, "mag mode filter");
+ BITS (ptr[j], 16, 14, "min mode filter");
+ BITS (ptr[j], 13, 5, "lod bias (s4.4)");
+ FLAG (ptr[j], 4, "shadow enable");
+ FLAG (ptr[j], 3, "max-aniso-4");
+ BITS (ptr[j], 2, 0, "shadow func");
+ j++;
+ }
+
+ {
+ fprintf (stderr, "\t TSn.1: 0x%08x\n", ptr[j]);
+ BITS (ptr[j], 31, 24, "min lod");
+ MBZ( ptr[j], 23, 18 );
+ FLAG (ptr[j], 17, "kill pixel enable");
+ FLAG (ptr[j], 16, "keyed tex filter mode");
+ FLAG (ptr[j], 15, "chromakey enable");
+ BITS (ptr[j], 14, 12, "tcx wrap mode");
+ BITS (ptr[j], 11, 9, "tcy wrap mode");
+ BITS (ptr[j], 8, 6, "tcz wrap mode");
+ FLAG (ptr[j], 5, "normalized coords");
+ BITS (ptr[j], 4, 1, "map (surface) index");
+ FLAG (ptr[j], 0, "EAST deinterlacer enable");
+ j++;
+ }
+ {
+ fprintf (stderr, "\t TSn.2: 0x%08x (default color)\n", ptr[j]);
+ j++;
+ }
+ }
+
+ stream->offset += len * sizeof(uint32_t);
+ assert(j == len);
+ return TRUE;
+}
+
+static cairo_bool_t
+debug_dest_vars (struct debug_stream *stream,
+ const char *name,
+ uint32_t len)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ {
+ fprintf (stderr, "\t0x%08x\n", ptr[j]);
+ FLAG (ptr[j], 31, "early classic ztest");
+ FLAG (ptr[j], 30, "opengl tex default color");
+ FLAG (ptr[j], 29, "bypass iz");
+ FLAG (ptr[j], 28, "lod preclamp");
+ BITS (ptr[j], 27, 26, "dither pattern");
+ FLAG (ptr[j], 25, "linear gamma blend");
+ FLAG (ptr[j], 24, "debug dither");
+ BITS (ptr[j], 23, 20, "dstorg x");
+ BITS (ptr[j], 19, 16, "dstorg y");
+ MBZ (ptr[j], 15, 15 );
+ BITS (ptr[j], 14, 12, "422 write select");
+ BITS (ptr[j], 11, 8, "cbuf format");
+ BITS (ptr[j], 3, 2, "zbuf format");
+ FLAG (ptr[j], 1, "vert line stride");
+ FLAG (ptr[j], 1, "vert line stride offset");
+ j++;
+ }
+
+ stream->offset += len * sizeof(uint32_t);
+ assert(j == len);
+ return TRUE;
+}
+
+static cairo_bool_t debug_buf_info( struct debug_stream *stream,
+ const char *name,
+ uint32_t len )
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t j = 0;
+
+ fprintf (stderr, "%04x: ", stream->offset);
+ fprintf (stderr, "%s (%d dwords):\n", name, len);
+ fprintf (stderr, "\t0x%08x\n", ptr[j++]);
+
+ {
+ fprintf (stderr, "\t0x%08x\n", ptr[j]);
+ BITS (ptr[j], 28, 28, "aux buffer id");
+ BITS (ptr[j], 27, 24, "buffer id (7=depth, 3=back)");
+ FLAG (ptr[j], 23, "use fence regs");
+ FLAG (ptr[j], 22, "tiled surface");
+ FLAG (ptr[j], 21, "tile walk ymajor");
+ MBZ (ptr[j], 20, 14);
+ BITS (ptr[j], 13, 2, "dword pitch");
+ MBZ (ptr[j], 2, 0);
+ j++;
+ }
+
+ fprintf (stderr, "\t0x%08x -- buffer base address\n", ptr[j++]);
+
+ stream->offset += len * sizeof(uint32_t);
+ assert(j == len);
+ return TRUE;
+}
+
+static cairo_bool_t
+decode_3d_i915 (struct debug_stream *stream)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t cmd = *ptr;
+
+ switch ((cmd >> 24) & 0x1f) {
+ case 0x6:
+ return debug (stream, "3DSTATE_ANTI_ALIASING", 1);
+ case 0x7:
+ return debug (stream, "3DSTATE_RASTERIZATION_RULES", 1);
+ case 0x8:
+ return debug (stream, "3DSTATE_BACKFACE_STENCIL_OPS", 1);
+ case 0x9:
+ return debug (stream, "3DSTATE_BACKFACE_STENCIL_MASKS", 1);
+ case 0xb:
+ return debug (stream, "3DSTATE_INDEPENDENT_ALPHA_BLEND", 1);
+ case 0xc:
+ return debug (stream, "3DSTATE_MODES5", 1);
+ case 0xd:
+ return debug_modes4(stream, "3DSTATE_MODES4", 1);
+ case 0x15:
+ return debug (stream, "3DSTATE_FOG_COLOR", 1);
+ case 0x16:
+ return debug (stream, "3DSTATE_COORD_SET_BINDINGS", 1);
+ case 0x1c:
+ /* 3DState16NP */
+ switch((cmd >> 19) & 0x1f) {
+ case 0x10:
+ return debug (stream, "3DSTATE_SCISSOR_ENABLE", 1);
+ case 0x11:
+ return debug (stream, "3DSTATE_DEPTH_SUBRECTANGLE_DISABLE", 1);
+ default:
+ break;
+ }
+ break;
+ case 0x1d:
+ /* 3DStateMW */
+ switch ((cmd >> 16) & 0xff) {
+ case 0x0:
+ return debug_map_state(stream, "3DSTATE_MAP_STATE", (cmd & 0x1f) + 2);
+ case 0x1:
+ return debug_sampler_state(stream, "3DSTATE_SAMPLER_STATE", (cmd & 0x1f) + 2);
+ case 0x4:
+ return debug_load_immediate(stream, "3DSTATE_LOAD_STATE_IMMEDIATE", (cmd & 0xf) + 2);
+ case 0x5:
+ return debug_program(stream, "3DSTATE_PIXEL_SHADER_PROGRAM", (cmd & 0x1ff) + 2);
+ case 0x6:
+ return debug (stream, "3DSTATE_PIXEL_SHADER_CONSTANTS", (cmd & 0xff) + 2);
+ case 0x7:
+ return debug_load_indirect(stream, "3DSTATE_LOAD_INDIRECT", (cmd & 0xff) + 2);
+ case 0x80:
+ return debug (stream, "3DSTATE_DRAWING_RECTANGLE", (cmd & 0xffff) + 2);
+ case 0x81:
+ return debug (stream, "3DSTATE_SCISSOR_RECTANGLE", (cmd & 0xffff) + 2);
+ case 0x83:
+ return debug (stream, "3DSTATE_SPAN_STIPPLE", (cmd & 0xffff) + 2);
+ case 0x85:
+ return debug_dest_vars(stream, "3DSTATE_DEST_BUFFER_VARS", (cmd & 0xffff) + 2);
+ case 0x88:
+ return debug (stream, "3DSTATE_CONSTANT_BLEND_COLOR", (cmd & 0xffff) + 2);
+ case 0x89:
+ return debug (stream, "3DSTATE_FOG_MODE", (cmd & 0xffff) + 2);
+ case 0x8e:
+ return debug_buf_info(stream, "3DSTATE_BUFFER_INFO", (cmd & 0xffff) + 2);
+ case 0x97:
+ return debug (stream, "3DSTATE_DEPTH_OFFSET_SCALE", (cmd & 0xffff) + 2);
+ case 0x98:
+ return debug (stream, "3DSTATE_DEFAULT_Z", (cmd & 0xffff) + 2);
+ case 0x99:
+ return debug (stream, "3DSTATE_DEFAULT_DIFFUSE", (cmd & 0xffff) + 2);
+ case 0x9a:
+ return debug (stream, "3DSTATE_DEFAULT_SPECULAR", (cmd & 0xffff) + 2);
+ case 0x9c:
+ return debug (stream, "3DSTATE_CLEAR_PARAMETERS", (cmd & 0xffff) + 2);
+ default:
+ ASSERT_NOT_REACHED;
+ return 0;
+ }
+ break;
+ case 0x1e:
+ if (cmd & (1 << 23))
+ return debug (stream, "???", (cmd & 0xffff) + 1);
+ else
+ return debug (stream, "", 1);
+ break;
+ case 0x1f:
+ if ((cmd & (1 << 23)) == 0) {
+ return debug_prim (stream, "3DPRIM (inline)", 1, (cmd & 0x1ffff) + 2);
+ } else if (cmd & (1 << 17)) {
+ if ((cmd & 0xffff) == 0)
+ return debug_variable_length_prim (stream);
+ else
+ return debug_prim (stream, "3DPRIM (indexed)", 0, (((cmd & 0xffff) + 1) / 2) + 1);
+ } else
+ return debug_prim (stream, "3DPRIM (indirect sequential)", 0, 2);
+ break;
+ default:
+ return debug (stream, "", 0);
+ }
+
+ return FALSE;
+}
+
+static cairo_bool_t
+decode_3d_i965 (struct debug_stream *stream)
+{
+ const uint32_t *data = (uint32_t *) (stream->ptr + stream->offset);
+ const uint32_t opcode = (data[0] & 0xffff0000) >> 16;
+ unsigned int idx;
+ const struct {
+ uint32_t opcode;
+ int min_len;
+ int max_len;
+ const char *name;
+ } opcodes_3d[] = {
+ { 0x6000, 3, 3, "URB_FENCE" },
+ { 0x6001, 2, 2, "CS_URB_STATE" },
+ { 0x6002, 2, 2, "CONSTANT_BUFFER" },
+ { 0x6101, 6, 6, "STATE_BASE_ADDRESS" },
+ { 0x6102, 2, 2 , "STATE_SIP" },
+ { 0x6104, 1, 1, "3DSTATE_PIPELINE_SELECT" },
+ { 0x680b, 1, 1, "3DSTATE_VF_STATISTICS" },
+ { 0x6904, 1, 1, "3DSTATE_PIPELINE_SELECT" },
+ { 0x7800, 7, 7, "3DSTATE_PIPELINED_POINTERS" },
+ { 0x7801, 6, 6, "3DSTATE_BINDING_TABLE_POINTERS" },
+ { 0x780b, 1, 1, "3DSTATE_VF_STATISTICS" },
+ { 0x7808, 5, 257, "3DSTATE_VERTEX_BUFFERS" },
+ { 0x7809, 3, 256, "3DSTATE_VERTEX_ELEMENTS" },
+ { 0x780a, 3, 3, "3DSTATE_INDEX_BUFFER" },
+ { 0x7900, 4, 4, "3DSTATE_DRAWING_RECTANGLE" },
+ { 0x7901, 5, 5, "3DSTATE_CONSTANT_COLOR" },
+ { 0x7905, 5, 7, "3DSTATE_DEPTH_BUFFER" },
+ { 0x7906, 2, 2, "3DSTATE_POLY_STIPPLE_OFFSET" },
+ { 0x7907, 33, 33, "3DSTATE_POLY_STIPPLE_PATTERN" },
+ { 0x7908, 3, 3, "3DSTATE_LINE_STIPPLE" },
+ { 0x7909, 2, 2, "3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP" },
+ { 0x790a, 3, 3, "3DSTATE_AA_LINE_PARAMETERS" },
+ { 0x7b00, 6, 6, "3DPRIMITIVE" },
+ }, *opcode_3d;
+
+ for (idx = 0; idx < ARRAY_LENGTH (opcodes_3d); idx++) {
+ opcode_3d = &opcodes_3d[idx];
+ if (opcode == opcode_3d->opcode) {
+ unsigned int len = 1;
+ if (opcode_3d->max_len != 1)
+ len = (data[0] & 0x000000ff) + 2;
+ return debug (stream, opcode_3d->name, len);
+ }
+ }
+
+ return FALSE;
+}
+
+static cairo_bool_t
+decode_3d_i830 (struct debug_stream *stream)
+{
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+static cairo_bool_t
+i915_debug_packet (struct debug_stream *stream,
+ int devid)
+{
+ uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+ uint32_t cmd = *ptr;
+
+ switch (((cmd >> 29) & 0x7)) {
+ case 0x0:
+ switch ((cmd >> 23) & 0x3f) {
+ case 0x0:
+ return debug (stream, "MI_NOOP", 1);
+ case 0x3:
+ return debug (stream, "MI_WAIT_FOR_EVENT", 1);
+ case 0x4:
+ return debug (stream, "MI_FLUSH", 1);
+ case 0xA:
+ debug (stream, "MI_BATCH_BUFFER_END", 1);
+ return FALSE;
+ case 0x22:
+ return debug (stream, "MI_LOAD_REGISTER_IMM", 3);
+ case 0x31:
+ return debug_chain(stream, "MI_BATCH_BUFFER_START", 2);
+ default:
+ break;
+ }
+ break;
+ case 0x1:
+ break;
+ case 0x2:
+ switch ((cmd >> 22) & 0xff) {
+ case 0x50:
+ return debug_color_blit(stream, "XY_COLOR_BLT", (cmd & 0xff) + 2);
+ case 0x53:
+ return debug_copy_blit(stream, "XY_SRC_COPY_BLT", (cmd & 0xff) + 2);
+ default:
+ return debug (stream, "blit command", (cmd & 0xff) + 2);
+ }
+ break;
+ case 0x3:
+ if (IS_965(devid))
+ return decode_3d_i965 (stream);
+ else if (IS_9XX(devid))
+ return decode_3d_i915 (stream);
+ else
+ return decode_3d_i830 (stream);
+ default:
+ break;
+ }
+
+ fprintf (stderr, "Bogus cmd: %x [%x]\n", (cmd >> 29) & 7, cmd);
+ ASSERT_NOT_REACHED;
+ return 0;
+}
+
+void
+intel_dump_batchbuffer (const void *batch,
+ uint32_t length,
+ int devid)
+{
+ struct debug_stream stream;
+ cairo_bool_t done = FALSE;
+
+ fprintf (stderr, "\nBATCH: (%d dwords)\n", length / 4);
+
+ stream.offset = 0;
+ stream.ptr = batch;
+
+ while (! done && stream.offset < length) {
+ if (! i915_debug_packet (&stream, devid))
+ break;
+
+ assert (stream.offset <= length);
+ }
+
+ fprintf (stderr, "END-BATCH\n\n");
+ fflush (stderr);
+}
diff --git a/src/drm/cairo-drm-intel-ioctl-private.h b/src/drm/cairo-drm-intel-ioctl-private.h
new file mode 100755
index 000000000..004d3bfd7
--- /dev/null
+++ b/src/drm/cairo-drm-intel-ioctl-private.h
@@ -0,0 +1,442 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_DRM_INTEL_IOCTL_PRIVATE_H
+#define CAIRO_DRM_INTEL_IOCTL_PRIVATE_H
+
+#include "cairo-drm-intel-command-private.h"
+
+#define I915_PARAM_IRQ_ACTIVE 1
+#define I915_PARAM_ALLOW_BATCHBUFFER 2
+#define I915_PARAM_LAST_DISPATCH 3
+#define I915_PARAM_CHIPSET_ID 4
+#define I915_PARAM_HAS_GEM 5
+#define I915_PARAM_NUM_FENCES_AVAIL 6
+#define I915_PARAM_HAS_OVERLAY 7
+#define I915_PARAM_HAS_PAGEFLIPPING 8
+#define I915_PARAM_HAS_EXECBUF2 9
+
+struct intel_getparam {
+ int param;
+ int *value;
+};
+
+
+/* @{
+ * Intel memory domains
+ *
+ * Most of these just align with the various caches in
+ * the system and are used to flush and invalidate as
+ * objects end up cached in different domains.
+ */
+/* CPU cache */
+#define I915_GEM_DOMAIN_CPU 0x00000001
+/* Render cache, used by 2D and 3D drawing */
+#define I915_GEM_DOMAIN_RENDER 0x00000002
+/* Sampler cache, used by texture engine */
+#define I915_GEM_DOMAIN_SAMPLER 0x00000004
+/* Command queue, used to load batch buffers */
+#define I915_GEM_DOMAIN_COMMAND 0x00000008
+/* Instruction cache, used by shader programs */
+#define I915_GEM_DOMAIN_INSTRUCTION 0x00000010
+/* Vertex address cache */
+#define I915_GEM_DOMAIN_VERTEX 0x00000020
+/* GTT domain - aperture and scanout */
+#define I915_GEM_DOMAIN_GTT 0x00000040
+/* @} */
+
+#define I915_TILING_NONE 0
+#define I915_TILING_X 1
+#define I915_TILING_Y 2
+
+#define I915_BIT_6_SWIZZLE_NONE 0
+#define I915_BIT_6_SWIZZLE_9 1
+#define I915_BIT_6_SWIZZLE_9_10 2
+#define I915_BIT_6_SWIZZLE_9_11 3
+#define I915_BIT_6_SWIZZLE_9_10_11 4
+
+#define DRM_I915_GEM_EXECBUFFER 0x14
+#define DRM_I915_GEM_BUSY 0x17
+#define DRM_I915_GEM_THROTTLE 0x18
+#define DRM_I915_GEM_CREATE 0x1b
+#define DRM_I915_GEM_PREAD 0x1c
+#define DRM_I915_GEM_PWRITE 0x1d
+#define DRM_I915_GEM_MMAP 0x1e
+#define DRM_I915_GEM_SET_DOMAIN 0x1f
+#define DRM_I915_GEM_SET_TILING 0x21
+#define DRM_I915_GEM_GET_TILING 0x22
+#define DRM_I915_GEM_GET_APERTURE 0x23
+#define DRM_I915_GEM_MMAP_GTT 0x24
+
+struct drm_i915_gem_create {
+ /*
+ * Requested size for the object.
+ *
+ * The (page-aligned) allocated size for the object will be returned.
+ */
+ uint64_t size;
+ /*
+ * Returned handle for the object.
+ *
+ * Object handles are nonzero.
+ */
+ uint32_t handle;
+ uint32_t pad;
+};
+
+struct drm_i915_gem_pread {
+ /* Handle for the object being read. */
+ uint32_t handle;
+ uint32_t pad;
+ /* Offset into the object to read from */
+ uint64_t offset;
+ /* Length of data to read */
+ uint64_t size;
+ /*
+ * Pointer to write the data into.
+ *
+ * This is a fixed-size type for 32/64 compatibility.
+ */
+ uint64_t data_ptr;
+};
+
+struct drm_i915_gem_pwrite {
+ /* Handle for the object being written to. */
+ uint32_t handle;
+ uint32_t pad;
+ /* Offset into the object to write to */
+ uint64_t offset;
+ /* Length of data to write */
+ uint64_t size;
+ /*
+ * Pointer to read the data from.
+ *
+ * This is a fixed-size type for 32/64 compatibility.
+ */
+ uint64_t data_ptr;
+};
+
+struct drm_i915_gem_mmap {
+ /* Handle for the object being mapped. */
+ uint32_t handle;
+ uint32_t pad;
+ /* Offset in the object to map. */
+ uint64_t offset;
+ /*
+ * Length of data to map.
+ *
+ * The value will be page-aligned.
+ */
+ uint64_t size;
+ /*
+ * Returned pointer the data was mapped at.
+ *
+ * This is a fixed-size type for 32/64 compatibility.
+ */
+ uint64_t addr_ptr;
+};
+
+struct drm_i915_gem_mmap_gtt {
+ /* Handle for the object being mapped. */
+ uint32_t handle;
+ uint32_t pad;
+ /*
+ * Fake offset to use for subsequent mmap call
+ *
+ * This is a fixed-size type for 32/64 compatibility.
+ */
+ uint64_t offset;
+};
+
+struct drm_i915_gem_set_domain {
+ /* Handle for the object */
+ uint32_t handle;
+
+ /* New read domains */
+ uint32_t read_domains;
+
+ /* New write domain */
+ uint32_t write_domain;
+};
+
+struct drm_i915_gem_relocation_entry {
+ /*
+ * Handle of the buffer being pointed to by this relocation entry.
+ *
+ * It's appealing to make this be an index into the mm_validate_entry
+ * list to refer to the buffer, but this allows the driver to create
+ * a relocation list for state buffers and not re-write it per
+ * exec using the buffer.
+ */
+ uint32_t target_handle;
+
+ /*
+ * Value to be added to the offset of the target buffer to make up
+ * the relocation entry.
+ */
+ uint32_t delta;
+
+ /* Offset in the buffer the relocation entry will be written into */
+ uint64_t offset;
+
+ /*
+ * Offset value of the target buffer that the relocation entry was last
+ * written as.
+ *
+ * If the buffer has the same offset as last time, we can skip syncing
+ * and writing the relocation. This value is written back out by
+ * the execbuffer ioctl when the relocation is written.
+ */
+ uint64_t presumed_offset;
+
+ /*
+ * Target memory domains read by this operation.
+ */
+ uint32_t read_domains;
+
+ /*
+ * Target memory domains written by this operation.
+ *
+ * Note that only one domain may be written by the whole
+ * execbuffer operation, so that where there are conflicts,
+ * the application will get -EINVAL back.
+ */
+ uint32_t write_domain;
+};
+
+struct drm_i915_gem_exec_object {
+ /*
+ * User's handle for a buffer to be bound into the GTT for this
+ * operation.
+ */
+ uint32_t handle;
+
+ /* Number of relocations to be performed on this buffer */
+ uint32_t relocation_count;
+ /*
+ * Pointer to array of struct drm_i915_gem_relocation_entry containing
+ * the relocations to be performed in this buffer.
+ */
+ uint64_t relocs_ptr;
+
+ /* Required alignment in graphics aperture */
+ uint64_t alignment;
+
+ /*
+ * Returned value of the updated offset of the object, for future
+ * presumed_offset writes.
+ */
+ uint64_t offset;
+};
+
+struct drm_i915_gem_execbuffer {
+ /*
+ * List of buffers to be validated with their relocations to be
+ * performend on them.
+ *
+ * This is a pointer to an array of struct drm_i915_gem_validate_entry.
+ *
+ * These buffers must be listed in an order such that all relocations
+ * a buffer is performing refer to buffers that have already appeared
+ * in the validate list.
+ */
+ uint64_t buffers_ptr;
+ uint32_t buffer_count;
+
+ /* Offset in the batchbuffer to start execution from. */
+ uint32_t batch_start_offset;
+ /* Bytes used in batchbuffer from batch_start_offset */
+ uint32_t batch_len;
+ uint32_t DR1;
+ uint32_t DR4;
+ uint32_t num_cliprects;
+ /* This is a struct drm_clip_rect *cliprects */
+ uint64_t cliprects_ptr;
+};
+
+struct drm_i915_gem_busy {
+ /* Handle of the buffer to check for busy */
+ uint32_t handle;
+
+ /* Return busy status (1 if busy, 0 if idle) */
+ uint32_t busy;
+};
+
+struct drm_i915_gem_set_tiling {
+ /* Handle of the buffer to have its tiling state updated */
+ uint32_t handle;
+
+ /*
+ * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X,
+ * I915_TILING_Y).
+ *
+ * This value is to be set on request, and will be updated by the
+ * kernel on successful return with the actual chosen tiling layout.
+ *
+ * The tiling mode may be demoted to I915_TILING_NONE when the system
+ * has bit 6 swizzling that can't be managed correctly by GEM.
+ *
+ * Buffer contents become undefined when changing tiling_mode.
+ */
+ uint32_t tiling_mode;
+
+ /*
+ * Stride in bytes for the object when in I915_TILING_X or
+ * I915_TILING_Y.
+ */
+ uint32_t stride;
+
+ /*
+ * Returned address bit 6 swizzling required for CPU access through
+ * mmap mapping.
+ */
+ uint32_t swizzle_mode;
+};
+
+struct drm_i915_gem_get_tiling {
+ /* Handle of the buffer to get tiling state for. */
+ uint32_t handle;
+
+ /*
+ * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X,
+ * I915_TILING_Y).
+ */
+ uint32_t tiling_mode;
+
+ /*
+ * Returned address bit 6 swizzling required for CPU access through
+ * mmap mapping.
+ */
+ uint32_t swizzle_mode;
+};
+
+struct drm_i915_gem_get_aperture {
+ /* Total size of the aperture used by i915_gem_execbuffer, in bytes */
+ uint64_t aper_size;
+
+ /*
+ * Available space in the aperture used by i915_gem_execbuffer, in
+ * bytes
+ */
+ uint64_t aper_available_size;
+};
+
+#define DRM_I915_GETPARAM 0x06
+
+#define DRM_IOCTL_I915_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, struct intel_getparam)
+#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer)
+#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
+#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE)
+#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create)
+#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread)
+#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite)
+#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap)
+#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt)
+#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain)
+#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
+#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
+#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
+
+#define I915_MADV_WILLNEED 0
+#define I915_MADV_DONTNEED 1
+
+struct drm_i915_gem_madvise {
+ uint32_t handle;
+ uint32_t madv;
+ uint32_t retained;
+};
+#define DRM_I915_GEM_MADVISE 0x26
+#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
+
+
+/* XXX execbuffer2 */
+struct drm_i915_gem_exec_object2 {
+ /*
+ * User's handle for a buffer to be bound into the GTT for this
+ * operation.
+ */
+ uint32_t handle;
+
+ /* Number of relocations to be performed on this buffer */
+ uint32_t relocation_count;
+ /*
+ * Pointer to array of struct drm_i915_gem_relocation_entry containing
+ * the relocations to be performed in this buffer.
+ */
+ uint64_t relocs_ptr;
+
+ /* Required alignment in graphics aperture */
+ uint64_t alignment;
+
+ /*
+ * Returned value of the updated offset of the object, for future
+ * presumed_offset writes.
+ */
+ uint64_t offset;
+
+#define EXEC_OBJECT_NEEDS_FENCE (1<<0)
+ uint64_t flags;
+ uint64_t rsvd1;
+ uint64_t rsvd2;
+};
+
+struct drm_i915_gem_execbuffer2 {
+ /*
+ * List of gem_exec_object2 structs
+ */
+ uint64_t buffers_ptr;
+ uint32_t buffer_count;
+
+ /* Offset in the batchbuffer to start execution from. */
+ uint32_t batch_start_offset;
+ /* Bytes used in batchbuffer from batch_start_offset */
+ uint32_t batch_len;
+ uint32_t DR1;
+ uint32_t DR4;
+ uint32_t num_cliprects;
+ /* This is a struct drm_clip_rect *cliprects */
+ uint64_t cliprects_ptr;
+ uint64_t flags;
+ uint64_t rsvd1;
+ uint64_t rsvd2;
+};
+
+#define I915_GEM_3D_PIPELINE 0x1
+#define I915_GEM_MEDIA_PIPELINE 0x2
+#define DRM_I915_GEM_EXECBUFFER2 0x29
+#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)
+
+struct drm_i915_gem_real_size {
+ uint32_t handle;
+ uint64_t size;
+};
+#define DRM_I915_GEM_REAL_SIZE 0x2a
+#define DRM_IOCTL_I915_GEM_REAL_SIZE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_REAL_SIZE, struct drm_i915_gem_real_size)
+
+#endif /* CAIRO_DRM_INTEL_IOCTL_PRIVATE_H */
diff --git a/src/drm/cairo-drm-intel-private.h b/src/drm/cairo-drm-intel-private.h
new file mode 100755
index 000000000..343270a3c
--- /dev/null
+++ b/src/drm/cairo-drm-intel-private.h
@@ -0,0 +1,519 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_DRM_INTEL_PRIVATE_H
+#define CAIRO_DRM_INTEL_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-cache-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-drm-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-mutex-private.h"
+#include "cairo-rtree-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-drm-intel-ioctl-private.h"
+
+#define INTEL_TILING_DEFAULT I915_TILING_Y
+
+#define INTEL_BO_CACHE_BUCKETS 12 /* cache surfaces up to 16 MiB */
+
+#define INTEL_GLYPH_CACHE_WIDTH 1024
+#define INTEL_GLYPH_CACHE_HEIGHT 1024
+#define INTEL_GLYPH_CACHE_MIN_SIZE 1
+#define INTEL_GLYPH_CACHE_MAX_SIZE 128
+
+typedef struct _intel_bo {
+ cairo_drm_bo_t base;
+
+ cairo_list_t link;
+ cairo_list_t cache_list;
+
+ uint32_t offset;
+ uint32_t batch_read_domains;
+ uint32_t batch_write_domain;
+
+ uint32_t opaque0;
+ uint32_t opaque1;
+
+ uint32_t full_size;
+ uint16_t stride;
+ uint16_t _stride;
+ uint32_t tiling :4;
+ uint32_t _tiling :4;
+ uint32_t purgeable :1;
+ uint32_t busy :1;
+ uint32_t cpu :1;
+
+ struct drm_i915_gem_exec_object2 *exec;
+ void *virtual;
+} intel_bo_t;
+
+#define INTEL_BATCH_SIZE (64*1024)
+#define INTEL_VERTEX_BUFFER_SIZE (512*1024)
+#define INTEL_MAX_RELOCS 2048
+
+static inline void
+intel_bo_mark_purgeable (intel_bo_t *bo)
+{
+ if (bo->base.name == 0)
+ bo->purgeable = 1;
+}
+
+typedef struct _intel_vertex_buffer intel_vertex_buffer_t;
+
+typedef void (*intel_vertex_buffer_new_func_t) (intel_vertex_buffer_t *vertex_buffer);
+typedef void (*intel_vertex_buffer_start_rectangles_func_t) (intel_vertex_buffer_t *vertex_buffer,
+ uint32_t floats_per_vertex);
+typedef void (*intel_vertex_buffer_flush_func_t) (intel_vertex_buffer_t *vertex_buffer);
+typedef void (*intel_vertex_buffer_finish_func_t) (intel_vertex_buffer_t *vertex_buffer);
+
+struct _intel_vertex_buffer {
+ uint32_t vbo_batch; /* reloc position in batch, 0 -> not yet allocated */
+ uint32_t vbo_offset;
+ uint32_t vbo_used;
+
+ uint32_t vertex_index;
+ uint32_t vertex_count;
+
+ uint32_t floats_per_vertex;
+ uint32_t rectangle_size;
+
+ intel_bo_t *last_vbo;
+ uint32_t last_vbo_offset;
+ uint32_t last_vbo_space;
+
+ intel_vertex_buffer_new_func_t new;
+ intel_vertex_buffer_start_rectangles_func_t start_rectangles;
+ intel_vertex_buffer_flush_func_t flush;
+ intel_vertex_buffer_finish_func_t finish;
+
+ uint32_t base[INTEL_VERTEX_BUFFER_SIZE / sizeof (uint32_t)];
+};
+
+typedef struct _intel_batch intel_batch_t;
+
+typedef void (*intel_batch_commit_func_t) (intel_batch_t *batch);
+typedef void (*intel_batch_reset_func_t) (intel_batch_t *batch);
+
+struct _intel_batch {
+ size_t gtt_size;
+ size_t gtt_avail_size;
+
+ intel_batch_commit_func_t commit;
+ intel_batch_reset_func_t reset;
+
+ uint16_t exec_count;
+ uint16_t reloc_count;
+ uint16_t used;
+ uint16_t header;
+
+ intel_bo_t *target_bo[INTEL_MAX_RELOCS];
+ struct drm_i915_gem_exec_object2 exec[INTEL_MAX_RELOCS];
+ struct drm_i915_gem_relocation_entry reloc[INTEL_MAX_RELOCS];
+
+ uint32_t base[INTEL_BATCH_SIZE / sizeof (uint32_t)];
+
+ intel_vertex_buffer_t vertex_buffer;
+};
+
+typedef struct _intel_buffer {
+ intel_bo_t *bo;
+ uint32_t offset;
+ cairo_format_t format;
+ uint32_t map0, map1;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+} intel_buffer_t;
+
+typedef struct _intel_buffer_cache {
+ int ref_count;
+ intel_buffer_t buffer;
+ cairo_rtree_t rtree;
+ cairo_list_t link;
+} intel_buffer_cache_t;
+
+typedef struct _intel_glyph {
+ cairo_rtree_node_t node;
+ intel_buffer_cache_t *cache;
+ void **owner;
+ float texcoord[3];
+ int width, height;
+} intel_glyph_t;
+
+typedef struct _intel_gradient_cache {
+ cairo_pattern_union_t pattern;
+ intel_buffer_t buffer;
+} intel_gradient_cache_t;
+#define GRADIENT_CACHE_SIZE 16
+
+typedef struct _intel_surface {
+ cairo_drm_surface_t drm;
+
+ cairo_cache_entry_t snapshot_cache_entry;
+} intel_surface_t;
+
+typedef void (*intel_reset_context_func_t) (void *device);
+
+typedef struct _intel_device {
+ cairo_drm_device_t base;
+
+ size_t gtt_max_size;
+ size_t gtt_avail_size;
+
+ cairo_freepool_t bo_pool;
+ cairo_list_t bo_in_flight;
+
+ cairo_mutex_t mutex;
+ intel_batch_t batch;
+
+ intel_buffer_cache_t glyph_cache[2];
+ cairo_list_t fonts;
+
+ struct {
+ intel_gradient_cache_t cache[GRADIENT_CACHE_SIZE];
+ unsigned int size;
+ } gradient_cache;
+
+ cairo_cache_t snapshot_cache;
+ size_t snapshot_cache_max_size;
+
+ intel_reset_context_func_t reset_context;
+
+ cairo_status_t (*flush) (struct _intel_device *);
+} intel_device_t;
+
+static inline intel_device_t *
+to_intel_device (cairo_device_t *base)
+{
+ return (intel_device_t *) base;
+}
+
+static inline intel_bo_t *
+to_intel_bo (cairo_drm_bo_t *base)
+{
+ return (intel_bo_t *) base;
+}
+
+static inline intel_bo_t *
+intel_bo_reference (intel_bo_t *bo)
+{
+ return to_intel_bo (cairo_drm_bo_reference (&bo->base));
+}
+
+cairo_private cairo_bool_t
+intel_bo_madvise (intel_device_t *device, intel_bo_t *bo, int madv);
+
+static cairo_always_inline void
+intel_bo_destroy (intel_device_t *device, intel_bo_t *bo)
+{
+ cairo_drm_bo_destroy (&device->base.base, &bo->base);
+}
+
+static inline void
+intel_bo_in_flight_add (intel_device_t *device,
+ intel_bo_t *bo)
+{
+ if (bo->base.name == 0 && bo->exec != NULL && cairo_list_is_empty (&bo->cache_list))
+ cairo_list_add (&bo->cache_list, &device->bo_in_flight);
+}
+
+cairo_private int
+intel_get (int fd, int param);
+
+cairo_private cairo_bool_t
+intel_info (int fd, uint64_t *gtt_size);
+
+cairo_private cairo_status_t
+intel_device_init (intel_device_t *device, int fd);
+
+cairo_private void
+intel_device_fini (intel_device_t *dev);
+
+cairo_private intel_bo_t *
+intel_bo_create (intel_device_t *dev,
+ uint32_t max_size,
+ uint32_t real_size,
+ cairo_bool_t gpu_target,
+ uint32_t tiling,
+ uint32_t stride);
+
+cairo_private intel_bo_t *
+intel_bo_create_for_name (intel_device_t *dev, uint32_t name);
+
+cairo_private void
+intel_bo_set_tiling (const intel_device_t *dev,
+ intel_bo_t *bo);
+
+cairo_private cairo_bool_t
+intel_bo_is_inactive (const intel_device_t *device,
+ intel_bo_t *bo);
+
+cairo_private cairo_bool_t
+intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo);
+
+cairo_private void
+intel_bo_write (const intel_device_t *dev,
+ intel_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ const void *data);
+
+cairo_private void
+intel_bo_read (const intel_device_t *dev,
+ intel_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ void *data);
+
+cairo_private void *
+intel_bo_map (const intel_device_t *dev, intel_bo_t *bo);
+
+cairo_private void
+intel_bo_unmap (intel_bo_t *bo);
+
+cairo_private cairo_status_t
+intel_bo_init (const intel_device_t *dev,
+ intel_bo_t *bo,
+ uint32_t size,
+ uint32_t initial_domain);
+
+cairo_private cairo_status_t
+intel_bo_init_for_name (const intel_device_t *dev,
+ intel_bo_t *bo,
+ uint32_t size,
+ uint32_t name);
+
+cairo_private cairo_surface_t *
+intel_bo_get_image (const intel_device_t *device,
+ intel_bo_t *bo,
+ const cairo_drm_surface_t *surface);
+
+cairo_private cairo_status_t
+intel_bo_put_image (intel_device_t *dev,
+ intel_bo_t *bo,
+ cairo_image_surface_t *src,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y);
+
+cairo_private void
+intel_surface_init (intel_surface_t *surface,
+ const cairo_surface_backend_t *backend,
+ cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height);
+
+cairo_private cairo_status_t
+intel_buffer_cache_init (intel_buffer_cache_t *cache,
+ intel_device_t *device,
+ cairo_format_t format,
+ int width, int height);
+
+cairo_private cairo_status_t
+intel_gradient_render (intel_device_t *device,
+ const cairo_gradient_pattern_t *pattern,
+ intel_buffer_t *buffer);
+
+cairo_private cairo_int_status_t
+intel_get_glyph (intel_device_t *device,
+ cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph);
+
+cairo_private void
+intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+intel_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+intel_glyph_cache_unpin (intel_device_t *device);
+
+static inline intel_glyph_t *
+intel_glyph_pin (intel_glyph_t *glyph)
+{
+ cairo_rtree_node_t *node = &glyph->node;
+ if (unlikely (node->pinned == 0))
+ return _cairo_rtree_pin (&glyph->cache->rtree, node);
+ return glyph;
+}
+
+cairo_private cairo_status_t
+intel_snapshot_cache_insert (intel_device_t *device,
+ intel_surface_t *surface);
+
+cairo_private void
+intel_surface_detach_snapshot (cairo_surface_t *abstract_surface);
+
+cairo_private void
+intel_snapshot_cache_thaw (intel_device_t *device);
+
+cairo_private void
+intel_throttle (intel_device_t *device);
+
+cairo_private cairo_status_t
+intel_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+cairo_private void
+intel_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra);
+cairo_private cairo_surface_t *
+intel_surface_map_to_image (void *abstract_surface);
+
+cairo_private cairo_status_t
+intel_surface_flush (void *abstract_surface,
+ unsigned flags);
+
+cairo_private cairo_status_t
+intel_surface_finish (void *abstract_surface);
+
+cairo_private void
+intel_dump_batchbuffer (const void *batch,
+ uint32_t length,
+ int devid);
+
+static inline uint32_t cairo_const
+MS3_tiling (uint32_t tiling)
+{
+ switch (tiling) {
+ default:
+ case I915_TILING_NONE: return 0;
+ case I915_TILING_X: return MS3_TILED_SURFACE;
+ case I915_TILING_Y: return MS3_TILED_SURFACE | MS3_TILE_WALK;
+ }
+}
+
+static inline float cairo_const
+texcoord_2d_16 (double x, double y)
+{
+ union {
+ uint32_t ui;
+ float f;
+ } u;
+ u.ui = (_cairo_half_from_float (y) << 16) | _cairo_half_from_float (x);
+ return u.f;
+}
+
+#define PCI_CHIP_I810 0x7121
+#define PCI_CHIP_I810_DC100 0x7123
+#define PCI_CHIP_I810_E 0x7125
+#define PCI_CHIP_I815 0x1132
+
+#define PCI_CHIP_I830_M 0x3577
+#define PCI_CHIP_845_G 0x2562
+#define PCI_CHIP_I855_GM 0x3582
+#define PCI_CHIP_I865_G 0x2572
+
+#define PCI_CHIP_I915_G 0x2582
+#define PCI_CHIP_E7221_G 0x258A
+#define PCI_CHIP_I915_GM 0x2592
+#define PCI_CHIP_I945_G 0x2772
+#define PCI_CHIP_I945_GM 0x27A2
+#define PCI_CHIP_I945_GME 0x27AE
+
+#define PCI_CHIP_Q35_G 0x29B2
+#define PCI_CHIP_G33_G 0x29C2
+#define PCI_CHIP_Q33_G 0x29D2
+
+#define PCI_CHIP_IGD_GM 0xA011
+#define PCI_CHIP_IGD_G 0xA001
+
+#define IS_IGDGM(devid) (devid == PCI_CHIP_IGD_GM)
+#define IS_IGDG(devid) (devid == PCI_CHIP_IGD_G)
+#define IS_IGD(devid) (IS_IGDG(devid) || IS_IGDGM(devid))
+
+#define PCI_CHIP_I965_G 0x29A2
+#define PCI_CHIP_I965_Q 0x2992
+#define PCI_CHIP_I965_G_1 0x2982
+#define PCI_CHIP_I946_GZ 0x2972
+#define PCI_CHIP_I965_GM 0x2A02
+#define PCI_CHIP_I965_GME 0x2A12
+
+#define PCI_CHIP_GM45_GM 0x2A42
+
+#define PCI_CHIP_IGD_E_G 0x2E02
+#define PCI_CHIP_Q45_G 0x2E12
+#define PCI_CHIP_G45_G 0x2E22
+#define PCI_CHIP_G41_G 0x2E32
+
+#define PCI_CHIP_ILD_G 0x0042
+#define PCI_CHIP_ILM_G 0x0046
+
+#define IS_MOBILE(devid) (devid == PCI_CHIP_I855_GM || \
+ devid == PCI_CHIP_I915_GM || \
+ devid == PCI_CHIP_I945_GM || \
+ devid == PCI_CHIP_I945_GME || \
+ devid == PCI_CHIP_I965_GM || \
+ devid == PCI_CHIP_I965_GME || \
+ devid == PCI_CHIP_GM45_GM || IS_IGD(devid))
+
+#define IS_G45(devid) (devid == PCI_CHIP_IGD_E_G || \
+ devid == PCI_CHIP_Q45_G || \
+ devid == PCI_CHIP_G45_G || \
+ devid == PCI_CHIP_G41_G)
+#define IS_GM45(devid) (devid == PCI_CHIP_GM45_GM)
+#define IS_G4X(devid) (IS_G45(devid) || IS_GM45(devid))
+
+#define IS_ILD(devid) (devid == PCI_CHIP_ILD_G)
+#define IS_ILM(devid) (devid == PCI_CHIP_ILM_G)
+#define IS_IRONLAKE(devid) (IS_ILD(devid) || IS_ILM(devid))
+
+#define IS_915(devid) (devid == PCI_CHIP_I915_G || \
+ devid == PCI_CHIP_E7221_G || \
+ devid == PCI_CHIP_I915_GM)
+
+#define IS_945(devid) (devid == PCI_CHIP_I945_G || \
+ devid == PCI_CHIP_I945_GM || \
+ devid == PCI_CHIP_I945_GME || \
+ devid == PCI_CHIP_G33_G || \
+ devid == PCI_CHIP_Q33_G || \
+ devid == PCI_CHIP_Q35_G || IS_IGD(devid))
+
+#define IS_965(devid) (devid == PCI_CHIP_I965_G || \
+ devid == PCI_CHIP_I965_Q || \
+ devid == PCI_CHIP_I965_G_1 || \
+ devid == PCI_CHIP_I965_GM || \
+ devid == PCI_CHIP_I965_GME || \
+ devid == PCI_CHIP_I946_GZ || \
+ IS_G4X(devid) || \
+ IS_IRONLAKE(devid))
+
+#define IS_9XX(devid) (IS_915(devid) || \
+ IS_945(devid) || \
+ IS_965(devid))
+
+
+#endif /* CAIRO_DRM_INTEL_PRIVATE_H */
diff --git a/src/drm/cairo-drm-intel-surface.c b/src/drm/cairo-drm-intel-surface.c
new file mode 100755
index 000000000..88f5b8f0c
--- /dev/null
+++ b/src/drm/cairo-drm-intel-surface.c
@@ -0,0 +1,451 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-intel-private.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+
+/* Basic generic/stub surface for intel chipsets */
+
+#define MAX_SIZE 2048
+
+static cairo_surface_t *
+intel_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ return cairo_image_surface_create (_cairo_format_from_content (content),
+ width, height);
+}
+
+cairo_status_t
+intel_surface_finish (void *abstract_surface)
+{
+ intel_surface_t *surface = abstract_surface;
+
+ intel_bo_in_flight_add (to_intel_device (surface->drm.base.device),
+ to_intel_bo (surface->drm.bo));
+ return _cairo_drm_surface_finish (&surface->drm);
+}
+
+static void
+surface_finish_and_destroy (cairo_surface_t *surface)
+{
+ cairo_surface_finish (surface);
+ cairo_surface_destroy (surface);
+}
+
+cairo_status_t
+intel_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ intel_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+ cairo_status_t status;
+ void *ptr;
+
+ if (surface->drm.fallback != NULL) {
+ image = surface->drm.fallback;
+ goto DONE;
+ }
+
+ image = _cairo_surface_has_snapshot (&surface->drm.base,
+ &_cairo_image_surface_backend);
+ if (image != NULL)
+ goto DONE;
+
+ if (surface->drm.base.backend->flush != NULL) {
+ status = surface->drm.base.backend->flush (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ ptr = intel_bo_map (to_intel_device (surface->drm.base.device),
+ to_intel_bo (surface->drm.bo));
+ if (unlikely (ptr == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ image = cairo_image_surface_create_for_data (ptr,
+ surface->drm.format,
+ surface->drm.width,
+ surface->drm.height,
+ surface->drm.stride);
+ if (unlikely (image->status))
+ return image->status;
+
+ _cairo_surface_attach_snapshot (&surface->drm.base, image, surface_finish_and_destroy);
+
+DONE:
+ *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+intel_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+cairo_surface_t *
+intel_surface_map_to_image (void *abstract_surface)
+{
+ intel_surface_t *surface = abstract_surface;
+
+ if (surface->drm.fallback == NULL) {
+ cairo_surface_t *image;
+ cairo_status_t status;
+ void *ptr;
+
+ if (surface->drm.base.backend->flush != NULL) {
+ status = surface->drm.base.backend->flush (surface);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+ }
+
+ ptr = intel_bo_map (to_intel_device (surface->drm.base.device),
+ to_intel_bo (surface->drm.bo));
+ if (unlikely (ptr == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ image = cairo_image_surface_create_for_data (ptr,
+ surface->drm.format,
+ surface->drm.width,
+ surface->drm.height,
+ surface->drm.stride);
+ if (unlikely (image->status))
+ return image;
+
+ surface->drm.fallback = image;
+ }
+
+ return surface->drm.fallback;
+}
+
+cairo_status_t
+intel_surface_flush (void *abstract_surface, unsigned flags)
+{
+ intel_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->drm.fallback == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* kill any outstanding maps */
+ cairo_surface_finish (surface->drm.fallback);
+
+ status = cairo_surface_status (surface->drm.fallback);
+ cairo_surface_destroy (surface->drm.fallback);
+ surface->drm.fallback = NULL;
+
+ return status;
+}
+
+static cairo_int_status_t
+intel_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_paint (intel_surface_map_to_image (abstract_surface),
+ op, source, clip);
+}
+
+static cairo_int_status_t
+intel_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_mask (intel_surface_map_to_image (abstract_surface),
+ op, source, mask, clip);
+}
+
+static cairo_int_status_t
+intel_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_stroke (intel_surface_map_to_image (abstract_surface),
+ op, source, path, stroke_style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+intel_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_fill (intel_surface_map_to_image (abstract_surface),
+ op, source, path, fill_rule,
+ tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+intel_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ *num_remaining = 0;
+ return _cairo_surface_show_text_glyphs (intel_surface_map_to_image (abstract_surface),
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font, clip);
+}
+
+static const cairo_surface_backend_t intel_surface_backend = {
+ CAIRO_SURFACE_TYPE_DRM,
+ _cairo_default_context_create,
+
+ intel_surface_create_similar,
+ intel_surface_finish,
+
+ NULL,
+ intel_surface_acquire_source_image,
+ intel_surface_release_source_image,
+
+ NULL, NULL, NULL,
+ NULL, /* composite */
+ NULL, /* fill */
+ NULL, /* trapezoids */
+ NULL, /* span */
+ NULL, /* check-span */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_drm_surface_get_extents,
+ NULL, /* old-glyphs */
+ _cairo_drm_surface_get_font_options,
+
+ intel_surface_flush,
+ NULL, /* mark dirty */
+ NULL, NULL, /* font/glyph fini */
+
+ intel_surface_paint,
+ intel_surface_mask,
+ intel_surface_stroke,
+ intel_surface_fill,
+ intel_surface_glyphs,
+};
+
+void
+intel_surface_init (intel_surface_t *surface,
+ const cairo_surface_backend_t *backend,
+ cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ _cairo_surface_init (&surface->drm.base,
+ backend,
+ &device->base,
+ _cairo_content_from_format (format));
+ _cairo_drm_surface_init (&surface->drm, format, width, height);
+
+ surface->snapshot_cache_entry.hash = 0;
+}
+
+static cairo_surface_t *
+intel_surface_create (cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ intel_surface_t *surface;
+ cairo_status_t status;
+
+ surface = malloc (sizeof (intel_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ intel_surface_init (surface, &intel_surface_backend, device,
+ format, width, height);
+
+ if (width && height) {
+ /* Vol I, p134: size restrictions for textures */
+ width = (width + 3) & -4;
+ height = (height + 1) & -2;
+ surface->drm.stride =
+ cairo_format_stride_for_width (surface->drm.format, width);
+ surface->drm.bo = &intel_bo_create (to_intel_device (&device->base),
+ surface->drm.stride * height,
+ surface->drm.stride * height,
+ TRUE, I915_TILING_NONE, surface->drm.stride)->base;
+ if (surface->drm.bo == NULL) {
+ status = _cairo_drm_surface_finish (&surface->drm);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ return &surface->drm.base;
+}
+
+static cairo_surface_t *
+intel_surface_create_for_name (cairo_drm_device_t *device,
+ unsigned int name,
+ cairo_format_t format,
+ int width, int height, int stride)
+{
+ intel_surface_t *surface;
+ cairo_status_t status;
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_A8:
+ break;
+ }
+
+ if (stride < cairo_format_stride_for_width (format, width))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+ surface = malloc (sizeof (intel_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ intel_surface_init (surface, &intel_surface_backend,
+ device, format, width, height);
+
+ if (width && height) {
+ surface->drm.stride = stride;
+
+ surface->drm.bo = &intel_bo_create_for_name (to_intel_device (&device->base),
+ name)->base;
+ if (unlikely (surface->drm.bo == NULL)) {
+ status = _cairo_drm_surface_finish (&surface->drm);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error
+ (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ return &surface->drm.base;
+}
+
+static cairo_status_t
+intel_surface_enable_scan_out (void *abstract_surface)
+{
+ intel_surface_t *surface = abstract_surface;
+
+ if (unlikely (surface->drm.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+
+ to_intel_bo (surface->drm.bo)->tiling = I915_TILING_X;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+intel_device_throttle (cairo_drm_device_t *device)
+{
+ intel_throttle (to_intel_device (&device->base));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+intel_device_destroy (void *data)
+{
+ intel_device_t *device = data;
+
+ intel_device_fini (device);
+
+ free (data);
+}
+
+cairo_drm_device_t *
+_cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+ intel_device_t *device;
+ cairo_status_t status;
+
+ if (! intel_info (fd, NULL))
+ return NULL;
+
+ device = malloc (sizeof (intel_device_t));
+ if (unlikely (device == NULL))
+ return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = intel_device_init (device, fd);
+ if (unlikely (status)) {
+ free (device);
+ return (cairo_drm_device_t *) _cairo_device_create_in_error (status);
+ }
+
+ device->base.surface.create = intel_surface_create;
+ device->base.surface.create_for_name = intel_surface_create_for_name;
+ device->base.surface.create_from_cacheable_image = NULL;
+ device->base.surface.flink = _cairo_drm_surface_flink;
+ device->base.surface.enable_scan_out = intel_surface_enable_scan_out;
+
+ device->base.surface.map_to_image = intel_surface_map_to_image;
+
+ device->base.device.flush = NULL;
+ device->base.device.throttle = intel_device_throttle;
+ device->base.device.destroy = intel_device_destroy;
+
+ return _cairo_drm_device_init (&device->base,
+ fd, dev,
+ vendor_id, chip_id,
+ MAX_SIZE);
+}
diff --git a/src/drm/cairo-drm-intel.c b/src/drm/cairo-drm-intel.c
new file mode 100755
index 000000000..d45155ebe
--- /dev/null
+++ b/src/drm/cairo-drm-intel.c
@@ -0,0 +1,1389 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#define GLYPH_CACHE_WIDTH 1024
+#define GLYPH_CACHE_HEIGHT 1024
+#define GLYPH_CACHE_MIN_SIZE 1
+#define GLYPH_CACHE_MAX_SIZE 128
+
+#define IMAGE_CACHE_WIDTH 1024
+#define IMAGE_CACHE_HEIGHT 1024
+
+int
+intel_get (int fd, int param)
+{
+ struct intel_getparam gp;
+ int value;
+
+ gp.param = param;
+ gp.value = &value;
+ if (ioctl (fd, DRM_IOCTL_I915_GETPARAM, &gp) < 0)
+ return 0;
+
+ VG (VALGRIND_MAKE_MEM_DEFINED (&value, sizeof (value)));
+
+ return value;
+}
+
+cairo_bool_t
+intel_info (int fd, uint64_t *gtt_size)
+{
+ struct drm_i915_gem_get_aperture info;
+
+ if (! intel_get (fd, I915_PARAM_HAS_GEM))
+ return FALSE;
+
+ if (! intel_get (fd, I915_PARAM_HAS_EXECBUF2))
+ return FALSE;
+
+ if (ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &info) < 0)
+ return FALSE;
+
+ VG (VALGRIND_MAKE_MEM_DEFINED (&info, sizeof (info)));
+
+ if (gtt_size != NULL)
+ *gtt_size = info.aper_size;
+
+ return TRUE;
+}
+
+void
+intel_bo_write (const intel_device_t *device,
+ intel_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ const void *data)
+{
+ struct drm_i915_gem_pwrite pwrite;
+ int ret;
+
+ assert (bo->tiling == I915_TILING_NONE);
+ assert (size);
+ assert (offset < bo->base.size);
+ assert (size+offset <= bo->base.size);
+
+ intel_bo_set_tiling (device, bo);
+
+ assert (bo->_tiling == I915_TILING_NONE);
+
+ memset (&pwrite, 0, sizeof (pwrite));
+ pwrite.handle = bo->base.handle;
+ pwrite.offset = offset;
+ pwrite.size = size;
+ pwrite.data_ptr = (uint64_t) (uintptr_t) data;
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite);
+ } while (ret == -1 && errno == EINTR);
+ assert (ret == 0);
+
+ bo->busy = FALSE;
+}
+
+void
+intel_bo_read (const intel_device_t *device,
+ intel_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ void *data)
+{
+ struct drm_i915_gem_pread pread;
+ int ret;
+
+ assert (bo->tiling == I915_TILING_NONE);
+ assert (size);
+ assert (offset < bo->base.size);
+ assert (size+offset <= bo->base.size);
+
+ intel_bo_set_tiling (device, bo);
+
+ assert (bo->_tiling == I915_TILING_NONE);
+
+ memset (&pread, 0, sizeof (pread));
+ pread.handle = bo->base.handle;
+ pread.offset = offset;
+ pread.size = size;
+ pread.data_ptr = (uint64_t) (uintptr_t) data;
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PREAD, &pread);
+ } while (ret == -1 && errno == EINTR);
+ assert (ret == 0);
+
+ bo->cpu = TRUE;
+ bo->busy = FALSE;
+}
+
+void *
+intel_bo_map (const intel_device_t *device, intel_bo_t *bo)
+{
+ struct drm_i915_gem_set_domain set_domain;
+ uint32_t domain;
+ int ret;
+
+ intel_bo_set_tiling (device, bo);
+
+ if (bo->virtual != NULL)
+ return bo->virtual;
+
+ if (bo->cpu && bo->tiling == I915_TILING_NONE) {
+ struct drm_i915_gem_mmap mmap_arg;
+
+ mmap_arg.handle = bo->base.handle;
+ mmap_arg.offset = 0;
+ mmap_arg.size = bo->base.size;
+ mmap_arg.addr_ptr = 0;
+
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg);
+ } while (ret == -1 && errno == EINTR);
+ if (unlikely (ret != 0)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ bo->virtual = (void *) (uintptr_t) mmap_arg.addr_ptr;
+ domain = I915_GEM_DOMAIN_CPU;
+ } else {
+ struct drm_i915_gem_mmap_gtt mmap_arg;
+ void *ptr;
+
+ /* Get the fake offset back... */
+ mmap_arg.handle = bo->base.handle;
+ do {
+ ret = ioctl (device->base.fd,
+ DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg);
+ } while (ret == -1 && errno == EINTR);
+ if (unlikely (ret != 0)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ /* and mmap it */
+ ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, device->base.fd,
+ mmap_arg.offset);
+ if (unlikely (ptr == MAP_FAILED)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ bo->virtual = ptr;
+ domain = I915_GEM_DOMAIN_GTT;
+ }
+
+ VG (VALGRIND_MAKE_MEM_DEFINED (bo->virtual, bo->base.size));
+
+ set_domain.handle = bo->base.handle;
+ set_domain.read_domains = domain;
+ set_domain.write_domain = domain;
+
+ do {
+ ret = ioctl (device->base.fd,
+ DRM_IOCTL_I915_GEM_SET_DOMAIN,
+ &set_domain);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret != 0) {
+ intel_bo_unmap (bo);
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR);
+ return NULL;
+ }
+
+ bo->busy = FALSE;
+ return bo->virtual;
+}
+
+void
+intel_bo_unmap (intel_bo_t *bo)
+{
+ munmap (bo->virtual, bo->base.size);
+ bo->virtual = NULL;
+}
+
+cairo_bool_t
+intel_bo_is_inactive (const intel_device_t *device, intel_bo_t *bo)
+{
+ struct drm_i915_gem_busy busy;
+
+ if (! bo->busy)
+ return TRUE;
+
+ /* Is this buffer busy for our intended usage pattern? */
+ busy.handle = bo->base.handle;
+ busy.busy = 1;
+ ioctl (device->base.fd, DRM_IOCTL_I915_GEM_BUSY, &busy);
+
+ bo->busy = busy.busy;
+ return ! busy.busy;
+}
+
+cairo_bool_t
+intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo)
+{
+ struct drm_i915_gem_set_domain set_domain;
+ int ret;
+
+ set_domain.handle = bo->base.handle;
+ set_domain.read_domains = I915_GEM_DOMAIN_GTT;
+ set_domain.write_domain = 0;
+
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain);
+ } while (ret == -1 && errno == EINTR);
+
+ return ret == 0;
+}
+
+static inline int
+pot (int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+cairo_bool_t
+intel_bo_madvise (intel_device_t *device,
+ intel_bo_t *bo,
+ int advice)
+{
+ struct drm_i915_gem_madvise madv;
+
+ madv.handle = bo->base.handle;
+ madv.madv = advice;
+ madv.retained = TRUE;
+ ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MADVISE, &madv);
+ return madv.retained;
+}
+
+static void
+intel_bo_set_real_size (intel_device_t *device,
+ intel_bo_t *bo,
+ size_t size)
+{
+ struct drm_i915_gem_real_size arg;
+ int ret;
+
+ return;
+
+ if (size == bo->base.size)
+ return;
+
+ arg.handle = bo->base.handle;
+ arg.size = size;
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_REAL_SIZE, &arg);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == 0) {
+ if (size > bo->base.size) {
+ assert (bo->exec == NULL);
+ bo->cpu = TRUE;
+ bo->busy = FALSE;
+ }
+
+ bo->base.size = size;
+ }
+}
+
+intel_bo_t *
+intel_bo_create (intel_device_t *device,
+ uint32_t max_size,
+ uint32_t real_size,
+ cairo_bool_t gpu_target,
+ uint32_t tiling,
+ uint32_t stride)
+{
+ intel_bo_t *bo;
+ uint32_t cache_size;
+ struct drm_i915_gem_create create;
+ int bucket;
+ int ret;
+
+ max_size = (max_size + 4095) & -4096;
+ real_size = (real_size + 4095) & -4096;
+ cache_size = pot (max_size);
+ bucket = ffs (cache_size / 4096) - 1;
+ if (bucket >= INTEL_BO_CACHE_BUCKETS)
+ cache_size = max_size;
+
+ if (gpu_target) {
+ intel_bo_t *first = NULL;
+
+ cairo_list_foreach_entry (bo, intel_bo_t,
+ &device->bo_in_flight,
+ cache_list)
+ {
+ assert (bo->exec != NULL);
+ if (tiling && bo->_tiling &&
+ (bo->_tiling != tiling || bo->_stride != stride))
+ {
+ continue;
+ }
+
+ if (real_size <= bo->base.size) {
+ if (real_size >= bo->base.size/2) {
+ cairo_list_del (&bo->cache_list);
+ bo = intel_bo_reference (bo);
+ goto DONE;
+ }
+
+ if (first == NULL)
+ first = bo;
+ }
+ }
+
+ if (first != NULL) {
+ cairo_list_del (&first->cache_list);
+ bo = intel_bo_reference (first);
+ goto DONE;
+ }
+ }
+
+ /* no cached buffer available, allocate fresh */
+ bo = _cairo_freepool_alloc (&device->bo_pool);
+ if (unlikely (bo == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return bo;
+ }
+
+ cairo_list_init (&bo->cache_list);
+
+ bo->base.name = 0;
+
+ bo->offset = 0;
+ bo->virtual = NULL;
+ bo->cpu = TRUE;
+
+ bo->_tiling = I915_TILING_NONE;
+ bo->_stride = 0;
+ bo->purgeable = 0;
+ bo->busy = FALSE;
+
+ bo->opaque0 = 0;
+ bo->opaque1 = 0;
+
+ bo->exec = NULL;
+ bo->batch_read_domains = 0;
+ bo->batch_write_domain = 0;
+ cairo_list_init (&bo->link);
+
+ create.size = cache_size;
+ create.handle = 0;
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_CREATE, &create);
+ if (unlikely (ret != 0)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ _cairo_freepool_free (&device->bo_pool, bo);
+ return NULL;
+ }
+
+ bo->base.handle = create.handle;
+ bo->full_size = bo->base.size = create.size;
+
+ intel_bo_set_real_size (device, bo, real_size);
+ CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+DONE:
+ bo->tiling = tiling;
+ bo->stride = stride;
+ return bo;
+}
+
+intel_bo_t *
+intel_bo_create_for_name (intel_device_t *device, uint32_t name)
+{
+ struct drm_i915_gem_get_tiling get_tiling;
+ cairo_status_t status;
+ intel_bo_t *bo;
+ int ret;
+
+ bo = _cairo_freepool_alloc (&device->bo_pool);
+ if (unlikely (bo == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name);
+ if (unlikely (status))
+ goto FAIL;
+
+ CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+ cairo_list_init (&bo->cache_list);
+
+ bo->full_size = bo->base.size;
+ bo->offset = 0;
+ bo->virtual = NULL;
+ bo->purgeable = 0;
+ bo->busy = TRUE;
+ bo->cpu = FALSE;
+
+ bo->opaque0 = 0;
+ bo->opaque1 = 0;
+
+ bo->exec = NULL;
+ bo->batch_read_domains = 0;
+ bo->batch_write_domain = 0;
+ cairo_list_init (&bo->link);
+
+ memset (&get_tiling, 0, sizeof (get_tiling));
+ get_tiling.handle = bo->base.handle;
+
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_GET_TILING, &get_tiling);
+ if (unlikely (ret != 0)) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR);
+ _cairo_drm_bo_close (&device->base, &bo->base);
+ goto FAIL;
+ }
+
+ bo->_tiling = bo->tiling = get_tiling.tiling_mode;
+ // bo->stride = get_tiling.stride; /* XXX not available from get_tiling */
+
+ return bo;
+
+FAIL:
+ _cairo_freepool_free (&device->bo_pool, bo);
+ return NULL;
+}
+
+static void
+intel_bo_release (void *_dev, void *_bo)
+{
+ intel_device_t *device = _dev;
+ intel_bo_t *bo = _bo;
+
+ if (bo->virtual != NULL)
+ intel_bo_unmap (bo);
+
+ assert (bo->exec == NULL);
+ assert (cairo_list_is_empty (&bo->cache_list));
+
+ _cairo_drm_bo_close (&device->base, &bo->base);
+ _cairo_freepool_free (&device->bo_pool, bo);
+}
+
+void
+intel_bo_set_tiling (const intel_device_t *device,
+ intel_bo_t *bo)
+{
+ struct drm_i915_gem_set_tiling set_tiling;
+ int ret;
+
+ if (bo->tiling == bo->_tiling &&
+ (bo->tiling == I915_TILING_NONE || bo->stride == bo->_stride))
+ return;
+
+ do {
+ set_tiling.handle = bo->base.handle;
+ set_tiling.tiling_mode = bo->tiling;
+ set_tiling.stride = bo->stride;
+
+ ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling);
+ } while (ret == -1 && errno == EINTR);
+
+ assert (ret == 0);
+ bo->_tiling = bo->tiling;
+ bo->_stride = bo->stride;
+}
+
+cairo_surface_t *
+intel_bo_get_image (const intel_device_t *device,
+ intel_bo_t *bo,
+ const cairo_drm_surface_t *surface)
+{
+ cairo_image_surface_t *image;
+ uint8_t *dst;
+ int size, row;
+
+ image = (cairo_image_surface_t *)
+ cairo_image_surface_create (surface->format,
+ surface->width,
+ surface->height);
+ if (unlikely (image->base.status))
+ return &image->base;
+
+ intel_bo_set_tiling (device, bo);
+
+ if (bo->tiling == I915_TILING_NONE && image->stride == surface->stride) {
+ size = surface->stride * surface->height;
+ intel_bo_read (device, bo, 0, size, image->data);
+ } else {
+ const uint8_t *src;
+
+ src = intel_bo_map (device, bo);
+ if (unlikely (src == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ size = surface->width;
+ if (surface->format != CAIRO_FORMAT_A8)
+ size *= 4;
+
+ row = surface->height;
+ dst = image->data;
+ while (row--) {
+ memcpy (dst, src, size);
+ dst += image->stride;
+ src += surface->stride;
+ }
+ }
+
+ return &image->base;
+}
+
+static cairo_status_t
+_intel_bo_put_a1_image (intel_device_t *device,
+ intel_bo_t *bo,
+ cairo_image_surface_t *src,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y)
+{
+ uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+ uint8_t *a8 = buf;
+ uint8_t *data;
+ int x;
+
+ data = src->data + src_y * src->stride;
+
+ if (bo->tiling == I915_TILING_NONE && width == bo->stride) {
+ uint8_t *p;
+ int size;
+
+ size = bo->stride * height;
+ if (size > (int) sizeof (buf)) {
+ a8 = _cairo_malloc_ab (bo->stride, height);
+ if (a8 == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ p = a8;
+ while (height--) {
+ for (x = 0; x < width; x++) {
+ int i = src_x + x;
+ int byte = i / 8;
+ int bit = i % 8;
+ p[x] = data[byte] & (1 << bit) ? 0xff : 0x00;
+ }
+
+ data += src->stride;
+ p += bo->stride;
+ }
+
+ intel_bo_write (device, bo,
+ dst_y * bo->stride + dst_x, /* XXX bo_offset */
+ size, a8);
+ } else {
+ uint8_t *dst;
+
+ if (width > (int) sizeof (buf)) {
+ a8 = malloc (width);
+ if (a8 == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ dst = intel_bo_map (device, bo);
+ if (dst == NULL) {
+ if (a8 != buf)
+ free (a8);
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+ }
+
+ dst += dst_y * bo->stride + dst_x; /* XXX bo_offset */
+ while (height--) {
+ for (x = 0; x < width; x++) {
+ int i = src_x + x;
+ int byte = i / 8;
+ int bit = i % 8;
+ a8[x] = data[byte] & (1 << bit) ? 0xff : 0x00;
+ }
+
+ memcpy (dst, a8, width);
+ dst += bo->stride;
+ data += src->stride;
+ }
+ }
+
+ if (a8 != buf)
+ free (a8);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+intel_bo_put_image (intel_device_t *device,
+ intel_bo_t *bo,
+ cairo_image_surface_t *src,
+ int src_x, int src_y,
+ int width, int height,
+ int dst_x, int dst_y)
+{
+ uint8_t *data;
+ int size;
+ int offset;
+
+ intel_bo_set_tiling (device, bo);
+
+ offset = dst_y * bo->stride;
+ data = src->data + src_y * src->stride;
+ switch (src->format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ offset += 4 * dst_x;
+ data += 4 * src_x;
+ size = 4 * width;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ offset += 2 * dst_x;
+ data += 2 * src_x;
+ size = 2 * width;
+ break;
+ case CAIRO_FORMAT_A8:
+ offset += dst_x;
+ data += src_x;
+ size = width;
+ break;
+ case CAIRO_FORMAT_A1:
+ return _intel_bo_put_a1_image (device, bo, src,
+ src_x, src_y,
+ width, height,
+ dst_x, dst_y);
+ default:
+ case CAIRO_FORMAT_INVALID:
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ }
+
+ if (bo->tiling == I915_TILING_NONE && src->stride == bo->stride) {
+ intel_bo_write (device, bo, offset, bo->stride * height, data);
+ } else {
+ uint8_t *dst;
+
+ dst = intel_bo_map (device, bo);
+ if (unlikely (dst == NULL))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ dst += offset;
+ while (height--) {
+ memcpy (dst, data, size);
+ dst += bo->stride;
+ data += src->stride;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_intel_snapshot_cache_entry_can_remove (const void *closure)
+{
+ return TRUE;
+}
+
+static void
+_intel_snapshot_cache_entry_destroy (void *closure)
+{
+ intel_surface_t *surface = cairo_container_of (closure,
+ intel_surface_t,
+ snapshot_cache_entry);
+
+ surface->snapshot_cache_entry.hash = 0;
+}
+
+cairo_status_t
+intel_device_init (intel_device_t *device, int fd)
+{
+ struct drm_i915_gem_get_aperture aperture;
+ cairo_status_t status;
+ size_t size;
+ int ret;
+ int n;
+
+ ret = ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture);
+ if (ret != 0)
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ CAIRO_MUTEX_INIT (device->mutex);
+
+ device->gtt_max_size = aperture.aper_size;
+ device->gtt_avail_size = aperture.aper_available_size;
+ device->gtt_avail_size -= device->gtt_avail_size >> 5;
+
+ size = aperture.aper_size / 8;
+ device->snapshot_cache_max_size = size / 4;
+ status = _cairo_cache_init (&device->snapshot_cache,
+ NULL,
+ _intel_snapshot_cache_entry_can_remove,
+ _intel_snapshot_cache_entry_destroy,
+ size);
+ if (unlikely (status))
+ return status;
+
+ for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) {
+ device->glyph_cache[n].buffer.bo = NULL;
+ cairo_list_init (&device->glyph_cache[n].rtree.pinned);
+ }
+ cairo_list_init (&device->fonts);
+
+ device->gradient_cache.size = 0;
+
+ device->base.bo.release = intel_bo_release;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_intel_gradient_cache_fini (intel_device_t *device)
+{
+ unsigned int n;
+
+ for (n = 0; n < device->gradient_cache.size; n++) {
+ _cairo_pattern_fini (&device->gradient_cache.cache[n].pattern.base);
+ if (device->gradient_cache.cache[n].buffer.bo != NULL)
+ cairo_drm_bo_destroy (&device->base.base,
+ &device->gradient_cache.cache[n].buffer.bo->base);
+ }
+}
+
+static void
+_intel_glyph_cache_fini (intel_device_t *device, intel_buffer_cache_t *cache)
+{
+ if (cache->buffer.bo == NULL)
+ return;
+
+ intel_bo_destroy (device, cache->buffer.bo);
+ _cairo_rtree_fini (&cache->rtree);
+}
+
+void
+intel_device_fini (intel_device_t *device)
+{
+ cairo_scaled_font_t *scaled_font, *next_scaled_font;
+ int n;
+
+ cairo_list_foreach_entry_safe (scaled_font,
+ next_scaled_font,
+ cairo_scaled_font_t,
+ &device->fonts,
+ link)
+ {
+ _cairo_scaled_font_revoke_ownership (scaled_font);
+ }
+
+ for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++)
+ _intel_glyph_cache_fini (device, &device->glyph_cache[n]);
+
+ _cairo_cache_fini (&device->snapshot_cache);
+
+ _intel_gradient_cache_fini (device);
+ _cairo_freepool_fini (&device->bo_pool);
+
+ _cairo_drm_device_fini (&device->base);
+}
+
+void
+intel_throttle (intel_device_t *device)
+{
+ ioctl (device->base.fd, DRM_IOCTL_I915_GEM_THROTTLE);
+}
+
+void
+intel_glyph_cache_unpin (intel_device_t *device)
+{
+ int n;
+
+ for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++)
+ _cairo_rtree_unpin (&device->glyph_cache[n].rtree);
+}
+
+static cairo_status_t
+intel_glyph_cache_add_glyph (intel_device_t *device,
+ intel_buffer_cache_t *cache,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+ intel_glyph_t *glyph;
+ cairo_rtree_node_t *node = NULL;
+ double sf_x, sf_y;
+ cairo_status_t status;
+ uint8_t *dst, *src;
+ int width, height;
+
+ width = glyph_surface->width;
+ if (width < GLYPH_CACHE_MIN_SIZE)
+ width = GLYPH_CACHE_MIN_SIZE;
+ height = glyph_surface->height;
+ if (height < GLYPH_CACHE_MIN_SIZE)
+ height = GLYPH_CACHE_MIN_SIZE;
+
+ /* search for an available slot */
+ status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+ /* search for an unpinned slot */
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_rtree_evict_random (&cache->rtree, width, height, &node);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_rtree_node_insert (&cache->rtree, node, width, height, &node);
+ }
+ if (unlikely (status))
+ return status;
+
+ /* XXX streaming upload? */
+
+ height = glyph_surface->height;
+ src = glyph_surface->data;
+ dst = cache->buffer.bo->virtual;
+ if (dst == NULL) {
+ dst = intel_bo_map (device, cache->buffer.bo);
+ if (unlikely (dst == NULL))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+ }
+
+ dst += node->y * cache->buffer.stride;
+ switch (glyph_surface->format) {
+ case CAIRO_FORMAT_A1: {
+ uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+ uint8_t *a8 = buf;
+ int x;
+
+ if (width > (int) sizeof (buf)) {
+ a8 = malloc (width);
+ if (unlikely (a8 == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ dst += node->x;
+ width = glyph_surface->width;
+ while (height--) {
+ for (x = 0; x < width; x++)
+ a8[x] = src[x>>3] & (1 << (x&7)) ? 0xff : 0x00;
+
+ memcpy (dst, a8, width);
+ dst += cache->buffer.stride;
+ src += glyph_surface->stride;
+ }
+
+ if (a8 != buf)
+ free (a8);
+ break;
+ }
+
+ case CAIRO_FORMAT_A8:
+ dst += node->x;
+ width = glyph_surface->width;
+ while (height--) {
+ memcpy (dst, src, width);
+ dst += cache->buffer.stride;
+ src += glyph_surface->stride;
+ }
+ break;
+
+ case CAIRO_FORMAT_ARGB32:
+ dst += 4*node->x;
+ width = 4*glyph_surface->width;
+ while (height--) {
+ memcpy (dst, src, width);
+ dst += cache->buffer.stride;
+ src += glyph_surface->stride;
+ }
+ break;
+ default:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ }
+
+ scaled_glyph->surface_private = node;
+
+ glyph= (intel_glyph_t *) node;
+ glyph->node.owner = &scaled_glyph->surface_private;
+ glyph->cache = cache;
+
+ /* compute tex coords: bottom-right, bottom-left, top-left */
+ sf_x = 1. / cache->buffer.width;
+ sf_y = 1. / cache->buffer.height;
+ glyph->texcoord[0] =
+ texcoord_2d_16 (sf_x * (node->x + glyph_surface->width),
+ sf_y * (node->y + glyph_surface->height));
+ glyph->texcoord[1] =
+ texcoord_2d_16 (sf_x * node->x,
+ sf_y * (node->y + glyph_surface->height));
+ glyph->texcoord[2] =
+ texcoord_2d_16 (sf_x * node->x,
+ sf_y * node->y);
+
+ glyph->width = glyph_surface->width;
+ glyph->height = glyph_surface->height;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font)
+{
+ intel_glyph_t *glyph;
+
+ glyph = scaled_glyph->surface_private;
+ if (glyph != NULL) {
+ /* XXX thread-safety? Probably ok due to the frozen scaled-font. */
+ glyph->node.owner = NULL;
+ if (! glyph->node.pinned)
+ _cairo_rtree_node_remove (&glyph->cache->rtree, &glyph->node);
+ }
+}
+
+void
+intel_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ cairo_list_del (&scaled_font->link);
+}
+
+static cairo_status_t
+intel_get_glyph_cache (intel_device_t *device,
+ cairo_format_t format,
+ intel_buffer_cache_t **out)
+{
+ intel_buffer_cache_t *cache;
+ cairo_status_t status;
+
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ cache = &device->glyph_cache[0];
+ format = CAIRO_FORMAT_ARGB32;
+ break;
+ case CAIRO_FORMAT_A8:
+ case CAIRO_FORMAT_A1:
+ cache = &device->glyph_cache[1];
+ format = CAIRO_FORMAT_A8;
+ break;
+ default:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ }
+
+ if (unlikely (cache->buffer.bo == NULL)) {
+ status = intel_buffer_cache_init (cache, device, format,
+ INTEL_GLYPH_CACHE_WIDTH,
+ INTEL_GLYPH_CACHE_HEIGHT);
+ if (unlikely (status))
+ return status;
+
+ _cairo_rtree_init (&cache->rtree,
+ INTEL_GLYPH_CACHE_WIDTH,
+ INTEL_GLYPH_CACHE_HEIGHT,
+ 0, sizeof (intel_glyph_t));
+ }
+
+ *out = cache;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+intel_get_glyph (intel_device_t *device,
+ cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_bool_t own_surface = FALSE;
+ intel_buffer_cache_t *cache;
+ cairo_status_t status;
+
+ if (scaled_glyph->surface == NULL) {
+ status =
+ scaled_font->backend->scaled_glyph_init (scaled_font,
+ scaled_glyph,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE);
+ if (unlikely (status))
+ return status;
+
+ if (unlikely (scaled_glyph->surface == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ own_surface = TRUE;
+ }
+
+ if (unlikely (scaled_glyph->surface->width == 0 ||
+ scaled_glyph->surface->height == 0))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ if (unlikely (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE ||
+ scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = intel_get_glyph_cache (device,
+ scaled_glyph->surface->format,
+ &cache);
+ if (unlikely (status))
+ return status;
+
+ status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+
+ if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) {
+ /* no room, replace entire cache */
+
+ assert (cache->buffer.bo->exec != NULL);
+
+ _cairo_rtree_reset (&cache->rtree);
+ intel_bo_destroy (device, cache->buffer.bo);
+ cache->buffer.bo = NULL;
+
+ status = intel_buffer_cache_init (cache, device,
+ scaled_glyph->surface->format,
+ GLYPH_CACHE_WIDTH,
+ GLYPH_CACHE_HEIGHT);
+ if (unlikely (status))
+ return status;
+
+ status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (own_surface) {
+ /* and release the copy of the image from system memory */
+ cairo_surface_destroy (&scaled_glyph->surface->base);
+ scaled_glyph->surface = NULL;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+intel_buffer_cache_init (intel_buffer_cache_t *cache,
+ intel_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ const uint32_t tiling = I915_TILING_Y;
+ uint32_t stride, size;
+
+ assert ((width & 3) == 0);
+ assert ((height & 1) == 0);
+ cache->buffer.format = format;
+ cache->buffer.width = width;
+ cache->buffer.height = height;
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ case CAIRO_FORMAT_ARGB32:
+ cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+ stride = width * 4;
+ break;
+ case CAIRO_FORMAT_A8:
+ cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8;
+ stride = width;
+ break;
+ }
+
+ size = height * stride;
+ cache->buffer.bo = intel_bo_create (device,
+ size, size,
+ FALSE, tiling, stride);
+ if (unlikely (cache->buffer.bo == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ cache->buffer.stride = stride;
+
+ cache->buffer.offset = 0;
+ cache->buffer.map0 |= MS3_tiling (tiling);
+ cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+ ((width - 1) << MS3_WIDTH_SHIFT);
+ cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT;
+
+ cache->ref_count = 0;
+ cairo_list_init (&cache->link);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+intel_snapshot_cache_insert (intel_device_t *device,
+ intel_surface_t *surface)
+{
+ cairo_status_t status;
+
+ surface->snapshot_cache_entry.size = surface->drm.bo->size;
+ if (surface->snapshot_cache_entry.size >
+ device->snapshot_cache_max_size)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (device->snapshot_cache.freeze_count == 0)
+ _cairo_cache_freeze (&device->snapshot_cache);
+
+ surface->snapshot_cache_entry.hash = (unsigned long) surface;
+ status = _cairo_cache_insert (&device->snapshot_cache,
+ &surface->snapshot_cache_entry);
+ if (unlikely (status)) {
+ surface->snapshot_cache_entry.hash = 0;
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+intel_surface_detach_snapshot (cairo_surface_t *abstract_surface)
+{
+ intel_surface_t *surface = (intel_surface_t *) abstract_surface;
+
+ if (surface->snapshot_cache_entry.hash) {
+ intel_device_t *device;
+
+ device = (intel_device_t *) surface->drm.base.device;
+ _cairo_cache_remove (&device->snapshot_cache,
+ &surface->snapshot_cache_entry);
+ assert (surface->snapshot_cache_entry.hash == 0);
+ }
+}
+
+void
+intel_snapshot_cache_thaw (intel_device_t *device)
+{
+ if (device->snapshot_cache.freeze_count)
+ _cairo_cache_thaw (&device->snapshot_cache);
+}
+
+static cairo_bool_t
+_gradient_color_stops_equal (const cairo_gradient_pattern_t *a,
+ const cairo_gradient_pattern_t *b)
+{
+ unsigned int n;
+
+ if (a->n_stops != b->n_stops)
+ return FALSE;
+
+ for (n = 0; n < a->n_stops; n++) {
+ if (_cairo_fixed_from_double (a->stops[n].offset) !=
+ _cairo_fixed_from_double (b->stops[n].offset))
+ {
+ return FALSE;
+ }
+
+ if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static int
+intel_gradient_sample_width (const cairo_gradient_pattern_t *gradient)
+{
+ unsigned int n;
+ int width;
+
+ width = 8;
+ for (n = 1; n < gradient->n_stops; n++) {
+ double dx = gradient->stops[n].offset - gradient->stops[n-1].offset;
+ double delta, max;
+ int ramp;
+
+ if (dx == 0)
+ continue;
+
+ max = gradient->stops[n].color.red -
+ gradient->stops[n-1].color.red;
+
+ delta = gradient->stops[n].color.green -
+ gradient->stops[n-1].color.green;
+ if (delta > max)
+ max = delta;
+
+ delta = gradient->stops[n].color.blue -
+ gradient->stops[n-1].color.blue;
+ if (delta > max)
+ max = delta;
+
+ delta = gradient->stops[n].color.alpha -
+ gradient->stops[n-1].color.alpha;
+ if (delta > max)
+ max = delta;
+
+ ramp = 128 * max / dx;
+ if (ramp > width)
+ width = ramp;
+ }
+
+ width = (width + 7) & -8;
+ return MIN (width, 1024);
+}
+
+cairo_status_t
+intel_gradient_render (intel_device_t *device,
+ const cairo_gradient_pattern_t *pattern,
+ intel_buffer_t *buffer)
+{
+ pixman_image_t *gradient, *image;
+ pixman_gradient_stop_t pixman_stops_stack[32];
+ pixman_gradient_stop_t *pixman_stops;
+ pixman_point_fixed_t p1, p2;
+ int width;
+ unsigned int i;
+ cairo_status_t status;
+
+ for (i = 0; i < device->gradient_cache.size; i++) {
+ if (_gradient_color_stops_equal (pattern,
+ &device->gradient_cache.cache[i].pattern.gradient.base)) {
+ *buffer = device->gradient_cache.cache[i].buffer;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ pixman_stops = pixman_stops_stack;
+ if (unlikely (pattern->n_stops > ARRAY_LENGTH (pixman_stops_stack))) {
+ pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+ sizeof (pixman_gradient_stop_t));
+ if (unlikely (pixman_stops == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < pattern->n_stops; i++) {
+ pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+ pixman_stops[i].color.red = pattern->stops[i].color.red_short;
+ pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+ pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
+ pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+ }
+
+ width = intel_gradient_sample_width (pattern);
+
+ p1.x = 0;
+ p1.y = 0;
+ p2.x = width << 16;
+ p2.y = 0;
+
+ gradient = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ pattern->n_stops);
+ if (pixman_stops != pixman_stops_stack)
+ free (pixman_stops);
+
+ if (unlikely (gradient == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
+ pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD);
+
+ image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, 1, NULL, 0);
+ if (unlikely (image == NULL)) {
+ pixman_image_unref (gradient);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ gradient, NULL, image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, 1);
+
+ pixman_image_unref (gradient);
+
+ buffer->bo = intel_bo_create (device,
+ 4*width, 4*width,
+ FALSE, I915_TILING_NONE, 4*width);
+ if (unlikely (buffer->bo == NULL)) {
+ pixman_image_unref (image);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ intel_bo_write (device, buffer->bo, 0, 4*width, pixman_image_get_data (image));
+ pixman_image_unref (image);
+
+ buffer->offset = 0;
+ buffer->width = width;
+ buffer->height = 1;
+ buffer->stride = 4*width;
+ buffer->format = CAIRO_FORMAT_ARGB32;
+ buffer->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+ buffer->map0 |= ((width - 1) << MS3_WIDTH_SHIFT);
+ buffer->map1 = (width - 1) << MS4_PITCH_SHIFT;
+
+ if (device->gradient_cache.size < GRADIENT_CACHE_SIZE) {
+ i = device->gradient_cache.size++;
+ } else {
+ i = hars_petruska_f54_1_random () % GRADIENT_CACHE_SIZE;
+ _cairo_pattern_fini (&device->gradient_cache.cache[i].pattern.base);
+ intel_bo_destroy (device, device->gradient_cache.cache[i].buffer.bo);
+ }
+
+ status = _cairo_pattern_init_copy (&device->gradient_cache.cache[i].pattern.base,
+ &pattern->base);
+ if (unlikely (status)) {
+ intel_bo_destroy (device, buffer->bo);
+ /* Ensure the cache is correctly initialised for i965_device_destroy */
+ _cairo_pattern_init_solid (&device->gradient_cache.cache[i].pattern.solid,
+ CAIRO_COLOR_TRANSPARENT);
+ return status;
+ }
+
+ device->gradient_cache.cache[i].buffer = *buffer;
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/drm/cairo-drm-ioctl-private.h b/src/drm/cairo-drm-ioctl-private.h
new file mode 100755
index 000000000..4294de2d5
--- /dev/null
+++ b/src/drm/cairo-drm-ioctl-private.h
@@ -0,0 +1,12 @@
+#ifndef CAIRO_DRM_IOCTL_PRIVATE_H
+#define CAIRO_DRM_IOCTL_PRIVATE_H
+
+#define DRM_IOCTL_BASE 'd'
+#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr)
+#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type)
+#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type)
+#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type)
+
+#define DRM_COMMAND_BASE 0x40
+
+#endif /* CAIRO_DRM_IOCTL_PRIVATE_H */
diff --git a/src/drm/cairo-drm-private.h b/src/drm/cairo-drm-private.h
new file mode 100755
index 000000000..2db7f38d6
--- /dev/null
+++ b/src/drm/cairo-drm-private.h
@@ -0,0 +1,238 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributors(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_DRM_PRIVATE_H
+#define CAIRO_DRM_PRIVATE_H
+
+#include "cairo-drm.h"
+
+#include "cairo-device-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-surface-private.h"
+
+#include <sys/types.h> /* dev_t */
+
+typedef struct _cairo_drm_device cairo_drm_device_t;
+
+typedef cairo_drm_device_t *
+(*cairo_drm_device_create_func_t) (int fd,
+ dev_t dev,
+ int vendor_id,
+ int chip_id);
+
+typedef cairo_int_status_t
+(*cairo_drm_device_flush_func_t) (cairo_drm_device_t *device);
+
+typedef cairo_int_status_t
+(*cairo_drm_device_throttle_func_t) (cairo_drm_device_t *device);
+
+typedef void
+(*cairo_drm_device_destroy_func_t) (void *data);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_create_func_t) (cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_create_for_name_func_t) (cairo_drm_device_t *device,
+ unsigned int name,
+ cairo_format_t format,
+ int width, int height, int stride);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_create_from_cacheable_image_func_t)
+ (cairo_drm_device_t *device, cairo_surface_t *image);
+
+typedef cairo_int_status_t
+(*cairo_drm_surface_flink_func_t) (void *surface);
+
+typedef cairo_status_t
+(*cairo_drm_surface_enable_scan_out_func_t) (void *surface);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_map_to_image_func_t) (void *surface);
+
+typedef struct _cairo_drm_bo_backend {
+ void (*release) (void *device, void *bo);
+} cairo_drm_bo_backend_t;
+
+typedef struct _cairo_drm_device_backend {
+ cairo_drm_device_flush_func_t flush;
+ cairo_drm_device_throttle_func_t throttle;
+ cairo_drm_device_destroy_func_t destroy;
+} cairo_drm_device_backend_t;
+
+typedef struct _cairo_drm_surface_backend {
+ cairo_drm_surface_create_func_t create;
+ cairo_drm_surface_create_for_name_func_t create_for_name;
+ cairo_drm_surface_create_from_cacheable_image_func_t create_from_cacheable_image;
+ cairo_drm_surface_flink_func_t flink;
+ cairo_drm_surface_enable_scan_out_func_t enable_scan_out;
+ cairo_drm_surface_map_to_image_func_t map_to_image;
+} cairo_drm_surface_backend_t;
+
+typedef struct _cairo_drm_bo {
+ cairo_reference_count_t ref_count;
+ uint32_t name;
+ uint32_t handle;
+ uint32_t size;
+} cairo_drm_bo_t;
+
+struct _cairo_drm_device {
+ cairo_device_t base;
+
+ int vendor_id;
+ int chip_id;
+ dev_t id;
+ int fd;
+
+ int max_surface_size;
+
+ cairo_drm_bo_backend_t bo;
+ cairo_drm_surface_backend_t surface;
+ cairo_drm_device_backend_t device;
+
+ cairo_drm_device_t *next, *prev;
+};
+
+typedef struct _cairo_drm_surface {
+ cairo_surface_t base;
+
+ cairo_drm_bo_t *bo;
+
+ cairo_format_t format;
+ int width, height, stride;
+
+ cairo_surface_t *fallback;
+ uint32_t map_count;
+} cairo_drm_surface_t;
+
+static inline cairo_drm_bo_t *
+cairo_drm_bo_reference (cairo_drm_bo_t *bo)
+{
+ _cairo_reference_count_inc (&bo->ref_count);
+ return bo;
+}
+
+static cairo_always_inline void
+cairo_drm_bo_destroy (cairo_device_t *abstract_device,
+ cairo_drm_bo_t *bo)
+{
+ if (_cairo_reference_count_dec_and_test (&bo->ref_count)) {
+ cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+ device->bo.release (device, bo);
+ }
+}
+
+cairo_private cairo_status_t
+_cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev,
+ cairo_drm_bo_t *bo,
+ uint32_t name);
+
+cairo_private cairo_status_t
+_cairo_drm_bo_flink (const cairo_drm_device_t *dev,
+ cairo_drm_bo_t *bo);
+
+cairo_private void
+_cairo_drm_bo_close (const cairo_drm_device_t *dev,
+ cairo_drm_bo_t *bo);
+
+cairo_private void
+_cairo_drm_surface_init (cairo_drm_surface_t *surface,
+ cairo_format_t format,
+ int width, int height);
+
+cairo_private cairo_status_t
+_cairo_drm_surface_finish (cairo_drm_surface_t *surface);
+
+cairo_private void
+_cairo_drm_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options);
+
+cairo_private cairo_bool_t
+_cairo_drm_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle);
+
+cairo_private cairo_int_status_t
+_cairo_drm_surface_flink (void *abstract_surface);
+
+static inline cairo_drm_device_t *
+_cairo_drm_device_create_in_error (cairo_status_t status)
+{
+ return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_device_init (cairo_drm_device_t *device,
+ int fd,
+ dev_t devid,
+ int vendor_id,
+ int chip_id,
+ int max_surface_size);
+
+cairo_private void
+_cairo_drm_device_fini (cairo_drm_device_t *device);
+
+/* h/w specific backends */
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_i915_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+#if CAIRO_HAS_GALLIUM_SURFACE
+cairo_private cairo_drm_device_t *
+_cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+#endif
+
+slim_hidden_proto (cairo_drm_device_default);
+slim_hidden_proto (cairo_drm_device_get);
+slim_hidden_proto (cairo_drm_device_get_for_fd);
+
+slim_hidden_proto (cairo_drm_surface_create_for_name);
+
+cairo_private cairo_bool_t
+_cairo_drm_size_is_valid (cairo_device_t *abstract_device,
+ int width, int height);
+
+#endif /* CAIRO_DRM_PRIVATE_H */
diff --git a/src/drm/cairo-drm-radeon-private.h b/src/drm/cairo-drm-radeon-private.h
new file mode 100755
index 000000000..546126c6f
--- /dev/null
+++ b/src/drm/cairo-drm-radeon-private.h
@@ -0,0 +1,107 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_DRM_RADEON_PRIVATE_H
+#define CAIRO_DRM_RADEON_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+#include "cairo-drm-private.h"
+#include "cairo-freelist-private.h"
+
+#define RADEON_GEM_DOMAIN_CPU 0x1
+#define RADEON_GEM_DOMAIN_GTT 0x2
+#define RADEON_GEM_DOMAIN_VRAM 0x4
+
+typedef struct _radeon_bo {
+ cairo_drm_bo_t base;
+
+ void *virtual;
+
+ cairo_bool_t in_batch;
+ uint32_t read_domains;
+ uint32_t write_domain;
+} radeon_bo_t;
+
+typedef struct _radeon_device {
+ cairo_drm_device_t base;
+ cairo_freepool_t bo_pool;
+
+ uint64_t vram_limit;
+ uint64_t gart_limit;
+} radeon_device_t;
+
+cairo_private cairo_status_t
+radeon_device_init (radeon_device_t *device, int fd);
+
+cairo_private void
+radeon_device_fini (radeon_device_t *device);
+
+cairo_private cairo_bool_t
+radeon_info (int fd,
+ uint64_t *gart_size,
+ uint64_t *vram_size);
+
+cairo_private void
+radeon_bo_write (const radeon_device_t *dev,
+ radeon_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ const void *data);
+
+cairo_private void
+radeon_bo_read (const radeon_device_t *dev,
+ radeon_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ void *data);
+
+cairo_private void
+radeon_bo_wait (const radeon_device_t *dev, radeon_bo_t *bo);
+
+cairo_private void *
+radeon_bo_map (const radeon_device_t *dev, radeon_bo_t *bo);
+
+cairo_private void
+radeon_bo_unmap (radeon_bo_t *bo);
+
+cairo_private cairo_drm_bo_t *
+radeon_bo_create (radeon_device_t *dev,
+ uint32_t size,
+ uint32_t initial_domain);
+
+cairo_private cairo_drm_bo_t *
+radeon_bo_create_for_name (radeon_device_t *dev, uint32_t name);
+
+cairo_private cairo_surface_t *
+radeon_bo_get_image (const radeon_device_t *device,
+ radeon_bo_t *bo,
+ const cairo_drm_surface_t *surface);
+
+#endif /* CAIRO_DRM_RADEON_PRIVATE_H */
diff --git a/src/drm/cairo-drm-radeon-surface.c b/src/drm/cairo-drm-radeon-surface.c
new file mode 100755
index 000000000..6dbddaae4
--- /dev/null
+++ b/src/drm/cairo-drm-radeon-surface.c
@@ -0,0 +1,448 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-radeon-private.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+
+/* Basic stub surface for radeon chipsets */
+
+#define MAX_SIZE 2048
+
+typedef struct _radeon_surface {
+ cairo_drm_surface_t base;
+} radeon_surface_t;
+
+static inline radeon_device_t *
+to_radeon_device (cairo_device_t *device)
+{
+ return (radeon_device_t *) device;
+}
+
+static inline radeon_bo_t *
+to_radeon_bo (cairo_drm_bo_t *bo)
+{
+ return (radeon_bo_t *) bo;
+}
+
+static cairo_surface_t *
+radeon_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ return cairo_image_surface_create (_cairo_format_from_content (content),
+ width, height);
+}
+
+static cairo_status_t
+radeon_surface_finish (void *abstract_surface)
+{
+ radeon_surface_t *surface = abstract_surface;
+
+ return _cairo_drm_surface_finish (&surface->base);
+}
+
+static cairo_status_t
+radeon_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ radeon_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+ cairo_status_t status;
+
+ /* XXX batch flush */
+
+ if (surface->base.fallback != NULL) {
+ image = surface->base.fallback;
+ goto DONE;
+ }
+
+ image = _cairo_surface_has_snapshot (&surface->base.base,
+ &_cairo_image_surface_backend);
+ if (image != NULL)
+ goto DONE;
+
+ if (surface->base.base.backend->flush != NULL) {
+ status = surface->base.base.backend->flush (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ image = radeon_bo_get_image (to_radeon_device (surface->base.base.device),
+ to_radeon_bo (surface->base.bo),
+ &surface->base);
+ status = image->status;
+ if (unlikely (status))
+ return status;
+
+ _cairo_surface_attach_snapshot (&surface->base.base, image, cairo_surface_destroy);
+
+DONE:
+ *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+radeon_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_surface_t *
+radeon_surface_map_to_image (radeon_surface_t *surface)
+{
+ if (surface->base.fallback == NULL) {
+ cairo_surface_t *image;
+ cairo_status_t status;
+ void *ptr;
+
+ if (surface->base.base.backend->flush != NULL) {
+ status = surface->base.base.backend->flush (surface);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+ }
+
+ ptr = radeon_bo_map (to_radeon_device (surface->base.base.device),
+ to_radeon_bo (surface->base.bo));
+ if (unlikely (ptr == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ image = cairo_image_surface_create_for_data (ptr,
+ surface->base.format,
+ surface->base.width,
+ surface->base.height,
+ surface->base.stride);
+ if (unlikely (image->status)) {
+ radeon_bo_unmap (to_radeon_bo (surface->base.bo));
+ return image;
+ }
+
+ surface->base.fallback = image;
+ }
+
+ return surface->base.fallback;
+}
+
+static cairo_status_t
+radeon_surface_flush (void *abstract_surface,
+ unsigned flags)
+{
+ radeon_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->base.fallback == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* kill any outstanding maps */
+ cairo_surface_finish (surface->base.fallback);
+
+ status = cairo_surface_status (surface->base.fallback);
+ cairo_surface_destroy (surface->base.fallback);
+ surface->base.fallback = NULL;
+
+ radeon_bo_unmap (to_radeon_bo (surface->base.bo));
+
+ return status;
+}
+
+static cairo_int_status_t
+radeon_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_paint (radeon_surface_map_to_image (abstract_surface),
+ op, source, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_mask (radeon_surface_map_to_image (abstract_surface),
+ op, source, mask, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_stroke (radeon_surface_map_to_image (abstract_surface),
+ op, source, path, stroke_style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ return _cairo_surface_fill (radeon_surface_map_to_image (abstract_surface),
+ op, source, path, fill_rule,
+ tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ *num_remaining = 0;
+ return _cairo_surface_show_text_glyphs (radeon_surface_map_to_image (abstract_surface),
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font, clip);
+}
+
+static const cairo_surface_backend_t radeon_surface_backend = {
+ CAIRO_SURFACE_TYPE_DRM,
+ _cairo_default_context_create,
+
+ radeon_surface_create_similar,
+ radeon_surface_finish,
+
+ NULL,
+ radeon_surface_acquire_source_image,
+ radeon_surface_release_source_image,
+
+ NULL, NULL, NULL,
+ NULL, /* composite */
+ NULL, /* fill */
+ NULL, /* trapezoids */
+ NULL, /* span */
+ NULL, /* check-span */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_drm_surface_get_extents,
+ NULL, /* old-glyphs */
+ _cairo_drm_surface_get_font_options,
+
+ radeon_surface_flush,
+ NULL, /* mark dirty */
+ NULL, NULL, /* font/glyph fini */
+
+ radeon_surface_paint,
+ radeon_surface_mask,
+ radeon_surface_stroke,
+ radeon_surface_fill,
+ radeon_surface_glyphs,
+};
+
+static void
+radeon_surface_init (radeon_surface_t *surface,
+ cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ _cairo_surface_init (&surface->base.base,
+ &radeon_surface_backend,
+ &device->base,
+ _cairo_content_from_format (format));
+ _cairo_drm_surface_init (&surface->base, format, width, height);
+}
+
+static cairo_surface_t *
+radeon_surface_create_internal (cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ radeon_surface_t *surface;
+ cairo_status_t status;
+
+ surface = malloc (sizeof (radeon_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ radeon_surface_init (surface, device, format, width, height);
+
+ if (width && height) {
+ surface->base.stride =
+ cairo_format_stride_for_width (surface->base.format, width);
+
+ surface->base.bo = radeon_bo_create (to_radeon_device (&device->base),
+ surface->base.stride * height,
+ RADEON_GEM_DOMAIN_GTT);
+
+ if (unlikely (surface->base.bo == NULL)) {
+ status = _cairo_drm_surface_finish (&surface->base);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ return &surface->base.base;
+}
+
+static cairo_surface_t *
+radeon_surface_create (cairo_drm_device_t *device,
+ cairo_format_t format,
+ int width, int height)
+{
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_RGB16_565:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_A8:
+ break;
+ }
+
+ return radeon_surface_create_internal (device, format, width, height);
+}
+
+static cairo_surface_t *
+radeon_surface_create_for_name (cairo_drm_device_t *device,
+ unsigned int name,
+ cairo_format_t format,
+ int width, int height, int stride)
+{
+ radeon_surface_t *surface;
+ cairo_status_t status;
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_RGB16_565:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_A8:
+ break;
+ }
+
+ if (stride < cairo_format_stride_for_width (format, width))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+ surface = malloc (sizeof (radeon_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ radeon_surface_init (surface, device, format, width, height);
+
+ if (width && height) {
+ surface->base.stride = stride;
+
+ surface->base.bo = radeon_bo_create_for_name (to_radeon_device (&device->base),
+ name);
+
+ if (unlikely (surface->base.bo == NULL)) {
+ status = _cairo_drm_surface_finish (&surface->base);
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ return &surface->base.base;
+}
+
+static void
+radeon_device_destroy (void *data)
+{
+ radeon_device_t *device = data;
+
+ radeon_device_fini (device);
+
+ free (data);
+}
+
+cairo_drm_device_t *
+_cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+ radeon_device_t *device;
+ uint64_t gart_size, vram_size;
+ cairo_status_t status;
+
+ if (! radeon_info (fd, &gart_size, &vram_size))
+ return NULL;
+
+ device = malloc (sizeof (radeon_device_t));
+ if (device == NULL)
+ return _cairo_drm_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = radeon_device_init (device, fd);
+ if (unlikely (status)) {
+ free (device);
+ return _cairo_drm_device_create_in_error (status);
+ }
+
+ device->base.surface.create = radeon_surface_create;
+ device->base.surface.create_for_name = radeon_surface_create_for_name;
+ device->base.surface.create_from_cacheable_image = NULL;
+ device->base.surface.flink = _cairo_drm_surface_flink;
+ device->base.surface.enable_scan_out = NULL;
+
+ device->base.device.flush = NULL;
+ device->base.device.throttle = NULL;
+ device->base.device.destroy = radeon_device_destroy;
+
+ device->vram_limit = vram_size;
+ device->gart_limit = gart_size;
+
+ return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE);
+}
diff --git a/src/drm/cairo-drm-radeon.c b/src/drm/cairo-drm-radeon.c
new file mode 100755
index 000000000..a6d22089b
--- /dev/null
+++ b/src/drm/cairo-drm-radeon.c
@@ -0,0 +1,451 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-radeon-private.h"
+#include "cairo-drm-ioctl-private.h"
+
+#include "cairo-error-private.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#define DRM_RADEON_GEM_INFO 0x1c
+#define DRM_RADEON_GEM_CREATE 0x1d
+#define DRM_RADEON_GEM_MMAP 0x1e
+#define DRM_RADEON_GEM_PREAD 0x21
+#define DRM_RADEON_GEM_PWRITE 0x22
+#define DRM_RADEON_GEM_SET_DOMAIN 0x23
+#define DRM_RADEON_GEM_WAIT_IDLE 0x24
+#define DRM_RADEON_CS 0x26
+#define DRM_RADEON_INFO 0x27
+
+#define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info)
+#define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create)
+#define DRM_IOCTL_RADEON_GEM_WAIT_IDLE DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle)
+#define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap)
+#define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread)
+#define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite)
+#define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain)
+//#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs)
+
+struct drm_radeon_gem_info {
+ uint64_t gart_size;
+ uint64_t vram_size;
+ uint64_t vram_visible;
+};
+
+#define RADEON_GEM_NO_BACKING_STORE 1
+
+struct drm_radeon_gem_create {
+ uint64_t size;
+ uint64_t alignment;
+ uint32_t handle;
+ uint32_t initial_domain;
+ uint32_t flags;
+};
+
+struct drm_radeon_gem_mmap {
+ uint32_t handle;
+ uint32_t pad;
+ uint64_t offset;
+ uint64_t size;
+ uint64_t addr_ptr;
+};
+
+struct drm_radeon_gem_set_domain {
+ uint32_t handle;
+ uint32_t read_domains;
+ uint32_t write_domain;
+};
+
+struct drm_radeon_gem_wait_idle {
+ uint32_t handle;
+ uint32_t pad;
+};
+
+struct drm_radeon_gem_busy {
+ uint32_t handle;
+ uint32_t busy;
+};
+
+struct drm_radeon_gem_pread {
+ /** Handle for the object being read. */
+ uint32_t handle;
+ uint32_t pad;
+ /** Offset into the object to read from */
+ uint64_t offset;
+ /** Length of data to read */
+ uint64_t size;
+ /** Pointer to write the data into. */
+ /* void *, but pointers are not 32/64 compatible */
+ uint64_t data_ptr;
+};
+
+struct drm_radeon_gem_pwrite {
+ /** Handle for the object being written to. */
+ uint32_t handle;
+ uint32_t pad;
+ /** Offset into the object to write to */
+ uint64_t offset;
+ /** Length of data to write */
+ uint64_t size;
+ /** Pointer to read the data from. */
+ /* void *, but pointers are not 32/64 compatible */
+ uint64_t data_ptr;
+};
+
+#define RADEON_CHUNK_ID_RELOCS 0x01
+#define RADEON_CHUNK_ID_IB 0x02
+
+struct drm_radeon_cs_chunk {
+ uint32_t chunk_id;
+ uint32_t length_dw;
+ uint64_t chunk_data;
+};
+
+struct drm_radeon_cs_reloc {
+ uint32_t handle;
+ uint32_t read_domains;
+ uint32_t write_domain;
+ uint32_t flags;
+};
+
+struct drm_radeon_cs {
+ uint32_t num_chunks;
+ uint32_t cs_id;
+ /* this points to uint64_t * which point to cs chunks */
+ uint64_t chunks;
+ /* updates to the limits after this CS ioctl */
+ uint64_t gart_limit;
+ uint64_t vram_limit;
+};
+
+#define RADEON_INFO_DEVICE_ID 0x00
+#define RADEON_INFO_NUM_GB_PIPES 0x01
+
+struct drm_radeon_info {
+ uint32_t request;
+ uint32_t pad;
+ uint64_t value;
+};
+
+
+cairo_bool_t
+radeon_info (int fd,
+ uint64_t *gart_size,
+ uint64_t *vram_size)
+{
+ struct drm_radeon_gem_info info;
+ int ret;
+
+ ret = ioctl (fd, DRM_IOCTL_RADEON_GEM_INFO, &info);
+ if (ret == -1)
+ return FALSE;
+
+ if (gart_size != NULL)
+ *gart_size = info.gart_size;
+
+ if (vram_size != NULL)
+ *vram_size = info.vram_size;
+
+ return TRUE;
+}
+
+void
+radeon_bo_write (const radeon_device_t *device,
+ radeon_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ const void *data)
+{
+ struct drm_radeon_gem_pwrite pwrite;
+ int ret;
+
+ memset (&pwrite, 0, sizeof (pwrite));
+ pwrite.handle = bo->base.handle;
+ pwrite.offset = offset;
+ pwrite.size = size;
+ pwrite.data_ptr = (uint64_t) (uintptr_t) data;
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PWRITE, &pwrite);
+ } while (ret == -1 && errno == EINTR);
+
+ /* XXX temporary workaround */
+ if (ret == -1 && errno == ENOSYS) {
+ uint8_t *ptr;
+
+ ptr = radeon_bo_map (device, bo);
+ if (ptr != NULL) {
+ memcpy (ptr + offset, data, size);
+ radeon_bo_unmap (bo);
+ }
+ }
+}
+
+void
+radeon_bo_read (const radeon_device_t *device,
+ radeon_bo_t *bo,
+ unsigned long offset,
+ unsigned long size,
+ void *data)
+{
+ struct drm_radeon_gem_pread pread;
+ int ret;
+
+ memset (&pread, 0, sizeof (pread));
+ pread.handle = bo->base.handle;
+ pread.offset = offset;
+ pread.size = size;
+ pread.data_ptr = (uint64_t) (uintptr_t) data;
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PREAD, &pread);
+ } while (ret == -1 && errno == EINTR);
+
+ /* XXX temporary workaround */
+ if (ret == -1 && errno == ENOSYS) {
+ uint8_t *ptr;
+
+ ptr = radeon_bo_map (device, bo);
+ if (ptr != NULL) {
+ memcpy (data, ptr + offset, size);
+ radeon_bo_unmap (bo);
+ }
+ }
+
+ VG (VALGRIND_MAKE_MEM_DEFINED (data, size));
+}
+
+void
+radeon_bo_wait (const radeon_device_t *device, radeon_bo_t *bo)
+{
+ struct drm_radeon_gem_wait_idle wait;
+ int ret;
+
+ wait.handle = bo->base.handle;
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_WAIT_IDLE, &wait);
+ } while (ret == -1 && (errno == EINTR || errno == EBUSY));
+}
+
+void *
+radeon_bo_map (const radeon_device_t *device, radeon_bo_t *bo)
+{
+ struct drm_radeon_gem_mmap mmap_arg;
+ void *ptr;
+ int ret;
+
+ assert (bo->virtual == NULL);
+
+ memset (&mmap_arg, 0, sizeof (mmap_arg));
+ mmap_arg.handle = bo->base.handle;
+ mmap_arg.offset = 0;
+ mmap_arg.size = bo->base.size;
+
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_MMAP, &mmap_arg);
+ } while (ret == -1 && errno == EINTR);
+ if (unlikely (ret != 0)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ VG (VALGRIND_MAKE_MEM_DEFINED (&mmap_arg, sizeof (mmap_arg)));
+
+ /* and mmap it */
+ ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, device->base.fd,
+ mmap_arg.addr_ptr);
+ if (unlikely (ptr == MAP_FAILED)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ bo->virtual = ptr;
+
+ /* XXX set_domain? */
+ return bo->virtual;
+}
+
+void
+radeon_bo_unmap (radeon_bo_t *bo)
+{
+ assert (bo->virtual != NULL);
+
+ munmap (bo->virtual, bo->base.size);
+ bo->virtual = NULL;
+}
+
+cairo_drm_bo_t *
+radeon_bo_create (radeon_device_t *device,
+ uint32_t size,
+ uint32_t initial_domain)
+{
+ struct drm_radeon_gem_create create;
+ radeon_bo_t *bo;
+ int ret;
+
+ bo = _cairo_freepool_alloc (&device->bo_pool);
+ if (unlikely (bo == NULL))
+ return NULL;
+
+ create.size = size;
+ create.alignment = 0;
+ create.initial_domain = initial_domain;
+ create.flags = 0;
+ create.handle = 0;
+
+ do {
+ ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_CREATE, &create);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1) {
+ _cairo_freepool_free (&device->bo_pool, bo);
+ return NULL;
+ }
+
+ bo->base.handle = create.handle;
+ bo->base.size = size;
+
+ bo->virtual = NULL;
+
+ bo->in_batch = FALSE;
+ bo->read_domains = 0;
+ bo->write_domain = 0;
+
+ CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+ return &bo->base;
+}
+
+cairo_drm_bo_t *
+radeon_bo_create_for_name (radeon_device_t *device,
+ uint32_t name)
+{
+ radeon_bo_t *bo;
+ cairo_status_t status;
+
+ bo = _cairo_freepool_alloc (&device->bo_pool);
+ if (unlikely (bo == NULL))
+ return NULL;
+
+ status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name);
+ if (unlikely (status)) {
+ _cairo_freepool_free (&device->bo_pool, bo);
+ return NULL;
+ }
+
+ bo->virtual = NULL;
+
+ bo->in_batch = FALSE;
+ bo->read_domains = 0;
+ bo->write_domain = 0;
+
+ CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+ return &bo->base;
+}
+
+static void
+radeon_bo_release (void *_dev, void *_bo)
+{
+ radeon_device_t *device = _dev;
+ radeon_bo_t *bo = _bo;
+
+ _cairo_drm_bo_close (&device->base, &bo->base);
+ _cairo_freepool_free (&device->bo_pool, bo);
+}
+
+cairo_surface_t *
+radeon_bo_get_image (const radeon_device_t *device,
+ radeon_bo_t *bo,
+ const cairo_drm_surface_t *surface)
+{
+ cairo_image_surface_t *image;
+ uint8_t *dst;
+ int size, row;
+
+ image = (cairo_image_surface_t *)
+ cairo_image_surface_create (surface->format,
+ surface->width,
+ surface->height);
+ if (unlikely (image->base.status))
+ return &image->base;
+
+ if (image->stride == surface->stride) {
+ size = surface->stride * surface->height;
+ radeon_bo_read (device, bo, 0, size, image->data);
+ } else {
+ int offset;
+
+ size = surface->width;
+ if (surface->format != CAIRO_FORMAT_A8)
+ size *= 4;
+
+ offset = 0;
+ row = surface->height;
+ dst = image->data;
+ while (row--) {
+ radeon_bo_read (device, bo, offset, size, dst);
+ offset += surface->stride;
+ dst += image->stride;
+ }
+ }
+
+ return &image->base;
+}
+
+static void
+_radeon_device_init_bo_cache (radeon_device_t *device)
+{
+ _cairo_freepool_init (&device->bo_pool, sizeof (radeon_bo_t));
+}
+
+cairo_status_t
+radeon_device_init (radeon_device_t *device, int fd)
+{
+ _radeon_device_init_bo_cache (device);
+
+ device->base.bo.release = radeon_bo_release;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_radeon_bo_cache_fini (radeon_device_t *device)
+{
+ _cairo_freepool_fini (&device->bo_pool);
+}
+
+void
+radeon_device_fini (radeon_device_t *device)
+{
+ _radeon_bo_cache_fini (device);
+ _cairo_drm_device_fini (&device->base);
+}
diff --git a/src/drm/cairo-drm-surface.c b/src/drm/cairo-drm-surface.c
new file mode 100755
index 000000000..8c4dd0ee8
--- /dev/null
+++ b/src/drm/cairo-drm-surface.c
@@ -0,0 +1,369 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+
+void
+_cairo_drm_surface_init (cairo_drm_surface_t *surface,
+ cairo_format_t format,
+ int width, int height)
+{
+ surface->bo = NULL;
+ surface->format = format;
+ surface->width = width;
+ surface->height = height;
+ surface->stride = 0;
+
+ surface->fallback = NULL;
+ surface->map_count = 0;
+}
+
+cairo_status_t
+_cairo_drm_surface_finish (cairo_drm_surface_t *surface)
+{
+ assert (surface->fallback == NULL);
+
+ if (surface->bo != NULL)
+ cairo_drm_bo_destroy (surface->base.device, surface->bo);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_drm_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+}
+
+cairo_bool_t
+_cairo_drm_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_drm_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+cairo_surface_t *
+cairo_drm_surface_create (cairo_device_t *abstract_device,
+ cairo_format_t format,
+ int width, int height)
+{
+ cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+ cairo_surface_t *surface;
+
+ if (device != NULL && device->base.status)
+ {
+ surface = _cairo_surface_create_in_error (device->base.status);
+ }
+ else if (device == NULL ||
+ device->surface.create == NULL ||
+ width == 0 || width > device->max_surface_size ||
+ height == 0 || height > device->max_surface_size)
+ {
+ surface = cairo_image_surface_create (format, width, height);
+ }
+ else if (device->base.finished)
+ {
+ surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+ }
+ else
+ {
+ surface = device->surface.create (device, format, width, height);
+ if (surface->status == CAIRO_STATUS_INVALID_SIZE)
+ surface = cairo_image_surface_create (format, width, height);
+ }
+
+ return surface;
+}
+
+cairo_surface_t *
+cairo_drm_surface_create_for_name (cairo_device_t *abstract_device,
+ unsigned int name,
+ cairo_format_t format,
+ int width, int height, int stride)
+{
+ cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+ cairo_surface_t *surface;
+
+ if (! CAIRO_FORMAT_VALID (format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ if (device != NULL && device->base.status)
+ {
+ surface = _cairo_surface_create_in_error (device->base.status);
+ }
+ else if (device == NULL || device->surface.create_for_name == NULL)
+ {
+ /* XXX invalid device! */
+ surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+ else if (width == 0 || width > device->max_surface_size ||
+ height == 0 || height > device->max_surface_size)
+ {
+ surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ }
+ else if (device->base.finished)
+ {
+ surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+ }
+ else
+ {
+ surface = device->surface.create_for_name (device,
+ name, format,
+ width, height, stride);
+ }
+
+ return surface;
+}
+slim_hidden_def (cairo_drm_surface_create_for_name);
+
+cairo_surface_t *
+cairo_drm_surface_create_from_cacheable_image (cairo_device_t *abstract_device,
+ cairo_surface_t *surface)
+{
+ cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+
+ if (surface->status) {
+ surface = _cairo_surface_create_in_error (surface->status);
+ } else if (device != NULL && device->base.status) {
+ surface = _cairo_surface_create_in_error (device->base.status);
+ } else if (device == NULL || device->surface.create_from_cacheable_image == NULL) {
+ /* XXX invalid device! */
+ surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ } else if (device->base.finished) {
+ surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+ } else {
+ surface = device->surface.create_from_cacheable_image (device, surface);
+ }
+
+ return surface;
+}
+
+static cairo_drm_surface_t *
+_cairo_surface_as_drm (cairo_surface_t *abstract_surface)
+{
+ if (unlikely (abstract_surface->status))
+ return NULL;
+
+ if (abstract_surface->type != CAIRO_SURFACE_TYPE_DRM)
+ return NULL;
+
+ return (cairo_drm_surface_t *) abstract_surface;
+}
+
+cairo_status_t
+cairo_drm_surface_enable_scan_out (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+ cairo_drm_device_t *device;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (unlikely (surface == NULL))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ if (unlikely (surface->base.finished))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ device = (cairo_drm_device_t *) surface->base.device;
+ if (device->surface.enable_scan_out == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (unlikely (device->base.finished))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ return device->surface.enable_scan_out (abstract_surface);
+}
+
+unsigned int
+cairo_drm_surface_get_handle (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ return surface->bo->handle;
+}
+
+cairo_int_status_t
+_cairo_drm_surface_flink (void *abstract_surface)
+{
+ cairo_drm_surface_t *surface = abstract_surface;
+
+ return _cairo_drm_bo_flink ((cairo_drm_device_t *) surface->base.device,
+ surface->bo);
+}
+
+unsigned int
+cairo_drm_surface_get_name (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+ cairo_drm_device_t *device;
+ cairo_status_t status;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ if (surface->bo->name)
+ return surface->bo->name;
+
+ device = (cairo_drm_device_t *) surface->base.device;
+ if (device->surface.flink == NULL)
+ return 0;
+
+ status = device->surface.flink (abstract_surface);
+ if (status) {
+ if (_cairo_status_is_error (status))
+ status = _cairo_surface_set_error (abstract_surface, status);
+
+ return 0;
+ }
+
+ return surface->bo->name;
+}
+
+cairo_format_t
+cairo_drm_surface_get_format (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL)
+ return cairo_image_surface_get_format (abstract_surface);
+
+ return surface->format;
+}
+
+int
+cairo_drm_surface_get_width (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL)
+ return cairo_image_surface_get_width (abstract_surface);
+
+ return surface->width;
+}
+
+int
+cairo_drm_surface_get_height (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL)
+ return cairo_image_surface_get_height (abstract_surface);
+
+ return surface->height;
+}
+
+int
+cairo_drm_surface_get_stride (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL)
+ return cairo_image_surface_get_stride (abstract_surface);
+
+ return surface->stride;
+}
+
+/* XXX drm or general surface layer? naming? */
+cairo_surface_t *
+cairo_drm_surface_map_to_image (cairo_surface_t *abstract_surface)
+{
+ cairo_drm_surface_t *surface;
+ cairo_drm_device_t *device;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return _cairo_surface_create_in_error (abstract_surface->status);
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL) {
+ if (_cairo_surface_is_image (abstract_surface))
+ return cairo_surface_reference (abstract_surface);
+
+ status = _cairo_surface_set_error (abstract_surface,
+ CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ surface->map_count++;
+ device = (cairo_drm_device_t *) surface->base.device;
+ return cairo_surface_reference (device->surface.map_to_image (surface));
+}
+
+void
+cairo_drm_surface_unmap (cairo_surface_t *abstract_surface,
+ cairo_surface_t *image)
+{
+ cairo_drm_surface_t *surface;
+
+ surface = _cairo_surface_as_drm (abstract_surface);
+ if (surface == NULL) {
+ if (_cairo_surface_is_image (abstract_surface))
+ cairo_surface_destroy (image);
+ else
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return;
+ }
+
+ /* XXX assert image belongs to drm */
+ //assert (image == drm->fallback);
+ cairo_surface_destroy (image);
+
+ assert (surface->map_count > 0);
+ if (--surface->map_count == 0)
+ cairo_surface_flush (&surface->base);
+}
diff --git a/src/drm/cairo-drm.c b/src/drm/cairo-drm.c
new file mode 100755
index 000000000..051b79e4f
--- /dev/null
+++ b/src/drm/cairo-drm.c
@@ -0,0 +1,387 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+#include <libudev.h>
+#include <fcntl.h>
+#include <unistd.h> /* open(), close() */
+
+static cairo_drm_device_t *_cairo_drm_known_devices;
+static cairo_drm_device_t *_cairo_drm_default_device;
+
+static const char *
+get_udev_property(struct udev_device *device, const char *name)
+{
+ struct udev_list_entry *entry;
+
+ udev_list_entry_foreach (entry,
+ udev_device_get_properties_list_entry (device))
+ {
+ if (strcmp (udev_list_entry_get_name (entry), name) == 0)
+ return udev_list_entry_get_value (entry);
+ }
+
+ return NULL;
+}
+
+static void
+_device_flush (void *abstract_device)
+{
+ cairo_drm_device_t *device = abstract_device;
+
+ device->device.flush (device);
+}
+
+static void
+_device_finish (void *abstract_device)
+{
+ cairo_drm_device_t *device = abstract_device;
+
+ CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex);
+ if (device->prev != NULL)
+ device->prev->next = device->next;
+ else
+ _cairo_drm_known_devices = device->next;
+ if (device->next != NULL)
+ device->next->prev = device->prev;
+
+ CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex);
+
+ if (_cairo_atomic_ptr_cmpxchg (&_cairo_drm_default_device,
+ device, NULL))
+ {
+ cairo_device_destroy (&device->base);
+ }
+}
+
+static void
+_device_destroy (void *abstract_device)
+{
+ cairo_drm_device_t *device = abstract_device;
+
+ device->device.destroy (device);
+}
+
+static const cairo_device_backend_t _cairo_drm_device_backend = {
+ CAIRO_DEVICE_TYPE_DRM,
+
+ NULL, NULL, /* lock, unlock */
+
+ _device_flush,
+ _device_finish,
+ _device_destroy,
+};
+
+cairo_drm_device_t *
+_cairo_drm_device_init (cairo_drm_device_t *dev,
+ int fd,
+ dev_t devid,
+ int vendor_id,
+ int chip_id,
+ int max_surface_size)
+{
+ assert (CAIRO_MUTEX_IS_LOCKED (_cairo_drm_device_mutex));
+
+ _cairo_device_init (&dev->base, &_cairo_drm_device_backend);
+
+ dev->id = devid;
+ dev->vendor_id = vendor_id;
+ dev->chip_id = chip_id;
+ dev->fd = fd;
+
+ dev->max_surface_size = max_surface_size;
+
+ dev->prev = NULL;
+ dev->next = _cairo_drm_known_devices;
+ if (_cairo_drm_known_devices != NULL)
+ _cairo_drm_known_devices->prev = dev;
+ _cairo_drm_known_devices = dev;
+
+ if (_cairo_drm_default_device == NULL)
+ _cairo_drm_default_device = (cairo_drm_device_t *) cairo_device_reference (&dev->base);
+
+ return dev;
+}
+
+cairo_device_t *
+cairo_drm_device_get (struct udev_device *device)
+{
+ static const struct dri_driver_entry {
+ uint32_t vendor_id;
+ uint32_t chip_id;
+ cairo_drm_device_create_func_t create_func;
+ } driver_map[] = {
+ { 0x8086, 0x29a2, _cairo_drm_i965_device_create }, /* I965_G */
+ { 0x8086, 0x2982, _cairo_drm_i965_device_create }, /* G35_G */
+ { 0x8086, 0x2992, _cairo_drm_i965_device_create }, /* I965_Q */
+ { 0x8086, 0x2972, _cairo_drm_i965_device_create }, /* I946_GZ */
+ { 0x8086, 0x2a02, _cairo_drm_i965_device_create }, /* I965_GM */
+ { 0x8086, 0x2a12, _cairo_drm_i965_device_create }, /* I965_GME */
+ { 0x8086, 0x2e02, _cairo_drm_i965_device_create }, /* IGD_E_G */
+ { 0x8086, 0x2e22, _cairo_drm_i965_device_create }, /* G45_G */
+ { 0x8086, 0x2e12, _cairo_drm_i965_device_create }, /* Q45_G */
+ { 0x8086, 0x2e32, _cairo_drm_i965_device_create }, /* G41_G */
+ { 0x8086, 0x2a42, _cairo_drm_i965_device_create }, /* GM45_GM */
+
+ { 0x8086, 0x2582, _cairo_drm_i915_device_create }, /* I915_G */
+ { 0x8086, 0x2592, _cairo_drm_i915_device_create }, /* I915_GM */
+ { 0x8086, 0x258a, _cairo_drm_i915_device_create }, /* E7221_G */
+ { 0x8086, 0x2772, _cairo_drm_i915_device_create }, /* I945_G */
+ { 0x8086, 0x27a2, _cairo_drm_i915_device_create }, /* I945_GM */
+ { 0x8086, 0x27ae, _cairo_drm_i915_device_create }, /* I945_GME */
+ { 0x8086, 0x29c2, _cairo_drm_i915_device_create }, /* G33_G */
+ { 0x8086, 0x29b2, _cairo_drm_i915_device_create }, /* Q35_G */
+ { 0x8086, 0x29d2, _cairo_drm_i915_device_create }, /* Q33_G */
+ { 0x8086, 0xa011, _cairo_drm_i915_device_create }, /* IGD_GM */
+ { 0x8086, 0xa001, _cairo_drm_i915_device_create }, /* IGD_G */
+
+ /* XXX i830 */
+
+ { 0x8086, ~0, _cairo_drm_intel_device_create },
+
+ { 0x1002, ~0, _cairo_drm_radeon_device_create },
+#if CAIRO_HAS_GALLIUM_SURFACE
+ { ~0, ~0, _cairo_drm_gallium_device_create },
+#endif
+ };
+
+ cairo_drm_device_t *dev;
+ dev_t devid;
+ struct udev_device *parent;
+ const char *pci_id;
+ uint32_t vendor_id, chip_id;
+ const char *path;
+ int i, fd;
+
+ devid = udev_device_get_devnum (device);
+
+ CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex);
+ for (dev = _cairo_drm_known_devices; dev != NULL; dev = dev->next) {
+ if (dev->id == devid) {
+ dev = (cairo_drm_device_t *) cairo_device_reference (&dev->base);
+ goto DONE;
+ }
+ }
+
+ parent = udev_device_get_parent (device);
+ pci_id = get_udev_property (parent, "PCI_ID");
+ if (pci_id == NULL || sscanf (pci_id, "%x:%x", &vendor_id, &chip_id) != 2) {
+ dev = (cairo_drm_device_t *)
+ _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+ goto DONE;
+ }
+
+#if CAIRO_HAS_GALLIUM_SURFACE
+ if (getenv ("CAIRO_GALLIUM_FORCE"))
+ {
+ i = ARRAY_LENGTH (driver_map) - 1;
+ }
+ else
+#endif
+ {
+ for (i = 0; i < ARRAY_LENGTH (driver_map); i++) {
+ if (driver_map[i].vendor_id == ~0U)
+ break;
+
+ if (driver_map[i].vendor_id == vendor_id &&
+ (driver_map[i].chip_id == ~0U || driver_map[i].chip_id == chip_id))
+ break;
+ }
+
+ if (i == ARRAY_LENGTH (driver_map)) {
+ dev = (cairo_drm_device_t *)
+ _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+ goto DONE;
+ }
+ }
+
+ path = udev_device_get_devnode (device);
+ if (path == NULL)
+ path = "/dev/dri/card0"; /* XXX buggy udev? */
+
+ fd = open (path, O_RDWR);
+ if (fd == -1) {
+ /* XXX more likely to be a permissions issue... */
+ _cairo_error_throw (CAIRO_STATUS_FILE_NOT_FOUND);
+ goto DONE;
+ }
+
+ dev = driver_map[i].create_func (fd, devid, vendor_id, chip_id);
+ if (dev == NULL)
+ close (fd);
+
+ DONE:
+ CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex);
+
+ return &dev->base;
+}
+slim_hidden_def (cairo_drm_device_get);
+
+cairo_device_t *
+cairo_drm_device_get_for_fd (int fd)
+{
+ struct stat st;
+ struct udev *udev;
+ struct udev_device *device;
+ cairo_device_t *dev = NULL;
+
+ if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) {
+ //_cairo_error_throw (CAIRO_STATUS_INVALID_DEVICE);
+ return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ udev = udev_new ();
+
+ device = udev_device_new_from_devnum (udev, 'c', st.st_rdev);
+ if (device != NULL) {
+ dev = cairo_drm_device_get (device);
+ udev_device_unref (device);
+ }
+
+ udev_unref (udev);
+
+ return dev;
+}
+slim_hidden_def (cairo_drm_device_get_for_fd);
+
+cairo_device_t *
+cairo_drm_device_default (void)
+{
+ struct udev *udev;
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ cairo_device_t *dev;
+
+ /* optimistic atomic pointer read */
+ dev = &_cairo_drm_default_device->base;
+ if (dev != NULL)
+ return dev;
+
+ udev = udev_new();
+ if (udev == NULL)
+ return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ e = udev_enumerate_new (udev);
+ udev_enumerate_add_match_subsystem (e, "drm");
+ udev_enumerate_scan_devices (e);
+ udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (e)) {
+ struct udev_device *device;
+
+ device =
+ udev_device_new_from_syspath (udev,
+ udev_list_entry_get_name (entry));
+
+ dev = cairo_drm_device_get (device);
+
+ udev_device_unref (device);
+
+ if (dev != NULL) {
+ if (((cairo_drm_device_t *) dev)->fd == -1) {
+ /* try again, we may find a usable card */
+ cairo_device_destroy (dev);
+ dev = NULL;
+ } else
+ break;
+ }
+ }
+ udev_enumerate_unref (e);
+ udev_unref (udev);
+
+ cairo_device_destroy (dev); /* owned by _cairo_drm_default_device */
+ return dev;
+}
+slim_hidden_def (cairo_drm_device_default);
+
+void
+_cairo_drm_device_reset_static_data (void)
+{
+ if (_cairo_drm_default_device != NULL) {
+ cairo_device_t *device = &_cairo_drm_default_device->base;
+ _cairo_drm_default_device = NULL;
+ cairo_device_destroy (device);
+ }
+}
+
+int
+cairo_drm_device_get_fd (cairo_device_t *abstract_device)
+{
+ cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+
+ if (device->base.status)
+ return -1;
+
+ return device->fd;
+}
+
+void
+_cairo_drm_device_fini (cairo_drm_device_t *device)
+{
+ if (device->fd != -1)
+ close (device->fd);
+}
+
+void
+cairo_drm_device_throttle (cairo_device_t *abstract_device)
+{
+ cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+ cairo_status_t status;
+
+ if (unlikely (device->base.status))
+ return;
+
+ if (device->device.throttle == NULL)
+ return;
+
+ status = device->device.throttle (device);
+ if (unlikely (status))
+ _cairo_status_set_error (&device->base.status, status);
+}
+
+cairo_bool_t
+_cairo_drm_size_is_valid (cairo_device_t *abstract_device,
+ int width, int height)
+{
+ cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+
+ if (unlikely (device->base.status))
+ return FALSE;
+
+ return width <= device->max_surface_size &&
+ height <= device->max_surface_size;
+}
diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp
new file mode 100755
index 000000000..bbe5507f6
--- /dev/null
+++ b/src/skia/cairo-skia-context.cpp
@@ -0,0 +1,1750 @@
+/* -*- 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 © 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., 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>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-private.h"
+#include "cairo-error-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-skia-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <SkShader.h>
+#include <SkColorShader.h>
+#include <SkGradientShader.h>
+#include <SkDashPathEffect.h>
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED)
+# define CAIRO_FIXED_TO_SK_SCALAR(x) (x)
+#elif defined(SK_SCALAR_IS_FIXED)
+/* This can be done better, but this will do for now */
+# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x))
+#else
+# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x))
+#endif
+
+#define UNSUPPORTED
+
+
+static freed_pool_t context_pool;
+
+static void
+_cairo_skia_context_destroy (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->reset ();
+ cr->paint->reset ();
+
+ delete cr->canvas;
+
+ cairo_surface_destroy (&cr->target->image.base);
+ cairo_surface_destroy (&cr->original->image.base);
+
+ if (cr->source != NULL) {
+ if (cr->source_image != NULL) {
+ _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+ cr->source_image = NULL;
+ }
+ cairo_surface_destroy (cr->source);
+ cr->source = NULL;
+ }
+
+ _cairo_fini (&cr->base);
+
+ _freed_pool_put (&context_pool, cr);
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_original_target (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return &cr->original->image.base;
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_current_target (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return &cr->target->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_context_save (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->canvas->save ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_restore (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->canvas->restore ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_surface_t *group_surface;
+ cairo_status_t status;
+ int width, height;
+
+ //clip = _cairo_gstate_get_clip (cr->gstate);
+ width = cr->target->image.width;
+ height = cr->target->image.height;
+ group_surface = cr->target->image.base.backend->create_similar (&cr->target->image.base,
+ content, width, height);
+
+#if 0
+ /* 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_path_fixed_transform (cr->path,
+ &group_surface->device_transform);
+#endif
+
+ status = _cairo_skia_context_save (cr);
+ if (unlikely (status)) {
+ cairo_surface_destroy (group_surface);
+ return status;
+ }
+
+ cairo_surface_destroy (&cr->target->image.base);
+ cr->target = (cairo_skia_surface_t *) group_surface;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_pop_group (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_surface_t *group_surface;
+ cairo_pattern_t *group_pattern;
+ cairo_status_t status;
+
+ group_surface = cairo_surface_reference (&cr->target->image.base);
+
+ status = _cairo_skia_context_restore (cr);
+ if (unlikely (status)) {
+ group_pattern = _cairo_pattern_create_in_error (status);
+ goto done;
+ }
+
+ group_pattern = cairo_pattern_create_for_surface (group_surface);
+ status = group_pattern->status;
+ if (unlikely (status))
+ goto done;
+
+#if 0
+ _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_path_fixed_transform (cr->path,
+ &group_surface->device_transform_inverse);
+#endif
+
+done:
+ cairo_surface_destroy (group_surface);
+
+ return group_pattern;
+}
+
+static inline cairo_surface_t *
+surface_from_pattern (const cairo_pattern_t *pattern)
+{
+ return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface;
+}
+
+static inline bool
+surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap)
+{
+ cairo_image_surface_t *img = (cairo_image_surface_t *) surface;
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (unlikely (! format_to_sk_config (img->format, config, opaque)))
+ return false;
+
+ bitmap.reset ();
+ bitmap.setConfig (config, img->width, img->height, img->stride);
+ bitmap.setIsOpaque (opaque);
+ bitmap.setPixels (img->data);
+
+ return true;
+}
+
+static inline SkMatrix
+matrix_to_sk (const cairo_matrix_t& mat)
+{
+ SkMatrix skm;
+
+ skm.reset ();
+ skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx));
+ skm.set (SkMatrix::kMSkewX, SkFloatToScalar (mat.xy));
+ skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0));
+ skm.set (SkMatrix::kMSkewY, SkFloatToScalar (mat.yx));
+ skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy));
+ skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0));
+
+ /*
+ skm[6] = SkFloatToScalar (0.0);
+ skm[7] = SkFloatToScalar (0.0);
+ skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself. It wants Sk_Fract1 (2.30), not Sk_Scalar1
+ */
+
+ return skm;
+}
+
+static inline SkMatrix
+matrix_inverse_to_sk (const cairo_matrix_t& mat)
+{
+ cairo_matrix_t inv = mat;
+ cairo_status_t status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ return matrix_to_sk (inv);
+}
+
+static SkShader::TileMode
+extend_to_sk (cairo_extend_t extend)
+{
+ static const SkShader::TileMode modeMap[] = {
+ SkShader::kClamp_TileMode, // NONE behaves like PAD, because noone wants NONE
+ SkShader::kRepeat_TileMode,
+ SkShader::kMirror_TileMode,
+ SkShader::kClamp_TileMode
+ };
+
+ return modeMap[extend];
+}
+
+static inline SkColor
+color_to_sk (const cairo_color_t& c)
+{
+ /* Need unpremultiplied 1-byte values */
+ return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+ (U8CPU) (c.red * 255),
+ (U8CPU) (c.green * 255),
+ (U8CPU) (c.blue * 255));
+}
+
+static inline SkColor
+color_stop_to_sk (const cairo_color_stop_t& c)
+{
+ /* Need unpremultiplied 1-byte values */
+ return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+ (U8CPU) (c.red * 255),
+ (U8CPU) (c.green * 255),
+ (U8CPU) (c.blue * 255));
+}
+
+static SkShader*
+source_to_sk_shader (cairo_skia_context_t *cr,
+ const cairo_pattern_t *pattern)
+{
+ SkShader *shader = NULL;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+ return new SkColorShader (color_to_sk (solid->color));
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_t *surface = surface_from_pattern (pattern);
+
+ cr->source = cairo_surface_reference (surface);
+
+ if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+
+ shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+ extend_to_sk (pattern->extend),
+ extend_to_sk (pattern->extend));
+ } else {
+ SkBitmap bitmap;
+
+ if (! _cairo_surface_is_image (surface)) {
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (surface,
+ &cr->source_image,
+ &cr->source_extra);
+ if (status)
+ return NULL;
+
+ surface = &cr->source_image->base;
+ }
+
+ if (unlikely (! surface_to_sk_bitmap (surface, bitmap)))
+ return NULL;
+
+ shader = SkShader::CreateBitmapShader (bitmap,
+ extend_to_sk (pattern->extend),
+ extend_to_sk (pattern->extend));
+ }
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR
+ /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */)
+ {
+ cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+ SkColor colors_stack[10];
+ SkScalar pos_stack[10];
+ SkColor *colors = colors_stack;
+ SkScalar *pos = pos_stack;
+
+ if (gradient->n_stops > 10) {
+ colors = new SkColor[gradient->n_stops];
+ pos = new SkScalar[gradient->n_stops];
+ }
+
+ for (unsigned int i = 0; i < gradient->n_stops; i++) {
+ pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
+ colors[i] = color_stop_to_sk (gradient->stops[i].color);
+ }
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ SkPoint points[2];
+
+ points[0].set (SkFloatToScalar (linear->pd1.x),
+ SkFloatToScalar (linear->pd1.y));
+ points[1].set (SkFloatToScalar (linear->pd2.x),
+ SkFloatToScalar (linear->pd2.y));
+ shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
+ extend_to_sk (pattern->extend));
+ } else {
+ // XXX todo -- implement real radial shaders in Skia
+ }
+
+ if (gradient->n_stops > 10) {
+ delete [] colors;
+ delete [] pos;
+ }
+ }
+
+ if (shader && ! _cairo_matrix_is_identity (&pattern->matrix))
+ shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix));
+
+ return shader;
+}
+
+static inline bool
+pattern_filter_to_sk (const cairo_pattern_t *pattern)
+{
+ switch (pattern->filter) {
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return true;
+ default:
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ return false;
+ }
+}
+
+static inline bool
+pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color)
+{
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return false;
+
+ color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color);
+ return true;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source (void *abstract_cr,
+ cairo_pattern_t *source)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkColor color;
+
+ if (cr->source != NULL) {
+ if (cr->source_image != NULL) {
+ _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+ cr->source_image = NULL;
+ }
+ cairo_surface_destroy (cr->source);
+ cr->source = NULL;
+ }
+
+ if (pattern_to_sk_color (source, color)) {
+ cr->paint->setColor (color);
+ } else {
+ SkShader *shader = source_to_sk_shader (cr, source);
+ if (shader == NULL) {
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ cr->paint->setShader (shader);
+ shader->unref ();
+
+ cr->paint->setFilterBitmap (pattern_filter_to_sk (source));
+ }
+
+ /* XXX change notification */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* Need unpremultiplied 1-byte values */
+ cr->paint->setARGB ((U8CPU) (alpha * 255),
+ (U8CPU) (red * 255),
+ (U8CPU) (green * 255),
+ (U8CPU) (blue * 255));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_surface (void *abstract_cr,
+ cairo_surface_t *surface,
+ double x,
+ double y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ cairo_status_t status;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ SkShader *shader;
+
+ shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+ SkShader::kClamp_TileMode, /* XXX */
+ SkShader::kClamp_TileMode);
+
+ cr->paint->setShader (shader);
+ shader->unref ();
+
+ cr->paint->setFilterBitmap (true);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ pattern = cairo_pattern_create_for_surface (surface);
+ if (unlikely (pattern->status))
+ return pattern->status;
+
+ cairo_matrix_init_translate (&matrix, -x, -y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ status = _cairo_skia_context_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ return status;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_get_source (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_tolerance (void *abstract_cr,
+ double tolerance)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX ignored */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline SkXfermode::Mode
+operator_to_sk (cairo_operator_t op)
+{
+ static const SkXfermode::Mode modeMap[] = {
+ SkXfermode::kClear_Mode,
+
+ SkXfermode::kSrc_Mode,
+ SkXfermode::kSrcOver_Mode,
+ SkXfermode::kSrcIn_Mode,
+ SkXfermode::kSrcOut_Mode,
+ SkXfermode::kSrcATop_Mode,
+
+ SkXfermode::kDst_Mode,
+ SkXfermode::kDstOver_Mode,
+ SkXfermode::kDstIn_Mode,
+ SkXfermode::kDstOut_Mode,
+ SkXfermode::kDstATop_Mode,
+
+ SkXfermode::kXor_Mode,
+ SkXfermode::kPlus_Mode, // XXX Add?
+ SkXfermode::kPlus_Mode, // XXX SATURATE
+
+ SkXfermode::kPlus_Mode,
+ SkXfermode::kMultiply_Mode,
+ SkXfermode::kScreen_Mode,
+ SkXfermode::kOverlay_Mode,
+ SkXfermode::kDarken_Mode,
+ SkXfermode::kLighten_Mode,
+ SkXfermode::kColorDodge_Mode,
+ SkXfermode::kColorBurn_Mode,
+ SkXfermode::kHardLight_Mode,
+ SkXfermode::kSoftLight_Mode,
+ SkXfermode::kDifference_Mode,
+ SkXfermode::kExclusion_Mode,
+
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION,
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR,
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY
+ };
+
+ return modeMap[op];
+}
+
+static cairo_status_t
+_cairo_skia_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setXfermodeMode (operator_to_sk (op));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_opacity (void *abstract_cr, double opacity)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_fill_rule (void *abstract_cr,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->setFillType (fill_rule == CAIRO_FILL_RULE_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_width (void *abstract_cr,
+ double line_width)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setStrokeWidth (SkFloatToScalar (line_width));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_cap (void *abstract_cr,
+ cairo_line_cap_t line_cap)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const SkPaint::Cap map[] = {
+ SkPaint::kButt_Cap,
+ SkPaint::kRound_Cap,
+ SkPaint::kSquare_Cap
+ };
+ cr->paint->setStrokeCap (map[line_cap]);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_join (void *abstract_cr,
+ cairo_line_join_t line_join)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const SkPaint::Join map[] = {
+ SkPaint::kMiter_Join,
+ SkPaint::kRound_Join,
+ SkPaint::kBevel_Join
+ };
+ cr->paint->setStrokeJoin (map[line_join]);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_dash (void *abstract_cr,
+ const double *dashes,
+ int num_dashes,
+ double offset)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkScalar intervals_static[20];
+ SkScalar *intervals = intervals_static;
+
+ if (num_dashes == 0) {
+ cr->paint->setPathEffect (NULL);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ int loop = 0;
+ if ((num_dashes & 1) != 0) {
+ loop = 1;
+ num_dashes <<= 1;
+ }
+
+ if (num_dashes > 20)
+ intervals = new SkScalar[num_dashes];
+
+ int i = 0;
+ do {
+ for (int j = 0; i < num_dashes; j++)
+ intervals[i++] = SkFloatToScalar (dashes[j]);
+ } while (loop--);
+
+ SkDashPathEffect *dash = new SkDashPathEffect (intervals, num_dashes, SkFloatToScalar (offset));
+
+ cr->paint->setPathEffect (dash);
+ dash->unref ();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_miter_limit (void *abstract_cr, double limit)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setStrokeMiter (SkFloatToScalar (limit));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_antialias_t
+_cairo_skia_context_get_antialias (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return cr->paint->isAntiAlias () ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE;
+}
+
+static void
+_cairo_skia_context_get_dash (void *abstract_cr,
+ double *dashes,
+ int *num_dashes,
+ double *offset)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ *num_dashes = 0;
+ /* XXX */
+}
+
+static cairo_fill_rule_t
+_cairo_skia_context_get_fill_rule (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkPath::FillType ft;
+
+ ft = cr->path->getFillType ();
+ if (ft == SkPath::kWinding_FillType)
+ return CAIRO_FILL_RULE_WINDING;
+ if (ft == SkPath::kEvenOdd_FillType)
+ return CAIRO_FILL_RULE_EVEN_ODD;;
+
+ UNSUPPORTED;
+ return CAIRO_FILL_RULE_WINDING;
+}
+
+static double
+_cairo_skia_context_get_line_width (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return /* ScalarToFloat */ cr->paint->getStrokeWidth ();
+}
+
+static cairo_line_cap_t
+_cairo_skia_context_get_line_cap (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const cairo_line_cap_t map[] = {
+ CAIRO_LINE_CAP_BUTT,
+ CAIRO_LINE_CAP_ROUND,
+ CAIRO_LINE_CAP_SQUARE
+ };
+ return map[cr->paint->getStrokeCap ()];
+}
+
+static cairo_line_join_t
+_cairo_skia_context_get_line_join (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const cairo_line_join_t map[] = {
+ CAIRO_LINE_JOIN_MITER,
+ CAIRO_LINE_JOIN_ROUND,
+ CAIRO_LINE_JOIN_BEVEL
+ };
+ return map[cr->paint->getStrokeJoin ()];
+}
+
+static double
+_cairo_skia_context_get_miter_limit (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return /* SkScalarToFloat */ cr->paint->getStrokeMiter ();
+}
+
+static cairo_operator_t
+_cairo_skia_context_get_operator (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ //cr->paint->getXfermode ();
+ return CAIRO_OPERATOR_OVER;
+}
+
+static double
+_cairo_skia_context_get_opacity (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return 1.;
+}
+
+static double
+_cairo_skia_context_get_tolerance (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX */
+ return CAIRO_GSTATE_TOLERANCE_DEFAULT;
+}
+
+
+/* Current tranformation matrix */
+
+static cairo_status_t
+_cairo_skia_context_translate (void *abstract_cr,
+ double tx,
+ double ty)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_translate (&cr->matrix, tx, ty);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_scale (void *abstract_cr,
+ double sx,
+ double sy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_scale (&cr->matrix, sx, sy);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rotate (void *abstract_cr,
+ double theta)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_rotate (&cr->matrix, theta);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_transform (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_multiply (&cr->matrix, &cr->matrix, matrix);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->matrix = *matrix;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_identity_matrix (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_init_identity (&cr->matrix);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ *matrix = cr->matrix;
+}
+
+static void
+_cairo_skia_context_user_to_device (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_transform_point (&cr->matrix, x, y);
+}
+
+static void
+_cairo_skia_context_user_to_device_distance (void *abstract_cr,
+ double *dx,
+ double *dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+}
+
+static void
+_cairo_skia_context_device_to_user (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_matrix_t inverse;
+ cairo_status_t status;
+
+ inverse = cr->matrix;
+ status = cairo_matrix_invert (&inverse);
+ assert (CAIRO_STATUS_SUCCESS == status);
+
+ cairo_matrix_transform_point (&inverse, x, y);
+}
+
+static void
+_cairo_skia_context_device_to_user_distance (void *abstract_cr,
+ double *dx,
+ double *dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_matrix_t inverse;
+ cairo_status_t status;
+
+ inverse = cr->matrix;
+ status = cairo_matrix_invert (&inverse);
+ assert (CAIRO_STATUS_SUCCESS == status);
+
+ cairo_matrix_transform_distance (&inverse, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_skia_context_new_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->reset ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_new_sub_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->rMoveTo (0, 0); /* XXX */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+user_to_device_point (cairo_skia_context_t *cr, double *x, double *y)
+{
+ cairo_matrix_transform_point (&cr->matrix, x, y);
+ cairo_matrix_transform_point (&cr->target->image.base.device_transform, x, y);
+}
+
+static void
+user_to_device_distance (cairo_skia_context_t *cr, double *dx, double *dy)
+{
+ cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+ cairo_matrix_transform_distance (&cr->target->image.base.device_transform, dx, dy);
+}
+
+static cairo_status_t
+_cairo_skia_context_move_to (void *abstract_cr, double x, double y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_point (cr, &x, &y);
+ cr->path->moveTo (SkFloatToScalar (x), SkFloatToScalar (y));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_line_to (void *abstract_cr, double x, double y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_point (cr, &x, &y);
+ cr->path->lineTo (SkFloatToScalar (x), SkFloatToScalar (y));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_curve_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_point (cr, &x1, &y1);
+ user_to_device_point (cr, &x2, &y2);
+ user_to_device_point (cr, &x3, &y3);
+ cr->path->cubicTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+ SkFloatToScalar (x2), SkFloatToScalar (y2),
+ SkFloatToScalar (x3), SkFloatToScalar (y3));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double radius)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+ user_to_device_point (cr, &x1, &y1);
+ user_to_device_point (cr, &x2, &y2);
+ user_to_device_distance (cr, &radius, &radius);
+#endif
+
+ cr->path->arcTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+ SkFloatToScalar (x2), SkFloatToScalar (y2),
+ SkFloatToScalar (radius));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_distance (cr, &dx, &dy);
+ cr->path->rMoveTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_distance (cr, &dx, &dy);
+ cr->path->rLineTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_curve_to (void *abstract_cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_distance (cr, &dx1, &dy1);
+ user_to_device_distance (cr, &dx2, &dy2);
+ user_to_device_distance (cr, &dx3, &dy3);
+ cr->path->rCubicTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+ SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+ SkFloatToScalar (dx3), SkFloatToScalar (dy3));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_arc_to (void *abstract_cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double radius)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+ user_to_device_point (cr, &x1, &y1);
+ user_to_device_point (cr, &x2, &y2);
+ user_to_device_distance (cr, &radius, &radius);
+#endif
+
+ cr->path->arcTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+ SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+ SkFloatToScalar (radius));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_close_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->close ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rectangle (void *abstract_cr,
+ double x, double y,
+ double width, double height)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ double x1, y1, x2, y2;
+
+ /* XXX assume no rotation! */
+ x1 = x, y1 = y;
+ user_to_device_point (cr, &x1, &y1);
+
+ x2 = x + width, y2 = y + height;
+ user_to_device_point (cr, &x2, &y2);
+
+ cr->path->addRect (SkFloatToScalar (x1), SkFloatToScalar (y1),
+ SkFloatToScalar (x2), SkFloatToScalar (y2));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc (void *abstract_cr,
+ double xc, double yc, double radius,
+ double angle1, double angle2,
+ cairo_bool_t forward)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ /* XXX cr->path->arc() */
+
+ /* Do nothing, successfully, if radius is <= 0 */
+ if (radius <= 0.0) {
+ status = _cairo_skia_context_line_to (cr, xc, yc);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_skia_context_line_to (cr, xc, yc);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_skia_context_line_to (cr,
+ xc + radius * cos (angle1),
+ yc + radius * sin (angle1));
+
+ if (unlikely (status))
+ return status;
+
+ if (forward)
+ _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
+ else
+ _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_path_extents (void *abstract_cr,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkRect rect;
+
+ rect = cr->path->getBounds ();
+
+ UNSUPPORTED;
+ /* XXX transform SkScalar rect to user */
+}
+
+static cairo_bool_t
+_cairo_skia_context_has_current_point (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_skia_context_get_current_point (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkPoint pt;
+
+ cr->path->getLastPt (&pt);
+ //*x = SkScalarToFloat (pt.x);
+ //*y = SkScalarToFloat (pt.y);
+ //_cairo_gstate_backend_to_user (cr->gstate, x, y);
+
+ return TRUE;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX iterate */
+ UNSUPPORTED;
+ return NULL;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path_flat (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX iterate and decompose */
+ UNSUPPORTED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_append_path (void *abstract_cr,
+ const cairo_path_t *path)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ // return _cairo_path_append_to_context (path, cr);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_stroke_to_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setStyle (SkPaint::kStroke_Style);
+ cr->paint->getFillPath (*cr->path, cr->path);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+_cairo_skia_context_paint (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+ if (cr->source != NULL) {
+ SkBitmap bitmap;
+ SkMatrix bitmapMatrix;
+
+ if (cr->source->type == CAIRO_SURFACE_TYPE_SKIA) {
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) cr->source->type;
+
+ bitmap = *esurf->bitmap;
+ } else {
+ surface_to_sk_bitmap (&cr->source_image->base, bitmap);
+ }
+
+ // XXX pattern->matrix, pattern->filter, pattern->extend
+ cr->canvas->drawBitmapMatrix (bitmap, bitmapMatrix, cr->paint);
+ } else {
+ cr->canvas->drawPaint (*cr->paint);
+ }
+#else
+ cr->canvas->drawPaint (*cr->paint);
+#endif
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_paint_with_alpha (void *abstract_cr,
+ double alpha)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ if (CAIRO_ALPHA_IS_OPAQUE (alpha))
+ return _cairo_skia_context_paint (cr);
+
+ cr->paint->setAlpha(SkScalarRound(255*alpha));
+ status = _cairo_skia_context_paint (cr);
+ cr->paint->setAlpha(255);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_skia_context_mask (void *abstract_cr,
+ cairo_pattern_t *mask)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX */
+ //UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_preserve (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setStyle (SkPaint::kStroke_Style);
+
+ /* XXX pen transformation? */
+ //assert (_cairo_matrix_is_identity (&cr->matrix));
+ cr->canvas->drawPath (*cr->path, *cr->paint);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_skia_context_stroke_preserve (cr);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_stroke (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_preserve (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setStyle (SkPaint::kFill_Style);
+ cr->canvas->drawPath (*cr->path, *cr->paint);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_skia_context_fill_preserve (cr);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_fill (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_preserve (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->canvas->clipPath (*cr->path);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_skia_context_clip_preserve (cr);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_clip (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_reset_clip (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkRegion rgn(SkIRect::MakeWH (cr->target->bitmap->width (),
+ cr->target->bitmap->height ()));
+
+ cr->canvas->setClipRegion(rgn);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_extents (void *abstract_cr,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_rectangle_list_t *
+_cairo_skia_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_copy_page (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_show_page (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_face (void *abstract_cr,
+ cairo_font_face_t *font_face)
+{
+ // cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ //return _cairo_gstate_set_font_face (cr->gstate, font_face);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_font_face_t *
+_cairo_skia_context_get_font_face (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_font_extents (void *abstract_cr,
+ cairo_font_extents_t *extents)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_size (void *abstract_cr,
+ double size)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_options (void *abstract_cr,
+ const cairo_font_options_t *options)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_options (void *abstract_cr,
+ cairo_font_options_t *options)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_scaled_font (void *abstract_cr,
+ cairo_scaled_font_t *scaled_font)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_scaled_font_t *
+_cairo_skia_context_get_scaled_font (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return _cairo_scaled_font_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+}
+
+static cairo_status_t
+_cairo_skia_context_glyphs (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_glyph_text_info_t *info)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX */
+ //UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_path (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_extents (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ UNSUPPORTED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_backend_t _cairo_skia_context_backend = {
+ CAIRO_TYPE_SKIA,
+ _cairo_skia_context_destroy,
+
+ _cairo_skia_context_get_original_target,
+ _cairo_skia_context_get_current_target,
+
+ _cairo_skia_context_save,
+ _cairo_skia_context_restore,
+
+ _cairo_skia_context_push_group,
+ _cairo_skia_context_pop_group,
+
+ _cairo_skia_context_set_source_rgba,
+ _cairo_skia_context_set_source_surface,
+ _cairo_skia_context_set_source,
+ _cairo_skia_context_get_source,
+
+ _cairo_skia_context_set_antialias,
+ _cairo_skia_context_set_dash,
+ _cairo_skia_context_set_fill_rule,
+ _cairo_skia_context_set_line_cap,
+ _cairo_skia_context_set_line_join,
+ _cairo_skia_context_set_line_width,
+ _cairo_skia_context_set_miter_limit,
+ _cairo_skia_context_set_opacity,
+ _cairo_skia_context_set_operator,
+ _cairo_skia_context_set_tolerance,
+ _cairo_skia_context_get_antialias,
+ _cairo_skia_context_get_dash,
+ _cairo_skia_context_get_fill_rule,
+ _cairo_skia_context_get_line_cap,
+ _cairo_skia_context_get_line_join,
+ _cairo_skia_context_get_line_width,
+ _cairo_skia_context_get_miter_limit,
+ _cairo_skia_context_get_opacity,
+ _cairo_skia_context_get_operator,
+ _cairo_skia_context_get_tolerance,
+
+ _cairo_skia_context_translate,
+ _cairo_skia_context_scale,
+ _cairo_skia_context_rotate,
+ _cairo_skia_context_transform,
+ _cairo_skia_context_set_matrix,
+ _cairo_skia_context_set_identity_matrix,
+ _cairo_skia_context_get_matrix,
+ _cairo_skia_context_user_to_device,
+ _cairo_skia_context_user_to_device_distance,
+ _cairo_skia_context_device_to_user,
+ _cairo_skia_context_device_to_user_distance,
+ _cairo_skia_context_user_to_device, /* XXX backend */
+ _cairo_skia_context_user_to_device_distance, /* XXX backend */
+ _cairo_skia_context_device_to_user, /* XXX backend */
+ _cairo_skia_context_device_to_user_distance, /* XXX backend */
+
+ _cairo_skia_context_new_path,
+ _cairo_skia_context_new_sub_path,
+ _cairo_skia_context_move_to,
+ _cairo_skia_context_rel_move_to,
+ _cairo_skia_context_line_to,
+ _cairo_skia_context_rel_line_to,
+ _cairo_skia_context_curve_to,
+ _cairo_skia_context_rel_curve_to,
+ _cairo_skia_context_arc_to,
+ _cairo_skia_context_rel_arc_to,
+ _cairo_skia_context_close_path,
+ _cairo_skia_context_arc,
+ _cairo_skia_context_rectangle,
+ _cairo_skia_context_path_extents,
+ _cairo_skia_context_has_current_point,
+ _cairo_skia_context_get_current_point,
+ _cairo_skia_context_copy_path,
+ _cairo_skia_context_copy_path_flat,
+ _cairo_skia_context_append_path,
+
+ _cairo_skia_stroke_to_path,
+
+ _cairo_skia_context_clip,
+ _cairo_skia_context_clip_preserve,
+ _cairo_skia_context_in_clip,
+ _cairo_skia_context_clip_extents,
+ _cairo_skia_context_reset_clip,
+ _cairo_skia_context_copy_clip_rectangle_list,
+
+ _cairo_skia_context_paint,
+ _cairo_skia_context_paint_with_alpha,
+ _cairo_skia_context_mask,
+
+ _cairo_skia_context_stroke,
+ _cairo_skia_context_stroke_preserve,
+ _cairo_skia_context_in_stroke,
+ _cairo_skia_context_stroke_extents,
+
+ _cairo_skia_context_fill,
+ _cairo_skia_context_fill_preserve,
+ _cairo_skia_context_in_fill,
+ _cairo_skia_context_fill_extents,
+
+ _cairo_skia_context_set_font_face,
+ _cairo_skia_context_get_font_face,
+ _cairo_skia_context_set_font_size,
+ _cairo_skia_context_set_font_matrix,
+ _cairo_skia_context_get_font_matrix,
+ _cairo_skia_context_set_font_options,
+ _cairo_skia_context_get_font_options,
+ _cairo_skia_context_set_scaled_font,
+ _cairo_skia_context_get_scaled_font,
+ _cairo_skia_context_font_extents,
+
+ _cairo_skia_context_glyphs,
+ _cairo_skia_context_glyph_path,
+ _cairo_skia_context_glyph_extents,
+
+ _cairo_skia_context_copy_page,
+ _cairo_skia_context_show_page,
+};
+
+cairo_t *
+_cairo_skia_context_create (void *target)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target;
+ cairo_skia_context_t *cr;
+
+ cr = (cairo_skia_context_t *) _freed_pool_get (&context_pool);
+ if (unlikely (cr == NULL)) {
+ cr = new cairo_skia_context_t;
+ if (unlikely (cr == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ cr->path = new SkPath;
+ cr->paint = new SkPaint;
+ }
+
+ _cairo_init (&cr->base, &_cairo_skia_context_backend);
+
+ cr->source = NULL;
+ cr->source_image = NULL;
+
+ cr->paint->setStrokeWidth (SkFloatToScalar (2.0));
+
+ cr->target = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+ cr->original = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+ cr->canvas = new SkCanvas (*surface->bitmap);
+ cr->canvas->save ();
+
+ cairo_matrix_init_identity (&cr->matrix);
+
+ return &cr->base;
+}
+
+#if 0
+void
+_cairo_skia_context_set_SkPaint (cairo_t *cr, SkPaint paint)
+{
+ *cr->paint = paint;
+}
+
+void
+_cairo_skia_context_set_SkPath (cairo_t *cr, SkPath path)
+{
+ *cr->path = path;
+}
+#endif
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
new file mode 100755
index 000000000..cbd8c888d
--- /dev/null
+++ b/src/skia/cairo-skia-private.h
@@ -0,0 +1,110 @@
+/* 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 D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_SKIA_CONTEXT_PRIVATE_H
+#define CAIRO_SKIA_CONTEXT_PRIVATE_H
+
+#include "cairo-private.h"
+#include "cairo-image-surface-private.h"
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+
+typedef struct _cairo_skia_context cairo_skia_context_t;
+typedef struct _cairo_skia_surface cairo_skia_surface_t;
+
+struct _cairo_skia_context {
+ cairo_t base;
+
+ cairo_skia_surface_t *original;
+ cairo_skia_surface_t *target;
+
+ cairo_matrix_t matrix;
+
+ SkCanvas *canvas;
+ SkPaint *paint;
+ SkPath *path;
+
+ cairo_surface_t *source;
+ cairo_image_surface_t *source_image;
+ void *source_extra;
+};
+
+struct _cairo_skia_surface {
+ cairo_image_surface_t image;
+
+ SkBitmap *bitmap;
+};
+
+static inline bool
+format_to_sk_config (cairo_format_t format,
+ SkBitmap::Config& config,
+ bool& opaque)
+{
+ opaque = false;
+
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ config = SkBitmap::kARGB_8888_Config;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ config = SkBitmap::kARGB_8888_Config;
+ opaque = true;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ config = SkBitmap::kRGB_565_Config;
+ opaque = true;
+ break;
+ case CAIRO_FORMAT_A8:
+ config = SkBitmap::kA8_Config;
+ break;
+ case CAIRO_FORMAT_A1:
+ config = SkBitmap::kA1_Config;
+ break;
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+cairo_private cairo_t *
+_cairo_skia_context_create (void *target);
+
+#endif /* CAIRO_SKIA_CONTEXT_PRIVATE_H */
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
new file mode 100755
index 000000000..bb785e192
--- /dev/null
+++ b/src/skia/cairo-skia-surface.cpp
@@ -0,0 +1,324 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-skia.h"
+#include "cairo-skia-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-fallback-private.h"
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+ bool opaque,
+ unsigned char *data,
+ int width,
+ int height,
+ int stride);
+
+static cairo_surface_t *
+_cairo_skia_surface_create_similar (void *asurface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (content == surface->image.base.content)
+ {
+ config = surface->bitmap->getConfig ();
+ opaque = surface->bitmap->isOpaque ();
+ }
+ else if (! format_to_sk_config (_cairo_format_from_content (content),
+ config, opaque))
+ {
+ return NULL;
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque,
+ NULL,
+ width, height,
+ 0)->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_surface_finish (void *asurface)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ cairo_surface_finish (&surface->image.base);
+ delete surface->bitmap;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_image_surface_t *
+_cairo_skia_surface_map_to_image (void *asurface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->lockPixels ();
+ return _cairo_image_surface_map_to_image (&surface->image, extents);
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_unmap_image (void *asurface,
+ cairo_image_surface_t *image)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ cairo_int_status_t status;
+
+ status = _cairo_image_surface_unmap_image (&surface->image, image);
+ surface->bitmap->unlockPixels ();
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_skia_surface_acquire_source_image (void *asurface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->lockPixels ();
+
+ *image_out = &surface->image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_source_image (void *asurface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->unlockPixels ();
+}
+
+static cairo_bool_t
+_cairo_skia_surface_get_extents (void *asurface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ extents->x = extents->y = 0;
+ extents->width = surface->image.width;
+ extents->height = surface->image.height;
+ return TRUE;
+}
+
+static void
+_cairo_skia_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 const struct _cairo_surface_backend
+cairo_skia_surface_backend = {
+ CAIRO_SURFACE_TYPE_SKIA,
+ _cairo_skia_surface_finish,
+
+ _cairo_skia_context_create,
+
+ _cairo_skia_surface_create_similar,
+ NULL, //_cairo_skia_surface_create_similar_image,
+ _cairo_skia_surface_map_to_image,
+ _cairo_skia_surface_unmap_image,
+
+ _cairo_surface_default_source,
+ _cairo_skia_surface_acquire_source_image,
+ _cairo_skia_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_skia_surface_get_extents,
+ _cairo_skia_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ /* XXX native surface functions? */
+ _cairo_surface_fallback_paint,
+ _cairo_surface_fallback_mask,
+ _cairo_surface_fallback_stroke,
+ _cairo_surface_fallback_fill,
+ NULL, /* fill/stroke */
+ _cairo_surface_fallback_glyphs
+};
+
+/*
+ * Surface constructors
+ */
+
+static inline pixman_format_code_t
+sk_config_to_pixman_format_code (SkBitmap::Config config,
+ bool opaque)
+{
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ return opaque ? PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8;
+
+ case SkBitmap::kA8_Config:
+ return PIXMAN_a8;
+
+ case SkBitmap::kA1_Config:
+ return PIXMAN_a1;
+ case SkBitmap::kRGB_565_Config:
+ return PIXMAN_r5g6b5;
+ case SkBitmap::kARGB_4444_Config:
+ return PIXMAN_a4r4g4b4;
+
+ case SkBitmap::kNo_Config:
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kRLE_Index8_Config:
+ case SkBitmap::kConfigCount:
+ default:
+ ASSERT_NOT_REACHED;
+ return (pixman_format_code_t) -1;
+ }
+}
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+ bool opaque,
+ unsigned char *data,
+ int width,
+ int height,
+ int stride)
+{
+ cairo_skia_surface_t *surface;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+
+ surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t));
+ if (unlikely (surface == NULL))
+ return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ pixman_format = sk_config_to_pixman_format_code (config, opaque);
+ pixman_image = pixman_image_create_bits (pixman_format,
+ width, height,
+ (uint32_t *) data, stride);
+ if (unlikely (pixman_image == NULL))
+ return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->image.base,
+ &cairo_skia_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_pixman_format (pixman_format));
+
+ _cairo_image_surface_init (&surface->image, pixman_image, pixman_format);
+
+ surface->bitmap = new SkBitmap;
+ surface->bitmap->setConfig (config, width, height, surface->image.stride);
+ surface->bitmap->setIsOpaque (opaque);
+ surface->bitmap->setPixels (surface->image.data);
+
+ surface->image.base.is_clear = data == NULL;
+
+ return surface;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+ int width,
+ int height)
+{
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (! CAIRO_FORMAT_VALID (format) ||
+ ! format_to_sk_config (format, config, opaque))
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride)
+{
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (! CAIRO_FORMAT_VALID (format) ||
+ ! format_to_sk_config (format, config, opaque))
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base;
+}
+
+/***
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+***/
diff --git a/src/test-base-compositor-surface.c b/src/test-base-compositor-surface.c
new file mode 100755
index 000000000..ff84b10af
--- /dev/null
+++ b/src/test-base-compositor-surface.c
@@ -0,0 +1,824 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "test-compositor-surface-private.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-traps-private.h"
+
+/* The intention is that this is a surface that just works, and most
+ * important of all does not try to be clever!
+ */
+
+typedef cairo_int_status_t
+(*draw_func_t) (cairo_image_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents);
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+ switch ((int) op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return PIXMAN_OP_CLEAR;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return PIXMAN_OP_SRC;
+ case CAIRO_OPERATOR_OVER:
+ return PIXMAN_OP_OVER;
+ case CAIRO_OPERATOR_IN:
+ return PIXMAN_OP_IN;
+ case CAIRO_OPERATOR_OUT:
+ return PIXMAN_OP_OUT;
+ case CAIRO_OPERATOR_ATOP:
+ return PIXMAN_OP_ATOP;
+
+ case CAIRO_OPERATOR_DEST:
+ return PIXMAN_OP_DST;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return PIXMAN_OP_OVER_REVERSE;
+ case CAIRO_OPERATOR_DEST_IN:
+ return PIXMAN_OP_IN_REVERSE;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return PIXMAN_OP_OUT_REVERSE;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return PIXMAN_OP_ATOP_REVERSE;
+
+ case CAIRO_OPERATOR_XOR:
+ return PIXMAN_OP_XOR;
+ case CAIRO_OPERATOR_ADD:
+ return PIXMAN_OP_ADD;
+ case CAIRO_OPERATOR_SATURATE:
+ return PIXMAN_OP_SATURATE;
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ return PIXMAN_OP_MULTIPLY;
+ case CAIRO_OPERATOR_SCREEN:
+ return PIXMAN_OP_SCREEN;
+ case CAIRO_OPERATOR_OVERLAY:
+ return PIXMAN_OP_OVERLAY;
+ case CAIRO_OPERATOR_DARKEN:
+ return PIXMAN_OP_DARKEN;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return PIXMAN_OP_LIGHTEN;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return PIXMAN_OP_COLOR_DODGE;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return PIXMAN_OP_COLOR_BURN;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return PIXMAN_OP_HARD_LIGHT;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return PIXMAN_OP_SOFT_LIGHT;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return PIXMAN_OP_DIFFERENCE;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return PIXMAN_OP_EXCLUSION;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return PIXMAN_OP_HSL_HUE;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return PIXMAN_OP_HSL_SATURATION;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return PIXMAN_OP_HSL_COLOR;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return PIXMAN_OP_HSL_LUMINOSITY;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return PIXMAN_OP_OVER;
+ }
+}
+
+static cairo_image_surface_t *
+create_composite_mask (cairo_image_surface_t *dst,
+ void *draw_closure,
+ draw_func_t draw_func,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_image_surface_t *surface;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ surface = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL, PIXMAN_a8,
+ extents->bounded.width,
+ extents->bounded.height,
+ 0);
+ if (unlikely (surface->base.status))
+ return surface;
+
+ status = draw_func (surface, draw_closure,
+ CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded);
+ if (unlikely (status))
+ goto error;
+
+ status = _cairo_clip_combine_with_surface (extents->clip,
+ &surface->base,
+ extents->bounded.x,
+ extents->bounded.y);
+ if (unlikely (status))
+ goto error;
+
+ return surface;
+
+error:
+ cairo_surface_destroy (&surface->base);
+ return (cairo_image_surface_t *)_cairo_surface_create_in_error (status);
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+clip_and_composite_with_mask (const cairo_composite_rectangles_t*extents,
+ draw_func_t draw_func,
+ void *draw_closure)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+ cairo_image_surface_t *mask;
+ pixman_image_t *src;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ int src_x, src_y;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ mask = create_composite_mask (dst, draw_closure, draw_func, extents);
+ if (unlikely (mask->base.status))
+ return mask->base.status;
+
+ src = _pixman_image_for_pattern (dst,
+ &extents->source_pattern.base, FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto error;
+ }
+
+ pixman_image_composite32 (_pixman_operator (extents->op),
+ src, mask->pixman_image, dst->pixman_image,
+ extents->bounded.x + src_x,
+ extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ pixman_image_unref (src);
+error:
+ cairo_surface_destroy (&mask->base);
+ return status;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+clip_and_composite_combine (const cairo_composite_rectangles_t*extents,
+ draw_func_t draw_func,
+ void *draw_closure)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+ cairo_image_surface_t *tmp, *clip;
+ int clip_x, clip_y;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ tmp = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ dst->pixman_format,
+ extents->bounded.width,
+ extents->bounded.height,
+ 0);
+ if (unlikely (tmp->base.status))
+ return tmp->base.status;
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ dst->pixman_image, NULL, tmp->pixman_image,
+ extents->bounded.x, extents->bounded.y,
+ 0, 0,
+ 0, 0,
+ extents->bounded.width, extents->bounded.height);
+
+ status = draw_func (tmp, draw_closure,
+ extents->op, &extents->source_pattern.base,
+ extents->bounded.x, extents->bounded.y,
+ &extents->bounded);
+ if (unlikely (status))
+ goto error;
+
+ clip = (cairo_image_surface_t *)
+ _cairo_clip_get_surface (extents->clip, &dst->base, &clip_x, &clip_y);
+ if (unlikely (clip->base.status))
+ goto error;
+
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ clip->pixman_image, NULL, dst->pixman_image,
+ extents->bounded.x - clip_x, extents->bounded.y - clip_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ tmp->pixman_image, clip->pixman_image, dst->pixman_image,
+ 0, 0,
+ extents->bounded.x - clip_x, extents->bounded.y - clip_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ cairo_surface_destroy (&clip->base);
+
+ error:
+ cairo_surface_destroy (&tmp->base);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+clip_and_composite_source (const cairo_composite_rectangles_t *extents,
+ draw_func_t draw_func,
+ void *draw_closure)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+ cairo_image_surface_t *mask;
+ pixman_image_t *src;
+ int src_x, src_y;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ mask = create_composite_mask (dst, draw_closure, draw_func, extents);
+ if (unlikely (mask->base.status))
+ return mask->base.status;
+
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask->pixman_image, NULL, dst->pixman_image,
+ 0, 0,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ src = _pixman_image_for_pattern (dst,
+ &extents->source_pattern.base, FALSE,
+ &extents->bounded,
+ &extents->source_sample_area,
+ &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto error;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ src, mask->pixman_image, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ pixman_image_unref (src);
+
+error:
+ cairo_surface_destroy (&mask->base);
+ return status;
+}
+
+static cairo_status_t
+fixup_unbounded (const cairo_composite_rectangles_t *extents)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+ pixman_image_t *mask;
+ int mask_x, mask_y;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (! _cairo_clip_is_region (extents->clip)) {
+ cairo_image_surface_t *clip;
+
+ clip = (cairo_image_surface_t *)
+ _cairo_clip_get_surface (extents->clip, &dst->base,
+ &mask_x, &mask_y);
+ if (unlikely (clip->base.status))
+ return clip->base.status;
+
+ mask = pixman_image_ref (clip->pixman_image);
+ cairo_surface_destroy (&clip->base);
+ } else {
+ mask_x = mask_y = 0;
+ mask = _pixman_image_for_color (CAIRO_COLOR_WHITE);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* top */
+ if (extents->bounded.y != extents->unbounded.y) {
+ int x = extents->unbounded.x;
+ int y = extents->unbounded.y;
+ int width = extents->unbounded.width;
+ int height = extents->bounded.y - y;
+
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* left */
+ if (extents->bounded.x != extents->unbounded.x) {
+ int x = extents->unbounded.x;
+ int y = extents->bounded.y;
+ int width = extents->bounded.x - x;
+ int height = extents->bounded.height;
+
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* right */
+ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+ int x = extents->bounded.x + extents->bounded.width;
+ int y = extents->bounded.y;
+ int width = extents->unbounded.x + extents->unbounded.width - x;
+ int height = extents->bounded.height;
+
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ /* bottom */
+ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+ int x = extents->unbounded.x;
+ int y = extents->bounded.y + extents->bounded.height;
+ int width = extents->unbounded.width;
+ int height = extents->unbounded.y + extents->unbounded.height - y;
+
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x - mask_x, y - mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ }
+
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (cairo_composite_rectangles_t *extents)
+{
+ cairo_image_surface_t *dst = (cairo_image_surface_t *) extents->surface;
+ cairo_region_t *region = _cairo_clip_get_region (extents->clip);
+ pixman_region32_t *rgn = region ? &region->rgn : NULL;
+ if (! pixman_image_set_clip_region32 (dst->pixman_image, rgn))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+clip_and_composite (cairo_composite_rectangles_t *extents,
+ draw_func_t draw_func,
+ void *draw_closure)
+{
+ cairo_status_t status;
+
+ status = set_clip_region (extents);
+ if (unlikely (status))
+ return status;
+
+ if (extents->op == CAIRO_OPERATOR_SOURCE) {
+ status = clip_and_composite_source (extents, draw_func, draw_closure);
+ } else {
+ if (extents->op == CAIRO_OPERATOR_CLEAR) {
+ extents->source_pattern.solid = _cairo_pattern_white;
+ extents->op = CAIRO_OPERATOR_DEST_OUT;
+ }
+ if (! _cairo_clip_is_region (extents->clip)) {
+ if (extents->is_bounded)
+ status = clip_and_composite_with_mask (extents, draw_func, draw_closure);
+ else
+ status = clip_and_composite_combine (extents, draw_func, draw_closure);
+ } else {
+ status = draw_func ((cairo_image_surface_t *) extents->surface,
+ draw_closure,
+ extents->op,
+ &extents->source_pattern.base,
+ 0, 0,
+ &extents->bounded);
+ }
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
+ status = fixup_unbounded (extents);
+
+ return status;
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+composite_paint (cairo_image_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_rectangle_int_t sample;
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ _cairo_pattern_sampled_area (pattern, extents, &sample);
+ src = _pixman_image_for_pattern (dst,
+ pattern, FALSE,
+ extents, &sample,
+ &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ TRACE ((stderr, "%s: src=(%d, %d), dst=(%d, %d) size=%dx%d\n", __FUNCTION__,
+ extents->x + src_x, extents->y + src_y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height));
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, NULL, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ pixman_image_unref (src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+base_compositor_paint (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return clip_and_composite (extents, composite_paint, NULL);
+}
+
+static cairo_int_status_t
+composite_mask (cairo_image_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_rectangle_int_t sample;
+ pixman_image_t *src, *mask;
+ int src_x, src_y;
+ int mask_x, mask_y;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ _cairo_pattern_sampled_area (pattern, extents, &sample);
+ src = _pixman_image_for_pattern (dst, pattern, FALSE,
+ extents, &sample,
+ &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_pattern_sampled_area (closure, extents, &sample);
+ mask = _pixman_image_for_pattern (dst, closure, TRUE,
+ extents, &sample,
+ &mask_x, &mask_y);
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ extents->x + mask_x, extents->y + mask_y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+base_compositor_mask (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return clip_and_composite (extents, composite_mask, &extents->mask_pattern.base);
+}
+
+typedef struct {
+ cairo_traps_t traps;
+ cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+static cairo_int_status_t
+composite_traps (cairo_image_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents)
+{
+ composite_traps_info_t *info = closure;
+ cairo_rectangle_int_t sample;
+ pixman_image_t *src, *mask;
+ int src_x, src_y;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ _cairo_pattern_sampled_area (pattern, extents, &sample);
+ src = _pixman_image_for_pattern (dst, pattern, FALSE,
+ extents, &sample,
+ &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ mask = pixman_image_create_bits (info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _pixman_image_add_traps (mask, extents->x, extents->y, &info->traps);
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->x + src_x - dst_x, extents->y + src_y - dst_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+trim_extents_to_traps (cairo_composite_rectangles_t *extents,
+ cairo_traps_t *traps)
+{
+ cairo_box_t box;
+
+ /* X trims the affected area to the extents of the trapezoids, so
+ * we need to compensate when fixing up the unbounded area.
+ */
+ _cairo_traps_extents (traps, &box);
+ return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+base_compositor_stroke (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ composite_traps_info_t info;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ info.antialias = antialias;
+ _cairo_traps_init_with_clip (&info.traps, extents->clip);
+ status = _cairo_path_fixed_stroke_polygon_to_traps (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &info.traps);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = trim_extents_to_traps (extents, &info.traps);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite (extents, composite_traps, &info);
+ _cairo_traps_fini (&info.traps);
+
+ return status;
+}
+
+static cairo_int_status_t
+base_compositor_fill (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ composite_traps_info_t info;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ info.antialias = antialias;
+ _cairo_traps_init_with_clip (&info.traps, extents->clip);
+ status = _cairo_path_fixed_fill_to_traps (path,
+ fill_rule, tolerance,
+ &info.traps);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = trim_extents_to_traps (extents, &info.traps);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = clip_and_composite (extents, composite_traps, &info);
+ _cairo_traps_fini (&info.traps);
+
+ return status;
+}
+
+static cairo_int_status_t
+composite_glyphs (cairo_image_surface_t *dst,
+ void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_composite_glyphs_info_t *info = closure;
+ pixman_image_t *mask;
+ cairo_status_t status;
+ int i;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = CAIRO_STATUS_SUCCESS;
+ _cairo_scaled_font_freeze_cache (info->font);
+ for (i = 0; i < info->num_glyphs; i++) {
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+ unsigned long glyph_index = info->glyphs[i].index;
+ int x, y;
+
+ status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ break;
+
+ glyph_surface = scaled_glyph->surface;
+ if (glyph_surface->width && glyph_surface->height) {
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (info->glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (info->glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ glyph_surface->pixman_image, NULL, mask,
+ 0, 0,
+ 0, 0,
+ x - extents->x, y - extents->y,
+ glyph_surface->width,
+ glyph_surface->height);
+ }
+ }
+ _cairo_scaled_font_thaw_cache (info->font);
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_rectangle_int_t sample;
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ _cairo_pattern_sampled_area (pattern, extents, &sample);
+ src = _pixman_image_for_pattern (dst, pattern, FALSE,
+ extents, &sample,
+ &src_x, &src_y);
+ if (src != NULL) {
+ dst_x = extents->x - dst_x;
+ dst_y = extents->y - dst_y;
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ src_x + dst_x, src_y + dst_y,
+ 0, 0,
+ dst_x, dst_y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ } else
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ pixman_image_unref (mask);
+
+ return status;
+}
+
+static cairo_int_status_t
+base_compositor_glyphs (const cairo_compositor_t *_compositor,
+ cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_composite_glyphs_info_t info;
+
+ info.font = scaled_font;
+ info.glyphs = glyphs;
+ info.num_glyphs = num_glyphs;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return clip_and_composite (extents, composite_glyphs, &info);
+}
+
+static const cairo_compositor_t base_compositor = {
+ &__cairo_no_compositor,
+
+ base_compositor_paint,
+ base_compositor_mask,
+ base_compositor_stroke,
+ base_compositor_fill,
+ base_compositor_glyphs,
+};
+
+cairo_surface_t *
+_cairo_test_base_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (&base_compositor,
+ content, width, height);
+}
diff --git a/src/test-compositor-surface-private.h b/src/test-compositor-surface-private.h
new file mode 100755
index 000000000..491f241ba
--- /dev/null
+++ b/src/test-compositor-surface-private.h
@@ -0,0 +1,56 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef TEST_COMPOSITOR_SURFACE_PRIVATE_H
+#define TEST_COMPOSITOR_SURFACE_PRIVATE_H
+
+#include "cairo.h"
+
+#include "test-compositor-surface.h"
+
+#include "cairo-compiler-private.h"
+#include "cairo-compositor-private.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_surface_t *
+test_compositor_surface_create (const cairo_compositor_t *compositor,
+ cairo_content_t content,
+ int width,
+ int height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_COMPOSITOR_SURFACE_PRIVATE H */
diff --git a/src/test-compositor-surface.c b/src/test-compositor-surface.c
new file mode 100755
index 000000000..ddee06f3e
--- /dev/null
+++ b/src/test-compositor-surface.c
@@ -0,0 +1,260 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "test-compositor-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef struct _test_compositor_surface {
+ cairo_image_surface_t base;
+} test_compositor_surface_t;
+
+static const cairo_surface_backend_t test_compositor_surface_backend;
+
+cairo_surface_t *
+test_compositor_surface_create (const cairo_compositor_t *compositor,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ test_compositor_surface_t *surface;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ pixman_format = PIXMAN_a8;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ default:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+ }
+
+ pixman_image = pixman_image_create_bits (pixman_format, width, height,
+ NULL, 0);
+ if (unlikely (pixman_image == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface = malloc (sizeof (test_compositor_surface_t));
+ if (unlikely (surface == NULL)) {
+ pixman_image_unref (pixman_image);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&surface->base.base,
+ &test_compositor_surface_backend,
+ NULL, /* device */
+ content);
+ _cairo_image_surface_init (&surface->base, pixman_image, pixman_format);
+
+ surface->base.compositor = compositor;
+
+ return &surface->base.base;
+}
+
+static cairo_surface_t *
+test_compositor_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ test_compositor_surface_t *surface = abstract_surface;
+
+ return test_compositor_surface_create (surface->base.compositor,
+ content, width, height);
+}
+
+static cairo_int_status_t
+test_compositor_surface_paint (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_paint (surface->base.compositor,
+ _surface, op, source,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_mask (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_mask (surface->base.compositor,
+ _surface, op, source, mask,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_stroke (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_stroke (surface->base.compositor,
+ _surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_fill (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_fill (surface->base.compositor,
+ _surface, op, source,
+ path, fill_rule, tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_glyphs (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_glyphs (surface->base.compositor,
+ _surface, op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+}
+
+static const cairo_surface_backend_t test_compositor_surface_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_image_surface_finish,
+ _cairo_default_context_create,
+
+ test_compositor_surface_create_similar,
+ NULL, /* create similar image */
+ _cairo_image_surface_map_to_image,
+ _cairo_image_surface_unmap_image,
+
+ _cairo_image_surface_source,
+ _cairo_image_surface_acquire_source_image,
+ _cairo_image_surface_release_source_image,
+ _cairo_image_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_image_surface_get_extents,
+ _cairo_image_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ test_compositor_surface_paint,
+ test_compositor_surface_mask,
+ test_compositor_surface_stroke,
+ test_compositor_surface_fill,
+ NULL, /* fill/stroke */
+ test_compositor_surface_glyphs,
+};
+
+static const cairo_compositor_t *
+get_fallback_compositor (void)
+{
+ return &_cairo_fallback_compositor;
+}
+
+cairo_surface_t *
+_cairo_test_fallback_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (get_fallback_compositor(),
+ content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_mask_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (_cairo_image_mask_compositor_get(),
+ content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_traps_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (_cairo_image_traps_compositor_get(),
+ content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_spans_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (_cairo_image_spans_compositor_get(),
+ content, width, height);
+}
diff --git a/src/test-compositor-surface.h b/src/test-compositor-surface.h
new file mode 100755
index 000000000..8d8af2d54
--- /dev/null
+++ b/src/test-compositor-surface.h
@@ -0,0 +1,71 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef TEST_COMPOSITOR_SURFACE_H
+#define TEST_COMPOSITOR_SURFACE_H
+
+#include "cairo.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+_cairo_test_fallback_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+
+cairo_surface_t *
+_cairo_test_mask_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+cairo_surface_t *
+_cairo_test_traps_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+cairo_surface_t *
+_cairo_test_spans_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+cairo_surface_t *
+_cairo_test_base_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_COMPOSITOR_SURFACE_H */
diff --git a/src/test-null-compositor-surface.c b/src/test-null-compositor-surface.c
new file mode 100755
index 000000000..4fdaac4a3
--- /dev/null
+++ b/src/test-null-compositor-surface.c
@@ -0,0 +1,482 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+
+#include "cairoint.h"
+
+#include "test-null-compositor-surface.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-spans-private.h"
+
+typedef struct _test_compositor_surface {
+ cairo_image_surface_t base;
+} test_compositor_surface_t;
+
+static const cairo_surface_backend_t test_compositor_surface_backend;
+
+static cairo_surface_t *
+test_compositor_surface_create (const cairo_compositor_t *compositor,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ test_compositor_surface_t *surface;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ pixman_format = PIXMAN_a8;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ default:
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+ }
+
+ pixman_image = pixman_image_create_bits (pixman_format, width, height,
+ NULL, 0);
+ if (unlikely (pixman_image == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface = malloc (sizeof (test_compositor_surface_t));
+ if (unlikely (surface == NULL)) {
+ pixman_image_unref (pixman_image);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&surface->base.base,
+ &test_compositor_surface_backend,
+ NULL, /* device */
+ content);
+ _cairo_image_surface_init (&surface->base, pixman_image, pixman_format);
+
+ surface->base.compositor = compositor;
+
+ return &surface->base.base;
+}
+
+static cairo_surface_t *
+test_compositor_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ test_compositor_surface_t *surface = abstract_surface;
+
+ return test_compositor_surface_create (surface->base.compositor,
+ content, width, height);
+}
+
+static cairo_int_status_t
+test_compositor_surface_paint (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_paint (surface->base.compositor,
+ _surface, op, source,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_mask (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_mask (surface->base.compositor,
+ _surface, op, source, mask,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_stroke (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_stroke (surface->base.compositor,
+ _surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_fill (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_fill (surface->base.compositor,
+ _surface, op, source,
+ path, fill_rule, tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_glyphs (void *_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ test_compositor_surface_t *surface = _surface;
+ return _cairo_compositor_glyphs (surface->base.compositor,
+ _surface, op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+}
+
+static const cairo_surface_backend_t test_compositor_surface_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_image_surface_finish,
+ _cairo_default_context_create,
+
+ test_compositor_surface_create_similar,
+ NULL, /* create similar image */
+ _cairo_image_surface_map_to_image,
+ _cairo_image_surface_unmap_image,
+
+ _cairo_image_surface_source,
+ _cairo_image_surface_acquire_source_image,
+ _cairo_image_surface_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_image_surface_get_extents,
+ _cairo_image_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ test_compositor_surface_paint,
+ test_compositor_surface_mask,
+ test_compositor_surface_stroke,
+ test_compositor_surface_fill,
+ NULL, /* fill/stroke */
+ test_compositor_surface_glyphs,
+};
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+ cairo_region_t *region)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+pattern_to_surface (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+}
+
+static cairo_int_status_t
+fill_boxes (void *_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_boxes_t *boxes)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+ cairo_image_surface_t *image,
+ cairo_boxes_t *boxes,
+ int dx, int dy)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp (void *_dst,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ cairo_surface_t *abstract_mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ cairo_boxes_t *boxes,
+ const cairo_rectangle_int_t *extents)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_traps (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *abstract_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int *num_glyphs)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_glyphs (void *_dst,
+ cairo_operator_t op,
+ cairo_surface_t *_src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ cairo_composite_glyphs_info_t *info)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+finish_spans (void *abstract_renderer)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+span_renderer_init (cairo_abstract_span_renderer_t *_r,
+ const cairo_composite_rectangles_t *composite,
+ cairo_antialias_t antialias,
+ cairo_bool_t needs_clip)
+{
+ cairo_span_renderer_t *r = (cairo_span_renderer_t *)_r;
+ r->render_rows = spans;
+ r->finish = finish_spans;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+ cairo_int_status_t status)
+{
+}
+
+static const cairo_compositor_t *
+no_fallback_compositor_get (void)
+{
+ return &__cairo_no_compositor;
+}
+
+static cairo_int_status_t
+check_composite (const cairo_composite_rectangles_t *extents)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_compositor_t *
+no_traps_compositor_get (void)
+{
+ static cairo_traps_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_traps_compositor_init (&compositor,
+ no_fallback_compositor_get ());
+
+ compositor.acquire = acquire;
+ compositor.release = release;
+ compositor.set_clip_region = set_clip_region;
+ compositor.pattern_to_surface = pattern_to_surface;
+ compositor.draw_image_boxes = draw_image_boxes;
+ //compositor.copy_boxes = copy_boxes;
+ compositor.fill_boxes = fill_boxes;
+ compositor.check_composite = check_composite;
+ compositor.composite = composite;
+ compositor.lerp = lerp;
+ //FIXME:
+ compositor.lerp_color_glyph = lerp;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ //compositor.check_composite_traps = check_composite_traps;
+ compositor.composite_traps = composite_traps;
+ compositor.check_composite_glyphs = check_composite_glyphs;
+ compositor.composite_glyphs = composite_glyphs;
+ }
+
+ return &compositor.base;
+}
+
+static const cairo_compositor_t *
+no_spans_compositor_get (void)
+{
+ static cairo_spans_compositor_t compositor;
+
+ if (compositor.base.delegate == NULL) {
+ _cairo_spans_compositor_init (&compositor,
+ no_traps_compositor_get());
+
+ //compositor.acquire = acquire;
+ //compositor.release = release;
+ compositor.fill_boxes = fill_boxes;
+ //compositor.check_composite_boxes = check_composite_boxes;
+ compositor.composite_boxes = composite_boxes;
+ //compositor.check_span_renderer = check_span_renderer;
+ compositor.renderer_init = span_renderer_init;
+ compositor.renderer_fini = span_renderer_fini;
+ }
+
+ return &compositor.base;
+}
+
+cairo_surface_t *
+_cairo_test_no_fallback_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (no_fallback_compositor_get(),
+ content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_no_traps_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (no_traps_compositor_get(),
+ content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_no_spans_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ return test_compositor_surface_create (no_spans_compositor_get(),
+ content, width, height);
+}
diff --git a/src/test-null-compositor-surface.h b/src/test-null-compositor-surface.h
new file mode 100755
index 000000000..52d864b28
--- /dev/null
+++ b/src/test-null-compositor-surface.h
@@ -0,0 +1,60 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef TEST_NULL_COMPOSITOR_SURFACE_H
+#define TEST_NULL_COMPOSITOR_SURFACE_H
+
+#include "cairo.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+_cairo_test_no_fallback_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+cairo_surface_t *
+_cairo_test_no_traps_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+cairo_surface_t *
+_cairo_test_no_spans_compositor_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_NULL_COMPOSITOR_SURFACE_H */
diff --git a/src/test-paginated-surface.c b/src/test-paginated-surface.c
new file mode 100755
index 000000000..0a7c79b37
--- /dev/null
+++ b/src/test-paginated-surface.c
@@ -0,0 +1,286 @@
+/* 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-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef struct _test_paginated_surface {
+ cairo_surface_t base;
+ cairo_surface_t *target;
+ cairo_paginated_mode_t paginated_mode;
+} test_paginated_surface_t;
+
+static const cairo_surface_backend_t test_paginated_surface_backend;
+static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend;
+
+cairo_surface_t *
+_cairo_test_paginated_surface_create (cairo_surface_t *target)
+{
+ cairo_status_t status;
+ cairo_surface_t *paginated;
+ test_paginated_surface_t *surface;
+
+ status = cairo_surface_status (target);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ surface = 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,
+ const cairo_clip_t *clip)
+{
+ test_paginated_surface_t *surface = abstract_surface;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_surface_paint (surface->target, op, source, clip);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ test_paginated_surface_t *surface = abstract_surface;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_surface_mask (surface->target,
+ op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ test_paginated_surface_t *surface = abstract_surface;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_surface_stroke (surface->target, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ test_paginated_surface_t *surface = abstract_surface;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_surface_fill (surface->target, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_bool_t
+_test_paginated_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ test_paginated_surface_t *surface = abstract_surface;
+
+ return cairo_surface_has_show_text_glyphs (surface->target);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ test_paginated_surface_t *surface = abstract_surface;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_surface_show_text_glyphs (surface->target, op, source,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
+}
+
+
+static 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,
+ _test_paginated_surface_finish,
+ _cairo_default_context_create,
+
+ /* Since we are a paginated user, we get to regard most of the
+ * surface backend interface as historical cruft and ignore it. */
+
+ NULL, /* create_similar */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _test_paginated_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ /* Here is the more "modern" section of the surface backend
+ * interface which is mostly just drawing functions */
+
+ _test_paginated_surface_paint,
+ _test_paginated_surface_mask,
+ _test_paginated_surface_stroke,
+ _test_paginated_surface_fill,
+ NULL, /* fill-stroke */
+ NULL, /* replaced by show_text_glyphs */
+ _test_paginated_surface_has_show_text_glyphs,
+ _test_paginated_surface_show_text_glyphs
+};
+
+static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend = {
+ NULL, /* start_page */
+ _test_paginated_surface_set_paginated_mode
+};
diff --git a/src/test-paginated-surface.h b/src/test-paginated-surface.h
new file mode 100755
index 000000000..2bd98aa5e
--- /dev/null
+++ b/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/src/win32/cairo-win32-debug.c b/src/win32/cairo-win32-debug.c
new file mode 100755
index 000000000..ff7aeaf1f
--- /dev/null
+++ b/src/win32/cairo-win32-debug.c
@@ -0,0 +1,87 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Owen Taylor <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-win32-private.h"
+
+#include <wchar.h>
+#include <windows.h>
+
+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);
+}
diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c
new file mode 100755
index 000000000..741e49e33
--- /dev/null
+++ b/src/win32/cairo-win32-device.c
@@ -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 © 2005 Red Hat, Inc.
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Owen Taylor <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-atomic-private.h"
+#include "cairo-device-private.h"
+#include "cairo-win32-private.h"
+
+#include <wchar.h>
+#include <windows.h>
+
+static cairo_device_t *__cairo_win32_device;
+
+static cairo_status_t
+_cairo_win32_device_flush (void *device)
+{
+ GdiFlush ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_device_finish (void *device)
+{
+}
+
+static void
+_cairo_win32_device_destroy (void *device)
+{
+ free (device);
+}
+
+static const cairo_device_backend_t _cairo_win32_device_backend = {
+ CAIRO_DEVICE_TYPE_WIN32,
+
+ NULL, NULL, /* lock, unlock */
+
+ _cairo_win32_device_flush,
+ _cairo_win32_device_finish,
+ _cairo_win32_device_destroy,
+};
+
+#if 0
+D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_IGNORE),
+ 0,
+ 0,
+ D2D1_RENDER_TARGET_USAGE_NONE,
+ D2D1_FEATURE_LEVEL_DEFAULT
+ );
+
+hr = m_pD2DFactory->CreateDCRenderTarget(&props, &device->d2d);
+#endif
+
+static cairo_bool_t is_win98 (void)
+{
+ OSVERSIONINFO os;
+
+ os.dwOSVersionInfoSize = sizeof (os);
+ GetVersionEx (&os);
+
+ return (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId &&
+ os.dwMajorVersion == 4 &&
+ os.dwMinorVersion == 10);
+}
+
+static void *
+_cairo_win32_device_get_alpha_blend (cairo_win32_device_t *device)
+{
+ void *func = NULL;
+
+ if (is_win98 ())
+ return NULL;
+
+ device->msimg32_dll = LoadLibraryW (L"msimg32");
+ if (device->msimg32_dll)
+ func = GetProcAddress (device->msimg32_dll, "AlphaBlend");
+
+ return func;
+}
+
+cairo_device_t *
+_cairo_win32_device_get (void)
+{
+ cairo_win32_device_t *device;
+
+ if (__cairo_win32_device)
+ return cairo_device_reference (__cairo_win32_device);
+
+ device = malloc (sizeof (*device));
+
+ _cairo_device_init (&device->base, &_cairo_win32_device_backend);
+
+ device->compositor = _cairo_win32_gdi_compositor_get ();
+
+ device->msimg32_dll = NULL;
+ device->alpha_blend = _cairo_win32_device_get_alpha_blend (device);
+
+ if (_cairo_atomic_ptr_cmpxchg ((void **)&__cairo_win32_device, NULL, device))
+ return cairo_device_reference(&device->base);
+
+ _cairo_win32_device_destroy (device);
+ return cairo_device_reference (__cairo_win32_device);
+}
+
+unsigned
+_cairo_win32_flags_for_dc (HDC dc)
+{
+ uint32_t flags = 0;
+ int cap;
+
+ 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;
+
+ 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.
+ */
+#if 0
+ flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+ flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
+#endif
+ } else {
+ cap = GetDeviceCaps(dc, SHADEBLENDCAPS);
+ if (cap != SB_NONE)
+ flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+ }
+
+ return flags;
+}
diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c
new file mode 100755
index 000000000..ccd285d7d
--- /dev/null
+++ b/src/win32/cairo-win32-display-surface.c
@@ -0,0 +1,1071 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Owen Taylor <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-compositor-private.h"
+#include "cairo-damage-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-inline.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <wchar.h>
+#include <windows.h>
+
+#if defined(__MINGW32__) && !defined(ETO_PDY)
+# define ETO_PDY 0x2000
+#endif
+
+#define PELS_72DPI ((LONG)(72. / 0.0254))
+
+/**
+ * SECTION:cairo-win32
+ * @Title: Win32 Surfaces
+ * @Short_Description: Microsoft Windows surface support
+ * @See_Also: #cairo_surface_t
+ *
+ * The Microsoft Windows surface is used to render cairo graphics to
+ * Microsoft Windows windows, bitmaps, and printing device contexts.
+ *
+ * The surface returned by cairo_win32_printing_surface_create() is of surface
+ * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface
+ * type.
+ *
+ * The surface returned by the other win32 constructors is of surface type
+ * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type.
+ **/
+
+/**
+ * CAIRO_HAS_WIN32_SURFACE:
+ *
+ * Defined if the Microsoft Windows surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.0
+ **/
+
+static const cairo_surface_backend_t cairo_win32_display_surface_backend;
+
+static cairo_status_t
+_create_dc_and_bitmap (cairo_win32_display_surface_t *surface,
+ HDC original_dc,
+ cairo_format_t format,
+ int width,
+ int height,
+ unsigned char **bits_out,
+ int *rowstride_out)
+{
+ cairo_status_t status;
+
+ BITMAPINFO *bitmap_info = NULL;
+ struct {
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[2];
+ } bmi_stack;
+ void *bits;
+
+ int num_palette = 0; /* Quiet GCC */
+ int i;
+
+ surface->win32.dc = NULL;
+ surface->bitmap = NULL;
+ surface->is_dib = FALSE;
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB30:
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ num_palette = 0;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ num_palette = 256;
+ break;
+
+ case CAIRO_FORMAT_A1:
+ num_palette = 2;
+ break;
+ }
+
+ if (num_palette > 2) {
+ bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER));
+ if (!bitmap_info)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else {
+ bitmap_info = (BITMAPINFO *)&bmi_stack;
+ }
+
+ bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
+ bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width;
+ bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */
+ bitmap_info->bmiHeader.biSizeImage = 0;
+ bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */
+ bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */
+ bitmap_info->bmiHeader.biPlanes = 1;
+
+ switch (format) {
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB30:
+ ASSERT_NOT_REACHED;
+ /* We can't create real RGB24 bitmaps because something seems to
+ * break if we do, especially if we don't set up an image
+ * fallback. It could be a bug with using a 24bpp pixman image
+ * (and creating one with masks). So treat them like 32bpp.
+ * Note: This causes problems when using BitBlt/AlphaBlend/etc!
+ * see end of file.
+ */
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_ARGB32:
+ bitmap_info->bmiHeader.biBitCount = 32;
+ bitmap_info->bmiHeader.biCompression = BI_RGB;
+ bitmap_info->bmiHeader.biClrUsed = 0; /* unused */
+ bitmap_info->bmiHeader.biClrImportant = 0;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ bitmap_info->bmiHeader.biBitCount = 8;
+ bitmap_info->bmiHeader.biCompression = BI_RGB;
+ bitmap_info->bmiHeader.biClrUsed = 256;
+ bitmap_info->bmiHeader.biClrImportant = 0;
+
+ for (i = 0; i < 256; i++) {
+ bitmap_info->bmiColors[i].rgbBlue = i;
+ bitmap_info->bmiColors[i].rgbGreen = i;
+ bitmap_info->bmiColors[i].rgbRed = i;
+ bitmap_info->bmiColors[i].rgbReserved = 0;
+ }
+ break;
+
+ case CAIRO_FORMAT_A1:
+ bitmap_info->bmiHeader.biBitCount = 1;
+ bitmap_info->bmiHeader.biCompression = BI_RGB;
+ bitmap_info->bmiHeader.biClrUsed = 2;
+ bitmap_info->bmiHeader.biClrImportant = 0;
+
+ for (i = 0; i < 2; i++) {
+ bitmap_info->bmiColors[i].rgbBlue = i * 255;
+ bitmap_info->bmiColors[i].rgbGreen = i * 255;
+ bitmap_info->bmiColors[i].rgbRed = i * 255;
+ bitmap_info->bmiColors[i].rgbReserved = 0;
+ }
+ break;
+ }
+
+ surface->win32.dc = CreateCompatibleDC (original_dc);
+ if (!surface->win32.dc)
+ goto FAIL;
+
+ surface->bitmap = CreateDIBSection (surface->win32.dc,
+ bitmap_info,
+ DIB_RGB_COLORS,
+ &bits,
+ NULL, 0);
+ if (!surface->bitmap)
+ goto FAIL;
+
+ surface->is_dib = TRUE;
+
+ GdiFlush();
+
+ surface->saved_dc_bitmap = SelectObject (surface->win32.dc,
+ surface->bitmap);
+ if (!surface->saved_dc_bitmap)
+ goto FAIL;
+
+ if (bitmap_info && num_palette > 2)
+ free (bitmap_info);
+
+ if (bits_out)
+ *bits_out = bits;
+
+ if (rowstride_out) {
+ /* Windows bitmaps are padded to 32-bit (dword) boundaries */
+ switch (format) {
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_RGB30:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ *rowstride_out = 4 * width;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ *rowstride_out = (width + 3) & ~3;
+ break;
+
+ case CAIRO_FORMAT_A1:
+ *rowstride_out = ((width + 31) & ~31) / 8;
+ break;
+ }
+ }
+
+ surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ FAIL:
+ status = _cairo_win32_print_gdi_error (__FUNCTION__);
+
+ if (bitmap_info && num_palette > 2)
+ free (bitmap_info);
+
+ if (surface->saved_dc_bitmap) {
+ SelectObject (surface->win32.dc, surface->saved_dc_bitmap);
+ surface->saved_dc_bitmap = NULL;
+ }
+
+ if (surface->bitmap) {
+ DeleteObject (surface->bitmap);
+ surface->bitmap = NULL;
+ }
+
+ if (surface->win32.dc) {
+ DeleteDC (surface->win32.dc);
+ surface->win32.dc = NULL;
+ }
+
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_win32_display_surface_create_for_dc (HDC original_dc,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+ cairo_device_t *device;
+ cairo_win32_display_surface_t *surface;
+ unsigned char *bits;
+ int rowstride;
+
+ surface = malloc (sizeof (*surface));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface->fallback = NULL;
+
+ status = _create_dc_and_bitmap (surface, original_dc, format,
+ width, height,
+ &bits, &rowstride);
+ if (status)
+ goto FAIL;
+
+ surface->image = cairo_image_surface_create_for_data (bits, format,
+ width, height, rowstride);
+ status = surface->image->status;
+ if (status)
+ goto FAIL;
+
+ _cairo_image_surface_set_parent (to_image_surface(surface->image),
+ &surface->win32.base);
+
+ surface->win32.format = format;
+
+ surface->win32.extents.x = 0;
+ surface->win32.extents.y = 0;
+ surface->win32.extents.width = width;
+ surface->win32.extents.height = height;
+
+ surface->initial_clip_rgn = NULL;
+ surface->had_simple_clip = FALSE;
+
+ device = _cairo_win32_device_get ();
+
+ _cairo_surface_init (&surface->win32.base,
+ &cairo_win32_display_surface_backend,
+ device,
+ _cairo_content_from_format (format));
+
+ cairo_device_destroy (device);
+
+ return &surface->win32.base;
+
+ FAIL:
+ if (surface->bitmap) {
+ SelectObject (surface->win32.dc, surface->saved_dc_bitmap);
+ DeleteObject (surface->bitmap);
+ DeleteDC (surface->win32.dc);
+ }
+ free (surface);
+
+ return _cairo_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_win32_display_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_win32_display_surface_t *src = abstract_src;
+ cairo_format_t format = _cairo_format_from_content (content);
+ cairo_surface_t *new_surf = NULL;
+
+ /* We force a DIB always if:
+ * - we need alpha; or
+ * - the parent is a DIB; or
+ * - the parent is for printing (because we don't care about the
+ * bit depth at that point)
+ *
+ * We also might end up with a DIB even if a DDB is requested if
+ * DDB creation failed due to out of memory.
+ */
+ if (!(src->is_dib || content & CAIRO_CONTENT_ALPHA)) {
+ /* try to create a ddb */
+ new_surf = cairo_win32_surface_create_with_ddb (src->win32.dc, CAIRO_FORMAT_RGB24, width, height);
+
+ if (new_surf->status)
+ new_surf = NULL;
+ }
+
+ if (new_surf == NULL) {
+ new_surf = _cairo_win32_display_surface_create_for_dc (src->win32.dc, format, width, height);
+ }
+
+ return new_surf;
+}
+
+static cairo_surface_t *
+_cairo_win32_display_surface_create_similar_image (void *abstract_other,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_win32_display_surface_t *surface = abstract_other;
+ cairo_image_surface_t *image;
+
+ surface = (cairo_win32_display_surface_t *)
+ _cairo_win32_display_surface_create_for_dc (surface->win32.dc,
+ format, width, height);
+ if (surface->win32.base.status)
+ return &surface->win32.base;
+
+ /* And clear in order to comply with our user API semantics */
+ image = (cairo_image_surface_t *) surface->image;
+ if (! image->base.is_clear) {
+ memset (image->data, 0, image->stride * height);
+ image->base.is_clear = TRUE;
+ }
+
+ return &image->base;
+}
+
+static cairo_status_t
+_cairo_win32_display_surface_finish (void *abstract_surface)
+{
+ cairo_win32_display_surface_t *surface = abstract_surface;
+
+ if (surface->image) {
+ /* Unhook ourselves first to avoid the double-unref from the image */
+ to_image_surface(surface->image)->parent = NULL;
+ cairo_surface_finish (surface->image);
+ cairo_surface_destroy (surface->image);
+ }
+
+ /* If we created the Bitmap and DC, destroy them */
+ if (surface->bitmap) {
+ SelectObject (surface->win32.dc, surface->saved_dc_bitmap);
+ DeleteObject (surface->bitmap);
+ DeleteDC (surface->win32.dc);
+ }
+
+ if (surface->initial_clip_rgn)
+ DeleteObject (surface->initial_clip_rgn);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_image_surface_t *
+_cairo_win32_display_surface_map_to_image (void *abstract_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_win32_display_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->win32.base.unique_id));
+
+ if (surface->image)
+ goto done;
+
+ if (surface->fallback == NULL) {
+ surface->fallback =
+ _cairo_win32_display_surface_create_for_dc (surface->win32.dc,
+ surface->win32.format,
+ surface->win32.extents.width,
+ surface->win32.extents.height);
+ if (unlikely (status = surface->fallback->status))
+ goto err;
+
+ if (!BitBlt (to_win32_surface(surface->fallback)->dc,
+ 0, 0,
+ surface->win32.extents.width,
+ surface->win32.extents.height,
+ surface->win32.dc,
+ 0, 0,
+ SRCCOPY)) {
+ status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+ goto err;
+ }
+ }
+
+ surface = to_win32_display_surface (surface->fallback);
+done:
+ GdiFlush();
+ return _cairo_surface_map_to_image (surface->image, extents);
+
+err:
+ cairo_surface_destroy (surface->fallback);
+ surface->fallback = NULL;
+
+ return _cairo_image_surface_create_in_error (status);
+}
+
+static cairo_int_status_t
+_cairo_win32_display_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ cairo_win32_display_surface_t *surface = abstract_surface;
+
+ /* Delay the download until the next flush, which means we also need
+ * to make sure our sources rare flushed.
+ */
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, to_win32_surface(surface)->base.unique_id));
+
+ if (surface->fallback) {
+ cairo_rectangle_int_t r;
+
+ r.x = image->base.device_transform_inverse.x0;
+ r.y = image->base.device_transform_inverse.y0;
+ r.width = image->width;
+ r.height = image->height;
+
+ TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n",
+ __FUNCTION__, r.x, r.y, r.width, r.height));
+ surface->fallback->damage =
+ _cairo_damage_add_rectangle (surface->fallback->damage, &r);
+ surface = to_win32_display_surface (surface->fallback);
+ }
+
+ return _cairo_surface_unmap_image (surface->image, image);
+}
+
+static cairo_status_t
+_cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_win32_display_surface_t *surface = abstract_surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->win32.base.unique_id));
+ if (surface->fallback == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->fallback->damage) {
+ cairo_win32_display_surface_t *fallback;
+ cairo_damage_t *damage;
+
+ damage = _cairo_damage_reduce (surface->fallback->damage);
+ surface->fallback->damage = NULL;
+
+ fallback = to_win32_display_surface (surface->fallback);
+ assert (fallback->image);
+
+ TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__,
+ damage->region ? cairo_region_num_rectangles (damage->region) : 0));
+
+ if (damage->status) {
+ if (!BitBlt (surface->win32.dc,
+ 0, 0,
+ surface->win32.extents.width,
+ surface->win32.extents.height,
+ fallback->win32.dc,
+ 0, 0,
+ SRCCOPY))
+ status = _cairo_win32_print_gdi_error (__FUNCTION__);
+ } else if (damage->region) {
+ int n = cairo_region_num_rectangles (damage->region), i;
+ for (i = 0; i < n; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (damage->region, i, &rect);
+ TRACE ((stderr, "%s: damage (%d,%d)x(%d,%d)\n", __FUNCTION__,
+ rect.x, rect.y,
+ rect.width, rect.height));
+ if (!BitBlt (surface->win32.dc,
+ rect.x, rect.y,
+ rect.width, rect.height,
+ fallback->win32.dc,
+ rect.x, rect.y,
+ SRCCOPY)) {
+ status = _cairo_win32_print_gdi_error (__FUNCTION__);
+ break;
+ }
+ }
+ }
+ _cairo_damage_destroy (damage);
+ } else {
+ cairo_surface_destroy (surface->fallback);
+ surface->fallback = NULL;
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_win32_display_surface_mark_dirty (void *abstract_surface,
+ int x, int y, int width, int height)
+{
+ _cairo_win32_display_surface_discard_fallback (abstract_surface);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_display_surface_t *surface)
+{
+ RECT rect;
+ int clipBoxType;
+ int gm;
+ XFORM saved_xform;
+
+ /* GetClipBox/GetClipRgn and friends interact badly with a world transform
+ * set. GetClipBox returns values in logical (transformed) coordinates;
+ * it's unclear what GetClipRgn returns, because the region is empty in the
+ * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates.
+ * Similarly, IntersectClipRect works in logical units, whereas SelectClipRgn
+ * works in device units.
+ *
+ * So, avoid the whole mess and get rid of the world transform
+ * while we store our initial data and when we restore initial coordinates.
+ *
+ * XXX we may need to modify x/y by the ViewportOrg or WindowOrg
+ * here in GM_COMPATIBLE; unclear.
+ */
+ gm = GetGraphicsMode (hdc);
+ if (gm == GM_ADVANCED) {
+ GetWorldTransform (hdc, &saved_xform);
+ ModifyWorldTransform (hdc, NULL, MWT_IDENTITY);
+ }
+
+ clipBoxType = GetClipBox (hdc, &rect);
+ if (clipBoxType == ERROR) {
+ _cairo_win32_print_gdi_error (__FUNCTION__);
+ SetGraphicsMode (hdc, gm);
+ /* XXX: Can we make a more reasonable guess at the error cause here? */
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+ }
+
+ surface->win32.extents.x = rect.left;
+ surface->win32.extents.y = rect.top;
+ surface->win32.extents.width = rect.right - rect.left;
+ surface->win32.extents.height = rect.bottom - rect.top;
+
+ surface->initial_clip_rgn = NULL;
+ surface->had_simple_clip = FALSE;
+
+ if (clipBoxType == COMPLEXREGION) {
+ surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0);
+ if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) {
+ DeleteObject(surface->initial_clip_rgn);
+ surface->initial_clip_rgn = NULL;
+ }
+ } else if (clipBoxType == SIMPLEREGION) {
+ surface->had_simple_clip = TRUE;
+ }
+
+ if (gm == GM_ADVANCED)
+ SetWorldTransform (hdc, &saved_xform);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface,
+ cairo_clip_t *clip)
+{
+ char stack[512];
+ cairo_rectangle_int_t extents;
+ int num_rects;
+ RGNDATA *data;
+ size_t data_size;
+ RECT *rects;
+ int i;
+ HRGN gdi_region;
+ cairo_status_t status;
+ cairo_region_t *region;
+
+ /* The semantics we want is that any clip set by cairo combines
+ * is intersected with the clip on device context that the
+ * surface was created for. To implement this, we need to
+ * save the original clip when first setting a clip on surface.
+ */
+
+ assert (_cairo_clip_is_region (clip));
+ region = _cairo_clip_get_region (clip);
+ if (region == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ cairo_region_get_extents (region, &extents);
+ num_rects = cairo_region_num_rectangles (region);
+
+ /* XXX see notes in _cairo_win32_save_initial_clip --
+ * this code will interact badly with a HDC which had an initial
+ * world transform -- we should probably manually transform the
+ * region rects, because SelectClipRgn takes device units, not
+ * logical units (unlike IntersectClipRect).
+ */
+
+ data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT);
+ if (data_size > sizeof (stack)) {
+ data = malloc (data_size);
+ if (!data)
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ } else
+ data = (RGNDATA *)stack;
+
+ data->rdh.dwSize = sizeof (RGNDATAHEADER);
+ data->rdh.iType = RDH_RECTANGLES;
+ data->rdh.nCount = num_rects;
+ data->rdh.nRgnSize = num_rects * sizeof (RECT);
+ data->rdh.rcBound.left = extents.x;
+ data->rdh.rcBound.top = extents.y;
+ data->rdh.rcBound.right = extents.x + extents.width;
+ data->rdh.rcBound.bottom = extents.y + extents.height;
+
+ rects = (RECT *)data->Buffer;
+ for (i = 0; i < num_rects; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (region, i, &rect);
+
+ rects[i].left = rect.x;
+ rects[i].top = rect.y;
+ rects[i].right = rect.x + rect.width;
+ rects[i].bottom = rect.y + rect.height;
+ }
+
+ gdi_region = ExtCreateRegion (NULL, data_size, data);
+ if ((char *)data != stack)
+ free (data);
+
+ if (!gdi_region)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* AND the new region into our DC */
+ status = CAIRO_STATUS_SUCCESS;
+ if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR)
+ status = _cairo_win32_print_gdi_error (__FUNCTION__);
+
+ DeleteObject (gdi_region);
+
+ return status;
+}
+
+void
+_cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface)
+{
+ XFORM saved_xform;
+ int gm = GetGraphicsMode (surface->win32.dc);
+ if (gm == GM_ADVANCED) {
+ GetWorldTransform (surface->win32.dc, &saved_xform);
+ ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY);
+ }
+
+ /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */
+ SelectClipRgn (surface->win32.dc, surface->initial_clip_rgn);
+
+ if (surface->had_simple_clip) {
+ /* then if we had a simple clip, intersect */
+ IntersectClipRect (surface->win32.dc,
+ surface->win32.extents.x,
+ surface->win32.extents.y,
+ surface->win32.extents.x + surface->win32.extents.width,
+ surface->win32.extents.y + surface->win32.extents.height);
+ }
+
+ if (gm == GM_ADVANCED)
+ SetWorldTransform (surface->win32.dc, &saved_xform);
+}
+
+void
+_cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface)
+{
+ if (surface->fallback) {
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, surface->win32.base.unique_id));
+
+ cairo_surface_destroy (surface->fallback);
+ surface->fallback = NULL;
+ }
+}
+
+static cairo_int_status_t
+_cairo_win32_display_surface_paint (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_device_t *device = to_win32_device_from_surface (surface);
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, to_win32_surface(surface)->base.unique_id));
+
+ if (clip == NULL &&
+ (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR))
+ _cairo_win32_display_surface_discard_fallback (surface);
+
+ return _cairo_compositor_paint (device->compositor,
+ surface, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_win32_display_surface_mask (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_device_t *device = to_win32_device_from_surface (surface);
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, to_win32_surface(surface)->base.unique_id));
+
+ if (clip == NULL && op == CAIRO_OPERATOR_SOURCE)
+ _cairo_win32_display_surface_discard_fallback (surface);
+
+ return _cairo_compositor_mask (device->compositor,
+ surface, op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_win32_display_surface_stroke (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_device_t *device = to_win32_device_from_surface (surface);
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, to_win32_surface(surface)->base.unique_id));
+
+ return _cairo_compositor_stroke (device->compositor, surface,
+ op, source, path,
+ style, ctm, ctm_inverse,
+ tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_win32_display_surface_fill (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_device_t *device = to_win32_device_from_surface (surface);
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, to_win32_surface(surface)->base.unique_id));
+
+ return _cairo_compositor_fill (device->compositor, surface,
+ op, source, path,
+ fill_rule, tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_win32_display_surface_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_device_t *device = to_win32_device_from_surface (surface);
+
+ TRACE ((stderr, "%s (surface=%d)\n",
+ __FUNCTION__, to_win32_surface(surface)->base.unique_id));
+
+ return _cairo_compositor_glyphs (device->compositor, surface,
+ op, source,
+ glyphs, num_glyphs, scaled_font,
+ clip);
+}
+
+static const cairo_surface_backend_t cairo_win32_display_surface_backend = {
+ CAIRO_SURFACE_TYPE_WIN32,
+ _cairo_win32_display_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_win32_display_surface_create_similar,
+ _cairo_win32_display_surface_create_similar_image,
+ _cairo_win32_display_surface_map_to_image,
+ _cairo_win32_display_surface_unmap_image,
+
+ _cairo_surface_default_source,
+ _cairo_surface_default_acquire_source_image,
+ _cairo_surface_default_release_source_image,
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_win32_surface_get_extents,
+ NULL, /* get_font_options */
+
+ _cairo_win32_display_surface_flush,
+ _cairo_win32_display_surface_mark_dirty,
+
+ _cairo_win32_display_surface_paint,
+ _cairo_win32_display_surface_mask,
+ _cairo_win32_display_surface_stroke,
+ _cairo_win32_display_surface_fill,
+ NULL, /* fill/stroke */
+ _cairo_win32_display_surface_glyphs,
+};
+
+/* Notes:
+ *
+ * Win32 alpha-understanding functions
+ *
+ * BitBlt - will copy full 32 bits from a 32bpp DIB to result
+ * (so it's safe to use for ARGB32->ARGB32 SOURCE blits)
+ * (but not safe going RGB24->ARGB32, if RGB24 is also represented
+ * as a 32bpp DIB, since the alpha isn't discarded!)
+ *
+ * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set,
+ * it will still copy over the src alpha, because the SCA value (255) will be
+ * multiplied by all the src components.
+ */
+
+/**
+ * cairo_win32_surface_create:
+ * @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
+ *
+ * Since: 1.0
+ **/
+cairo_surface_t *
+cairo_win32_surface_create (HDC hdc)
+{
+ cairo_win32_display_surface_t *surface;
+
+ cairo_format_t format;
+ cairo_status_t status;
+ cairo_device_t *device;
+
+ /* Assume that everything coming in as a HDC is RGB24 */
+ format = CAIRO_FORMAT_RGB24;
+
+ surface = malloc (sizeof (*surface));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ status = _cairo_win32_save_initial_clip (hdc, surface);
+ if (status) {
+ free (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ surface->image = NULL;
+ surface->fallback = NULL;
+ surface->win32.format = format;
+
+ surface->win32.dc = hdc;
+ surface->bitmap = NULL;
+ surface->is_dib = FALSE;
+ surface->saved_dc_bitmap = NULL;
+
+ surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc);
+
+ device = _cairo_win32_device_get ();
+
+ _cairo_surface_init (&surface->win32.base,
+ &cairo_win32_display_surface_backend,
+ device,
+ _cairo_content_from_format (format));
+
+ cairo_device_destroy (device);
+
+ return &surface->win32.base;
+}
+
+/**
+ * cairo_win32_surface_create_with_dib:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a device-independent-bitmap surface not associated with
+ * any particular existing surface or device context. The created
+ * bitmap will be uninitialized.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.2
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_dib (cairo_format_t format,
+ int width,
+ int height)
+{
+ if (! CAIRO_FORMAT_VALID (format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ return _cairo_win32_display_surface_create_for_dc (NULL, format, width, height);
+}
+
+/**
+ * cairo_win32_surface_create_with_ddb:
+ * @hdc: a DC compatible with the surface to create
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a device-dependent-bitmap surface not associated with
+ * any particular existing surface or device context. The created
+ * bitmap will be uninitialized.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.4
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_ddb (HDC hdc,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_win32_display_surface_t *new_surf;
+ HBITMAP ddb;
+ HDC screen_dc, ddb_dc;
+ HBITMAP saved_dc_bitmap;
+
+ 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_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ goto FINISH;
+ }
+
+ ddb = CreateCompatibleBitmap (hdc, width, height);
+ if (ddb == NULL) {
+ DeleteDC (ddb_dc);
+
+ /* Note that if an app actually does hit this out of memory
+ * condition, it's going to have lots of other issues, as
+ * video memory is probably exhausted. However, it can often
+ * continue using DIBs instead of DDBs.
+ */
+ new_surf = (cairo_win32_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ goto FINISH;
+ }
+
+ saved_dc_bitmap = SelectObject (ddb_dc, ddb);
+
+ new_surf = (cairo_win32_display_surface_t*) cairo_win32_surface_create (ddb_dc);
+ new_surf->bitmap = ddb;
+ new_surf->saved_dc_bitmap = saved_dc_bitmap;
+ new_surf->is_dib = FALSE;
+
+FINISH:
+ if (screen_dc)
+ ReleaseDC (NULL, screen_dc);
+
+ return &new_surf->win32.base;
+}
diff --git a/src/win32/cairo-win32-font.c b/src/win32/cairo-win32-font.c
new file mode 100755
index 000000000..a65d81b1a
--- /dev/null
+++ b/src/win32/cairo-win32-font.c
@@ -0,0 +1,2313 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ */
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as GetGlyphIndices */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairoint.h"
+
+#include "cairo-win32-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+#include <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.
+ *
+ * Since: 1.8
+ **/
+
+const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend;
+
+typedef struct {
+ cairo_scaled_font_t base;
+
+ LOGFONTW logfont;
+
+ BYTE quality;
+
+ /* We do drawing and metrics computation in a "logical space" which
+ * is similar to font space, except that it is scaled by a factor
+ * of the (desired font size) * (WIN32_FONT_LOGICAL_SCALE). The multiplication
+ * by WIN32_FONT_LOGICAL_SCALE allows for sub-pixel precision.
+ */
+ double logical_scale;
+
+ /* The size we should actually request the font at from Windows; differs
+ * from the logical_scale because it is quantized for orthogonal
+ * transformations
+ */
+ double logical_size;
+
+ /* Transformations from device <=> logical space
+ */
+ cairo_matrix_t logical_to_device;
+ cairo_matrix_t device_to_logical;
+
+ /* We special case combinations of 90-degree-rotations, scales and
+ * flips ... that is transformations that take the axes to the
+ * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y
+ * encode the 8 possibilities for orientation (4 rotation angles with
+ * and without a flip), and scale_x, scale_y the scale components.
+ */
+ cairo_bool_t preserve_axes;
+ cairo_bool_t swap_axes;
+ cairo_bool_t swap_x;
+ cairo_bool_t swap_y;
+ double x_scale;
+ double y_scale;
+
+ /* The size of the design unit of the font
+ */
+ int em_square;
+
+ HFONT scaled_hfont;
+ HFONT unscaled_hfont;
+
+ cairo_bool_t is_bitmap;
+ cairo_bool_t is_type1;
+ cairo_bool_t delete_scaled_hfont;
+ cairo_bool_t has_type1_notdef_index;
+ unsigned long type1_notdef_index;
+} cairo_win32_scaled_font_t;
+
+static cairo_status_t
+_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font);
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph);
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph);
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph);
+
+static void
+_cairo_win32_font_face_destroy (void *abstract_face);
+
+
+#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 */
+}
+
+static BYTE
+_get_system_quality (void)
+{
+ BOOL font_smoothing;
+ UINT smoothing_type;
+
+ if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
+ _cairo_win32_print_gdi_error ("_get_system_quality");
+ return DEFAULT_QUALITY;
+ }
+
+ if (font_smoothing) {
+ if (_have_cleartype_quality ()) {
+ if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE,
+ 0, &smoothing_type, 0)) {
+ _cairo_win32_print_gdi_error ("_get_system_quality");
+ return DEFAULT_QUALITY;
+ }
+
+ if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
+ return CLEARTYPE_QUALITY;
+ }
+
+ return ANTIALIASED_QUALITY;
+ } else {
+ return DEFAULT_QUALITY;
+ }
+}
+
+/* If face_hfont is non-%NULL then font_matrix must be a simple scale by some
+ * factor S, ctm must be the identity, logfont->lfHeight must be -S,
+ * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must
+ * all be 0, and face_hfont is the result of calling CreateFontIndirectW on
+ * logfont.
+ */
+static cairo_status_t
+_win32_scaled_font_create (LOGFONTW *logfont,
+ HFONT face_hfont,
+ cairo_font_face_t *font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **font_out)
+{
+ HDC hdc;
+ cairo_win32_scaled_font_t *f;
+ cairo_matrix_t scale;
+ cairo_status_t status;
+
+ hdc = _get_global_font_dc ();
+ if (hdc == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ f = 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 = _get_system_quality ();
+ else {
+ switch (options->antialias) {
+ case CAIRO_ANTIALIAS_NONE:
+ f->quality = NONANTIALIASED_QUALITY;
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_FAST:
+ case CAIRO_ANTIALIAS_GOOD:
+ f->quality = ANTIALIASED_QUALITY;
+ break;
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ case CAIRO_ANTIALIAS_BEST:
+ if (_have_cleartype_quality ())
+ f->quality = CLEARTYPE_QUALITY;
+ else
+ f->quality = ANTIALIASED_QUALITY;
+ break;
+ case CAIRO_ANTIALIAS_DEFAULT:
+ ASSERT_NOT_REACHED;
+ }
+ }
+
+ f->em_square = 0;
+ f->scaled_hfont = NULL;
+ f->unscaled_hfont = NULL;
+ f->has_type1_notdef_index = FALSE;
+
+ if (f->quality == logfont->lfQuality ||
+ (logfont->lfQuality == DEFAULT_QUALITY &&
+ options->antialias == CAIRO_ANTIALIAS_DEFAULT)) {
+ /* If face_hfont is non-NULL, then we can use it to avoid creating our
+ * own --- because the constraints on face_hfont mentioned above
+ * guarantee it was created in exactly the same way that
+ * _win32_scaled_font_get_scaled_hfont would create it.
+ */
+ f->scaled_hfont = face_hfont;
+ }
+ /* don't delete the hfont if we're using the one passed in to us */
+ f->delete_scaled_hfont = !f->scaled_hfont;
+
+ cairo_matrix_multiply (&scale, font_matrix, ctm);
+ status = _compute_transform (f, &scale);
+ if (status)
+ goto FAIL;
+
+ status = _cairo_scaled_font_init (&f->base, font_face,
+ font_matrix, ctm, options,
+ &_cairo_win32_scaled_font_backend);
+ if (status)
+ goto FAIL;
+
+ status = _cairo_win32_scaled_font_set_metrics (f);
+ if (status) {
+ _cairo_scaled_font_fini (&f->base);
+ goto FAIL;
+ }
+
+ *font_out = &f->base;
+ return CAIRO_STATUS_SUCCESS;
+
+ FAIL:
+ free (f);
+ return status;
+}
+
+static cairo_status_t
+_win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font,
+ HDC hdc)
+{
+ XFORM xform;
+
+ _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform);
+
+ if (!SetWorldTransform (hdc, &xform))
+ return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_win32_scaled_font_set_identity_transform (HDC hdc)
+{
+ if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY))
+ return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font,
+ HFONT *hfont_out)
+{
+ if (!scaled_font->scaled_hfont) {
+ LOGFONTW logfont = scaled_font->logfont;
+ logfont.lfHeight = -scaled_font->logical_size;
+ logfont.lfWidth = 0;
+ logfont.lfEscapement = 0;
+ logfont.lfOrientation = 0;
+ logfont.lfQuality = scaled_font->quality;
+
+ scaled_font->scaled_hfont = CreateFontIndirectW (&logfont);
+ if (!scaled_font->scaled_hfont)
+ return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont");
+ }
+
+ *hfont_out = scaled_font->scaled_hfont;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font,
+ HDC hdc,
+ HFONT *hfont_out)
+{
+ if (scaled_font->unscaled_hfont == NULL) {
+ OUTLINETEXTMETRIC *otm;
+ unsigned int otm_size;
+ HFONT scaled_hfont;
+ LOGFONTW logfont;
+ cairo_status_t status;
+
+ status = _win32_scaled_font_get_scaled_hfont (scaled_font,
+ &scaled_hfont);
+ if (status)
+ return status;
+
+ if (! SelectObject (hdc, scaled_hfont))
+ return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject");
+
+ otm_size = GetOutlineTextMetrics (hdc, 0, NULL);
+ if (! otm_size)
+ return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics");
+
+ otm = malloc (otm_size);
+ if (otm == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (! GetOutlineTextMetrics (hdc, otm_size, otm)) {
+ status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics");
+ free (otm);
+ return status;
+ }
+
+ scaled_font->em_square = otm->otmEMSquare;
+ free (otm);
+
+ logfont = scaled_font->logfont;
+ logfont.lfHeight = -scaled_font->em_square;
+ logfont.lfWidth = 0;
+ logfont.lfEscapement = 0;
+ logfont.lfOrientation = 0;
+ logfont.lfQuality = scaled_font->quality;
+
+ scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont);
+ if (! scaled_font->unscaled_hfont)
+ return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect");
+ }
+
+ *hfont_out = scaled_font->unscaled_hfont;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font,
+ HDC hdc)
+{
+ cairo_status_t status;
+ HFONT hfont;
+ HFONT old_hfont = NULL;
+
+ status = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc, &hfont);
+ if (status)
+ return status;
+
+ old_hfont = SelectObject (hdc, hfont);
+ if (!old_hfont)
+ return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font");
+
+ status = _win32_scaled_font_set_identity_transform (hdc);
+ if (status) {
+ SelectObject (hdc, old_hfont);
+ return status;
+ }
+
+ SetMapMode (hdc, MM_TEXT);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font)
+{
+ cairo_win32_scaled_font_t *win32_scaled_font;
+
+ win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font;
+
+ return win32_scaled_font->is_type1;
+}
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font)
+{
+ cairo_win32_scaled_font_t *win32_scaled_font;
+
+ win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font;
+
+ return win32_scaled_font->is_bitmap;
+}
+
+static void
+_cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font)
+{
+}
+
+/* implement the font backend interface */
+
+static cairo_status_t
+_cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
+ cairo_font_face_t **font_face)
+{
+ LOGFONTW logfont;
+ uint16_t *face_name;
+ int face_name_len;
+ cairo_status_t status;
+
+ status = _cairo_utf8_to_utf16 (toy_face->family, -1,
+ &face_name, &face_name_len);
+ if (status)
+ return status;
+
+ if (face_name_len > LF_FACESIZE - 1)
+ face_name_len = LF_FACESIZE - 1;
+
+ memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len);
+ logfont.lfFaceName[face_name_len] = 0;
+ free (face_name);
+
+ logfont.lfHeight = 0; /* filled in later */
+ logfont.lfWidth = 0; /* filled in later */
+ logfont.lfEscapement = 0; /* filled in later */
+ logfont.lfOrientation = 0; /* filled in later */
+
+ switch (toy_face->weight) {
+ case CAIRO_FONT_WEIGHT_NORMAL:
+ default:
+ logfont.lfWeight = FW_NORMAL;
+ break;
+ case CAIRO_FONT_WEIGHT_BOLD:
+ logfont.lfWeight = FW_BOLD;
+ break;
+ }
+
+ switch (toy_face->slant) {
+ case CAIRO_FONT_SLANT_NORMAL:
+ default:
+ logfont.lfItalic = FALSE;
+ break;
+ case CAIRO_FONT_SLANT_ITALIC:
+ case CAIRO_FONT_SLANT_OBLIQUE:
+ logfont.lfItalic = TRUE;
+ break;
+ }
+
+ logfont.lfUnderline = FALSE;
+ logfont.lfStrikeOut = FALSE;
+ /* The docs for LOGFONT discourage using this, since the
+ * interpretation is locale-specific, but it's not clear what
+ * would be a better alternative.
+ */
+ logfont.lfCharSet = DEFAULT_CHARSET;
+ logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */
+ logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+ *font_face = cairo_win32_font_face_create_for_logfontw (&logfont);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_scaled_font_fini (void *abstract_font)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+
+ if (scaled_font == NULL)
+ return;
+
+ if (scaled_font->scaled_hfont && scaled_font->delete_scaled_hfont)
+ DeleteObject (scaled_font->scaled_hfont);
+
+ if (scaled_font->unscaled_hfont)
+ DeleteObject (scaled_font->unscaled_hfont);
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_type1_text_to_glyphs (cairo_win32_scaled_font_t *scaled_font,
+ double x,
+ double y,
+ const char *utf8,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs)
+{
+ uint16_t *utf16;
+ int n16;
+ int i;
+ WORD *glyph_indices = NULL;
+ cairo_status_t status;
+ double x_pos, y_pos;
+ HDC hdc = NULL;
+ cairo_matrix_t mat;
+
+ status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16);
+ if (status)
+ return status;
+
+ glyph_indices = _cairo_malloc_ab (n16 + 1, sizeof (WORD));
+ if (!glyph_indices) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL1;
+ }
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+ if (status)
+ goto FAIL2;
+
+ if (GetGlyphIndicesW (hdc, utf16, n16, glyph_indices, 0) == GDI_ERROR) {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_type1_text_to_glyphs:GetGlyphIndicesW");
+ goto FAIL3;
+ }
+
+ *num_glyphs = n16;
+ *glyphs = _cairo_malloc_ab (n16, sizeof (cairo_glyph_t));
+ if (!*glyphs) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL3;
+ }
+
+ x_pos = x;
+ y_pos = y;
+
+ mat = scaled_font->base.ctm;
+ status = cairo_matrix_invert (&mat);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _cairo_scaled_font_freeze_cache (&scaled_font->base);
+
+ for (i = 0; i < n16; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ (*glyphs)[i].index = glyph_indices[i];
+ (*glyphs)[i].x = x_pos;
+ (*glyphs)[i].y = y_pos;
+
+ status = _cairo_scaled_glyph_lookup (&scaled_font->base,
+ glyph_indices[i],
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (status) {
+ free (*glyphs);
+ *glyphs = NULL;
+ break;
+ }
+
+ x = scaled_glyph->x_advance;
+ y = scaled_glyph->y_advance;
+ cairo_matrix_transform_distance (&mat, &x, &y);
+ x_pos += x;
+ y_pos += y;
+ }
+
+ _cairo_scaled_font_thaw_cache (&scaled_font->base);
+
+FAIL3:
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+FAIL2:
+ free (glyph_indices);
+FAIL1:
+ free (utf16);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_text_to_glyphs (void *abstract_font,
+ double x,
+ double y,
+ const char *utf8,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ uint16_t *utf16;
+ int n16;
+ GCP_RESULTSW gcp_results;
+ unsigned int buffer_size, i;
+ WCHAR *glyph_indices = NULL;
+ int *dx = NULL;
+ cairo_status_t status;
+ double x_pos, y_pos;
+ double x_incr, y_incr;
+ HDC hdc = NULL;
+
+ /* GetCharacterPlacement() returns utf16 instead of glyph indices
+ * for Type 1 fonts. Use GetGlyphIndices for Type 1 fonts. */
+ if (scaled_font->is_type1)
+ return _cairo_win32_scaled_font_type1_text_to_glyphs (scaled_font,
+ x,
+ y,
+ utf8,
+ glyphs,
+ num_glyphs);
+
+ /* Compute a vector in user space along the baseline of length one logical space unit */
+ x_incr = 1;
+ y_incr = 0;
+ cairo_matrix_transform_distance (&scaled_font->base.font_matrix, &x_incr, &y_incr);
+ x_incr /= scaled_font->logical_scale;
+ y_incr /= scaled_font->logical_scale;
+
+ status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16);
+ if (status)
+ return status;
+
+ gcp_results.lStructSize = sizeof (GCP_RESULTS);
+ gcp_results.lpOutString = NULL;
+ gcp_results.lpOrder = NULL;
+ gcp_results.lpCaretPos = NULL;
+ gcp_results.lpClass = NULL;
+
+ buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */
+ if (buffer_size > INT_MAX) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL1;
+ }
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+ if (status)
+ goto FAIL1;
+
+ while (TRUE) {
+ free (glyph_indices);
+ glyph_indices = NULL;
+
+ free (dx);
+ dx = NULL;
+
+ glyph_indices = _cairo_malloc_ab (buffer_size, sizeof (WCHAR));
+ dx = _cairo_malloc_ab (buffer_size, sizeof (int));
+ if (!glyph_indices || !dx) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL2;
+ }
+
+ gcp_results.nGlyphs = buffer_size;
+ gcp_results.lpDx = dx;
+ gcp_results.lpGlyphs = glyph_indices;
+
+ if (!GetCharacterPlacementW (hdc, utf16, n16,
+ 0,
+ &gcp_results,
+ GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs");
+ goto FAIL2;
+ }
+
+ if (gcp_results.lpDx && gcp_results.lpGlyphs)
+ break;
+
+ /* Too small a buffer, try again */
+
+ buffer_size += buffer_size / 2;
+ if (buffer_size > INT_MAX) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL2;
+ }
+ }
+
+ *num_glyphs = gcp_results.nGlyphs;
+ *glyphs = _cairo_malloc_ab (gcp_results.nGlyphs, sizeof (cairo_glyph_t));
+ if (!*glyphs) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL2;
+ }
+
+ x_pos = x;
+ y_pos = y;
+
+ for (i = 0; i < gcp_results.nGlyphs; i++) {
+ (*glyphs)[i].index = glyph_indices[i];
+ (*glyphs)[i].x = x_pos ;
+ (*glyphs)[i].y = y_pos;
+
+ x_pos += x_incr * dx[i];
+ y_pos += y_incr * dx[i];
+ }
+
+ FAIL2:
+ free (glyph_indices);
+ free (dx);
+
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ FAIL1:
+ free (utf16);
+
+ return status;
+}
+
+static unsigned long
+_cairo_win32_scaled_font_ucs4_to_index (void *abstract_font,
+ uint32_t ucs4)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ wchar_t unicode[2];
+ WORD glyph_index;
+ HDC hdc = NULL;
+ cairo_status_t status;
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+ if (status)
+ return 0;
+
+ unicode[0] = ucs4;
+ unicode[1] = 0;
+ if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) {
+ _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW");
+ glyph_index = 0;
+ }
+
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ return glyph_index;
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font)
+{
+ cairo_status_t status;
+ cairo_font_extents_t extents;
+
+ TEXTMETRIC metrics;
+ 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 > 0 && scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) {
+ /* The bounding box reported by Windows supposedly contains the glyph's "black" area;
+ * however, antialiasing (especially with ClearType) means that the actual image that
+ * needs to be rendered may "bleed" into the adjacent pixels, mainly on the right side.
+ * To avoid clipping the glyphs when drawn by _cairo_surface_fallback_show_glyphs,
+ * for example, or other code that uses glyph extents to determine the area to update,
+ * we add a pixel of "slop" to left side of the nominal "black" area returned by GDI,
+ * and two pixels to the right (as tests show some glyphs bleed into this column).
+ */
+ metrics.gmptGlyphOrigin.x -= 1;
+ metrics.gmBlackBoxX += 3;
+ }
+ }
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ if (scaled_font->swap_axes) {
+ extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale;
+ extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale;
+ extents.width = metrics.gmBlackBoxY / scaled_font->y_scale;
+ extents.height = metrics.gmBlackBoxX / scaled_font->x_scale;
+ extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale;
+ extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale;
+ } else {
+ extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale;
+ extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale;
+ extents.width = metrics.gmBlackBoxX / scaled_font->x_scale;
+ extents.height = metrics.gmBlackBoxY / scaled_font->y_scale;
+ extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale;
+ extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale;
+ }
+
+ if (scaled_font->swap_x) {
+ extents.x_bearing = (- extents.x_bearing - extents.width);
+ extents.x_advance = - extents.x_advance;
+ }
+
+ if (scaled_font->swap_y) {
+ extents.y_bearing = (- extents.y_bearing - extents.height);
+ extents.y_advance = - extents.y_advance;
+ }
+ } else {
+ /* For all other transformations, we use the design metrics
+ * of the font.
+ */
+ status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc);
+ if (status)
+ return status;
+
+ if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
+ GGO_METRICS | GGO_GLYPH_INDEX,
+ &metrics, 0, NULL, &matrix) == GDI_ERROR) {
+ memset (&metrics, 0, sizeof (GLYPHMETRICS));
+ }
+ _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base);
+
+ extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square;
+ extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square;
+ extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square;
+ extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square;
+ extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square;
+ extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square;
+ }
+
+ _cairo_scaled_glyph_set_metrics (scaled_glyph,
+ &scaled_font->base,
+ &extents);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Not currently used code, but may be useful in the future if we add
+ * back the capability to the scaled font backend interface to get the
+ * actual device space bbox rather than computing it from the
+ * font-space metrics.
+ */
+#if 0
+static cairo_status_t
+_cairo_win32_scaled_font_glyph_bbox (void *abstract_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_box_t *bbox)
+{
+ static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+
+ if (num_glyphs > 0) {
+ HDC hdc;
+ GLYPHMETRICS metrics;
+ cairo_status_t status;
+ int i;
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+ if (status)
+ return status;
+
+ for (i = 0; i < num_glyphs; i++) {
+ int x = _cairo_lround (glyphs[i].x);
+ int y = _cairo_lround (glyphs[i].y);
+
+ GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX,
+ &metrics, 0, NULL, &matrix);
+
+ if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x)
+ x1 = x + metrics.gmptGlyphOrigin.x;
+ if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y)
+ y1 = y - metrics.gmptGlyphOrigin.y;
+ if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX)
+ x2 = x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX;
+ if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY)
+ y2 = y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY;
+ }
+
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+ }
+
+ bbox->p1.x = _cairo_fixed_from_int (x1);
+ bbox->p1.y = _cairo_fixed_from_int (y1);
+ bbox->p2.x = _cairo_fixed_from_int (x2);
+ bbox->p2.y = _cairo_fixed_from_int (y2);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+typedef struct {
+ cairo_win32_scaled_font_t *scaled_font;
+ HDC hdc;
+
+ cairo_array_t glyphs;
+ cairo_array_t dx;
+
+ int start_x;
+ int last_x;
+ int last_y;
+} cairo_glyph_state_t;
+
+static void
+_start_glyphs (cairo_glyph_state_t *state,
+ cairo_win32_scaled_font_t *scaled_font,
+ HDC hdc)
+{
+ state->hdc = hdc;
+ state->scaled_font = scaled_font;
+
+ _cairo_array_init (&state->glyphs, sizeof (WCHAR));
+ _cairo_array_init (&state->dx, sizeof (int));
+}
+
+static cairo_status_t
+_flush_glyphs (cairo_glyph_state_t *state)
+{
+ cairo_status_t status;
+ int dx = 0;
+ WCHAR * elements;
+ int * dx_elements;
+
+ status = _cairo_array_append (&state->dx, &dx);
+ if (status)
+ return status;
+
+ elements = _cairo_array_index (&state->glyphs, 0);
+ dx_elements = _cairo_array_index (&state->dx, 0);
+ if (!ExtTextOutW (state->hdc,
+ state->start_x, state->last_y,
+ ETO_GLYPH_INDEX,
+ NULL,
+ elements,
+ state->glyphs.num_elements,
+ dx_elements)) {
+ return _cairo_win32_print_gdi_error ("_flush_glyphs");
+ }
+
+ _cairo_array_truncate (&state->glyphs, 0);
+ _cairo_array_truncate (&state->dx, 0);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_add_glyph (cairo_glyph_state_t *state,
+ unsigned long index,
+ double device_x,
+ double device_y)
+{
+ cairo_status_t status;
+ double user_x = device_x;
+ double user_y = device_y;
+ WCHAR glyph_index = index;
+ int logical_x, logical_y;
+
+ cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y);
+
+ logical_x = _cairo_lround (user_x);
+ logical_y = _cairo_lround (user_y);
+
+ if (state->glyphs.num_elements > 0) {
+ int dx;
+
+ if (logical_y != state->last_y) {
+ status = _flush_glyphs (state);
+ if (status)
+ return status;
+ state->start_x = logical_x;
+ } else {
+ dx = logical_x - state->last_x;
+ status = _cairo_array_append (&state->dx, &dx);
+ if (status)
+ return status;
+ }
+ } else {
+ state->start_x = logical_x;
+ }
+
+ state->last_x = logical_x;
+ state->last_y = logical_y;
+
+ status = _cairo_array_append (&state->glyphs, &glyph_index);
+ if (status)
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_finish_glyphs (cairo_glyph_state_t *state)
+{
+ cairo_status_t status;
+
+ status = _flush_glyphs (state);
+
+ _cairo_array_fini (&state->glyphs);
+ _cairo_array_fini (&state->dx);
+
+ return status;
+}
+
+static cairo_status_t
+_draw_glyphs_on_surface (cairo_win32_surface_t *surface,
+ cairo_win32_scaled_font_t *scaled_font,
+ COLORREF color,
+ int x_offset,
+ int y_offset,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ cairo_glyph_state_t state;
+ cairo_status_t status, status2;
+ int i;
+
+ if (!SaveDC (surface->dc))
+ return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC");
+
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc);
+ if (status)
+ goto FAIL1;
+
+ SetTextColor (surface->dc, color);
+ SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT);
+ SetBkMode (surface->dc, TRANSPARENT);
+
+ _start_glyphs (&state, scaled_font, surface->dc);
+
+ for (i = 0; i < num_glyphs; i++) {
+ status = _add_glyph (&state, glyphs[i].index,
+ glyphs[i].x - x_offset, glyphs[i].y - y_offset);
+ if (status)
+ goto FAIL2;
+ }
+
+ FAIL2:
+ status2 = _finish_glyphs (&state);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+ FAIL1:
+ RestoreDC (surface->dc, -1);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_glyph_init (void *abstract_font,
+ cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_glyph_info_t info)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ cairo_status_t status;
+
+ if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) {
+ status = _cairo_win32_scaled_font_init_glyph_metrics (scaled_font, scaled_glyph);
+ if (status)
+ return status;
+ }
+
+ if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) {
+ status = _cairo_win32_scaled_font_init_glyph_surface (scaled_font, scaled_glyph);
+ if (status)
+ return status;
+ }
+
+ if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) {
+ status = _cairo_win32_scaled_font_init_glyph_path (scaled_font, scaled_glyph);
+ if (status)
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_load_truetype_table (void *abstract_font,
+ unsigned long tag,
+ long offset,
+ unsigned char *buffer,
+ unsigned long *length)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ HDC hdc;
+ cairo_status_t status;
+ DWORD ret;
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ tag = (tag&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;
+
+ ret = GetFontData (hdc, tag, offset, buffer, *length);
+ if (ret == GDI_ERROR || (buffer && ret != *length))
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ else
+ *length = ret;
+
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font,
+ unsigned long index,
+ uint32_t *ucs4)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ GLYPHSET *glyph_set;
+ uint16_t *utf16 = NULL;
+ WORD *glyph_indices = NULL;
+ HDC hdc = NULL;
+ int res;
+ unsigned int i, j, num_glyphs;
+ cairo_status_t status;
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+ if (status)
+ return status;
+
+ res = GetFontUnicodeRanges(hdc, NULL);
+ if (res == 0) {
+ status = _cairo_win32_print_gdi_error (
+ "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges");
+ goto exit1;
+ }
+
+ glyph_set = malloc (res);
+ if (glyph_set == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto exit1;
+ }
+
+ res = GetFontUnicodeRanges(hdc, glyph_set);
+ if (res == 0) {
+ status = _cairo_win32_print_gdi_error (
+ "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges");
+ goto exit1;
+ }
+
+ *ucs4 = (uint32_t) -1;
+ for (i = 0; i < glyph_set->cRanges; i++) {
+ num_glyphs = glyph_set->ranges[i].cGlyphs;
+
+ utf16 = _cairo_malloc_ab (num_glyphs + 1, sizeof (uint16_t));
+ if (utf16 == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto exit1;
+ }
+
+ glyph_indices = _cairo_malloc_ab (num_glyphs + 1, sizeof (WORD));
+ if (glyph_indices == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto exit2;
+ }
+
+ for (j = 0; j < num_glyphs; j++)
+ utf16[j] = glyph_set->ranges[i].wcLow + j;
+ utf16[j] = 0;
+
+ if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) {
+ status = _cairo_win32_print_gdi_error (
+ "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW");
+ goto exit2;
+ }
+
+ for (j = 0; j < num_glyphs; j++) {
+ if (glyph_indices[j] == index) {
+ *ucs4 = utf16[j];
+ goto exit2;
+ }
+ }
+
+ free (glyph_indices);
+ glyph_indices = NULL;
+ free (utf16);
+ utf16 = NULL;
+ }
+
+exit2:
+ free (glyph_indices);
+ free (utf16);
+ free (glyph_set);
+exit1:
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ return status;
+}
+
+static cairo_bool_t
+_cairo_win32_scaled_font_is_synthetic (void *abstract_font)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ cairo_status_t status;
+ int weight;
+ cairo_bool_t bold;
+ cairo_bool_t italic;
+
+ status = _cairo_truetype_get_style (&scaled_font->base,
+ &weight,
+ &bold,
+ &italic);
+ /* If this doesn't work assume it is not synthetic to avoid
+ * unnecessary subsetting fallbacks. */
+ if (status != CAIRO_STATUS_SUCCESS)
+ return FALSE;
+
+ if (scaled_font->logfont.lfWeight != weight ||
+ scaled_font->logfont.lfItalic != italic)
+ return TRUE;
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_index_to_glyph_name (void *abstract_font,
+ char **glyph_names,
+ int num_glyph_names,
+ unsigned long glyph_index,
+ unsigned long *glyph_array_index)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ int i;
+
+ /* Windows puts .notdef at index 0 then numbers the remaining
+ * glyphs starting from 1 in the order they appear in the font. */
+
+ /* Find the position of .notdef in the list of glyph names. We
+ * only need to do this once per scaled font. */
+ if (! scaled_font->has_type1_notdef_index) {
+ for (i = 0; i < num_glyph_names; i++) {
+ if (strcmp (glyph_names[i], ".notdef") == 0) {
+ scaled_font->type1_notdef_index = i;
+ scaled_font->has_type1_notdef_index = TRUE;
+ break;
+ }
+ }
+ if (! scaled_font->has_type1_notdef_index)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* Once we know the position of .notdef the position of any glyph
+ * in the font can easily be obtained. */
+ if (glyph_index == 0)
+ *glyph_array_index = scaled_font->type1_notdef_index;
+ else if (glyph_index <= scaled_font->type1_notdef_index)
+ *glyph_array_index = glyph_index - 1;
+ else if (glyph_index < (unsigned long)num_glyph_names)
+ *glyph_array_index = glyph_index;
+ else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_load_type1_data (void *abstract_font,
+ long offset,
+ unsigned char *buffer,
+ unsigned long *length)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+
+ if (! scaled_font->is_type1)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Using the tag 0 retrieves the entire font file. This works with
+ * Type 1 fonts as well as TTF/OTF fonts. */
+ return _cairo_win32_scaled_font_load_truetype_table (scaled_font,
+ 0,
+ offset,
+ buffer,
+ length);
+}
+
+static cairo_surface_t *
+_compute_mask (cairo_surface_t *surface,
+ int quality)
+{
+ cairo_image_surface_t *glyph;
+ cairo_image_surface_t *mask;
+ int i, j;
+
+ glyph = (cairo_image_surface_t *)cairo_surface_map_to_image (surface, NULL);
+ if (unlikely (glyph->base.status))
+ return &glyph->base;
+
+ if (quality == CLEARTYPE_QUALITY) {
+ /* Duplicate the green channel of a 4-channel mask into the
+ * alpha channel, then invert the whole mask.
+ */
+ mask = (cairo_image_surface_t *)
+ cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ glyph->width, glyph->height);
+ if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
+ for (i = 0; i < glyph->height; i++) {
+ uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride);
+ uint32_t *q = (uint32_t *) (mask->data + i * mask->stride);
+
+ for (j = 0; j < glyph->width; j++) {
+ *q++ = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16));
+ p++;
+ }
+ }
+ }
+ } else {
+ /* Compute an alpha-mask from a using the green channel of a
+ * (presumed monochrome) RGB24 image.
+ */
+ mask = (cairo_image_surface_t *)
+ cairo_image_surface_create (CAIRO_FORMAT_A8,
+ glyph->width, glyph->height);
+ if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
+ for (i = 0; i < glyph->height; i++) {
+ uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride);
+ uint8_t *q = (uint8_t *) (mask->data + i * mask->stride);
+
+ for (j = 0; j < glyph->width; j++)
+ *q++ = 255 - ((*p++ & 0x0000ff00) >> 8);
+ }
+ }
+ }
+
+ cairo_surface_unmap_image (surface, &glyph->base);
+ return &mask->base;
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_status_t status;
+ cairo_glyph_t glyph;
+ cairo_surface_t *surface;
+ cairo_surface_t *image;
+ int width, height;
+ int x1, y1, x2, y2;
+
+ x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+ y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+ x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
+ y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
+ width = x2 - x1;
+ height = y2 - y1;
+
+ surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24,
+ width, height);
+ status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE,
+ &_cairo_pattern_white.base, NULL);
+ if (status)
+ goto FAIL;
+
+ glyph.index = _cairo_scaled_glyph_index (scaled_glyph);
+ glyph.x = -x1;
+ glyph.y = -y1;
+ status = _draw_glyphs_on_surface (to_win32_surface (surface),
+ scaled_font, RGB(0,0,0),
+ 0, 0, &glyph, 1);
+ if (status)
+ goto FAIL;
+
+ image = _compute_mask (surface, scaled_font->quality);
+ status = image->status;
+ if (status)
+ goto FAIL;
+
+ cairo_surface_set_device_offset (image, -x1, -y1);
+ _cairo_scaled_glyph_set_surface (scaled_glyph,
+ &scaled_font->base,
+ (cairo_image_surface_t *) image);
+
+ FAIL:
+ cairo_surface_destroy (surface);
+
+ return status;
+}
+
+static void
+_cairo_win32_transform_FIXED_to_fixed (cairo_matrix_t *matrix,
+ FIXED Fx, FIXED Fy,
+ cairo_fixed_t *fx, cairo_fixed_t *fy)
+{
+ double x = Fx.value + Fx.fract / 65536.0;
+ double y = Fy.value + Fy.fract / 65536.0;
+ cairo_matrix_transform_point (matrix, &x, &y);
+ *fx = _cairo_fixed_from_double (x);
+ *fy = _cairo_fixed_from_double (y);
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } };
+ cairo_status_t status;
+ GLYPHMETRICS metrics;
+ HDC hdc;
+ DWORD bytesGlyph;
+ unsigned char *buffer, *ptr;
+ cairo_path_fixed_t *path;
+ cairo_matrix_t transform;
+ cairo_fixed_t x, y;
+
+ if (scaled_font->is_bitmap)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ path = _cairo_path_fixed_create ();
+ if (!path)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) {
+ status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc);
+ transform = scaled_font->base.scale;
+ cairo_matrix_scale (&transform, 1.0/scaled_font->em_square, 1.0/scaled_font->em_square);
+ } else {
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+ cairo_matrix_init_identity(&transform);
+ }
+ if (status)
+ goto CLEANUP_PATH;
+
+ bytesGlyph = GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
+ GGO_NATIVE | GGO_GLYPH_INDEX,
+ &metrics, 0, NULL, &matrix);
+
+ if (bytesGlyph == GDI_ERROR) {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path");
+ goto CLEANUP_FONT;
+ }
+
+ ptr = buffer = 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_load_truetype_table,
+ _cairo_win32_scaled_font_index_to_ucs4,
+ _cairo_win32_scaled_font_is_synthetic,
+ _cairo_win32_scaled_font_index_to_glyph_name,
+ _cairo_win32_scaled_font_load_type1_data
+};
+
+/* #cairo_win32_font_face_t */
+
+typedef struct _cairo_win32_font_face cairo_win32_font_face_t;
+
+/* If hfont is non-%NULL then logfont->lfHeight must be -S for some S,
+ * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must
+ * all be 0, and hfont is the result of calling CreateFontIndirectW on
+ * logfont.
+ */
+struct _cairo_win32_font_face {
+ cairo_font_face_t base;
+ LOGFONTW logfont;
+ HFONT hfont;
+};
+
+/* implement the platform-specific interface */
+
+static cairo_bool_t
+_is_scale (const cairo_matrix_t *matrix, double scale)
+{
+ return matrix->xx == scale && matrix->yy == scale &&
+ matrix->xy == 0. && matrix->yx == 0. &&
+ matrix->x0 == 0. && matrix->y0 == 0.;
+}
+
+static cairo_status_t
+_cairo_win32_font_face_scaled_font_create (void *abstract_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **font)
+{
+ HFONT hfont = NULL;
+
+ cairo_win32_font_face_t *font_face = abstract_face;
+
+ if (font_face->hfont) {
+ /* Check whether it's OK to go ahead and use the font-face's HFONT. */
+ if (_is_scale (ctm, 1.) &&
+ _is_scale (font_matrix, -font_face->logfont.lfHeight)) {
+ hfont = font_face->hfont;
+ }
+ }
+
+ return _win32_scaled_font_create (&font_face->logfont,
+ hfont,
+ &font_face->base,
+ font_matrix, ctm, options,
+ font);
+}
+
+const cairo_font_face_backend_t _cairo_win32_font_face_backend = {
+ CAIRO_FONT_TYPE_WIN32,
+ _cairo_win32_font_face_create_for_toy,
+ _cairo_win32_font_face_destroy,
+ _cairo_win32_font_face_scaled_font_create
+};
+
+/* 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_destroy (void *abstract_face)
+{
+ cairo_win32_font_face_t *font_face = abstract_face;
+ cairo_hash_table_t *hash_table;
+
+ hash_table = _cairo_win32_font_face_hash_table_lock ();
+ /* All created objects must have been mapped in the hash table. */
+ assert (hash_table != NULL);
+
+ if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) {
+ /* somebody recreated the font whilst we waited for the lock */
+ _cairo_win32_font_face_hash_table_unlock ();
+ return;
+ }
+
+ /* Font faces in SUCCESS status are guaranteed to be in the
+ * hashtable. Font faces in an error status are removed from the
+ * hashtable if they are found during a lookup, thus they should
+ * only be removed if they are in the hashtable. */
+ if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) ||
+ _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face)
+ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+
+ _cairo_win32_font_face_hash_table_unlock ();
+}
+
+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;
+}
+
+/**
+ * cairo_win32_font_face_create_for_logfontw_hfont:
+ * @logfont: A #LOGFONTW structure specifying the font to use.
+ * If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement
+ * fields of this structure are ignored. Otherwise lfWidth, lfOrientation and
+ * lfEscapement must be zero.
+ * @font: An #HFONT that can be used when the font matrix is a scale by
+ * -lfHeight and the CTM is identity.
+ *
+ * Creates a new font for the Win32 font backend based on a
+ * #LOGFONT. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ * The #cairo_scaled_font_t
+ * returned from cairo_scaled_font_create() is also for the Win32 backend
+ * and can be used with functions such as cairo_win32_scaled_font_select_font().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.6
+ **/
+cairo_font_face_t *
+cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font)
+{
+ cairo_win32_font_face_t *font_face, key;
+ cairo_hash_table_t *hash_table;
+ cairo_status_t status;
+
+ hash_table = _cairo_win32_font_face_hash_table_lock ();
+ if (unlikely (hash_table == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+ }
+
+ _cairo_win32_font_face_init_key (&key, logfont, font);
+
+ /* Return existing unscaled font if it exists in the hash table. */
+ font_face = _cairo_hash_table_lookup (hash_table,
+ &key.base.hash_entry);
+ if (font_face != NULL) {
+ if (font_face->base.status == CAIRO_STATUS_SUCCESS) {
+ cairo_font_face_reference (&font_face->base);
+ _cairo_win32_font_face_hash_table_unlock ();
+ return &font_face->base;
+ }
+
+ /* remove the bad font from the hash table */
+ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+ }
+
+ /* Otherwise create it and insert into hash table. */
+ font_face = malloc (sizeof (cairo_win32_font_face_t));
+ if (!font_face) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+
+ _cairo_win32_font_face_init_key (font_face, logfont, font);
+ _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend);
+
+ assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
+ status = _cairo_hash_table_insert (hash_table,
+ &font_face->base.hash_entry);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_win32_font_face_hash_table_unlock ();
+ return &font_face->base;
+
+FAIL:
+ _cairo_win32_font_face_hash_table_unlock ();
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+}
+
+/**
+ * cairo_win32_font_face_create_for_logfontw:
+ * @logfont: A #LOGFONTW structure specifying the font to use.
+ * The lfHeight, lfWidth, lfOrientation and lfEscapement
+ * fields of this structure are ignored.
+ *
+ * Creates a new font for the Win32 font backend based on a
+ * #LOGFONT. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ * The #cairo_scaled_font_t
+ * returned from cairo_scaled_font_create() is also for the Win32 backend
+ * and can be used with functions such as cairo_win32_scaled_font_select_font().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.0
+ **/
+cairo_font_face_t *
+cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont)
+{
+ return cairo_win32_font_face_create_for_logfontw_hfont (logfont, NULL);
+}
+
+/**
+ * cairo_win32_font_face_create_for_hfont:
+ * @font: An #HFONT structure specifying the font to use.
+ *
+ * Creates a new font for the Win32 font backend based on a
+ * #HFONT. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ * The #cairo_scaled_font_t
+ * returned from cairo_scaled_font_create() is also for the Win32 backend
+ * and can be used with functions such as cairo_win32_scaled_font_select_font().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.2
+ **/
+cairo_font_face_t *
+cairo_win32_font_face_create_for_hfont (HFONT font)
+{
+ LOGFONTW logfont;
+ GetObjectW (font, sizeof(logfont), &logfont);
+
+ if (logfont.lfEscapement != 0 || logfont.lfOrientation != 0 ||
+ logfont.lfWidth != 0) {
+ /* We can't use this font because that optimization requires that
+ * lfEscapement, lfOrientation and lfWidth be zero. */
+ font = NULL;
+ }
+
+ return cairo_win32_font_face_create_for_logfontw_hfont (&logfont, font);
+}
+
+static cairo_bool_t
+_cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font)
+{
+ return scaled_font->backend == &_cairo_win32_scaled_font_backend;
+}
+
+/**
+ * cairo_win32_scaled_font_select_font:
+ * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an
+ * object can be created with cairo_win32_font_face_create_for_logfontw().
+ * @hdc: a device context
+ *
+ * Selects the font into the given device context and changes the
+ * map mode and world transformation of the device context to match
+ * that of the font. This function is intended for use when using
+ * layout APIs such as Uniscribe to do text layout with the
+ * cairo font. After finishing using the device context, you must call
+ * cairo_win32_scaled_font_done_font() to release any resources allocated
+ * by this function.
+ *
+ * See cairo_win32_scaled_font_get_metrics_factor() for converting logical
+ * coordinates from the device context to font space.
+ *
+ * Normally, calls to SaveDC() and RestoreDC() would be made around
+ * the use of this function to preserve the original graphics state.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded.
+ * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and
+ * the device context is unchanged.
+ *
+ * Since: 1.0
+ **/
+cairo_status_t
+cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font,
+ HDC hdc)
+{
+ cairo_status_t status;
+ HFONT hfont;
+ HFONT old_hfont = NULL;
+ int old_mode;
+
+ if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+ return _cairo_error (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+ }
+
+ if (scaled_font->status)
+ return scaled_font->status;
+
+ status = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, &hfont);
+ if (status)
+ return status;
+
+ old_hfont = SelectObject (hdc, hfont);
+ if (!old_hfont)
+ return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject");
+
+ old_mode = SetGraphicsMode (hdc, GM_ADVANCED);
+ if (!old_mode) {
+ status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode");
+ SelectObject (hdc, old_hfont);
+ return status;
+ }
+
+ status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc);
+ if (status) {
+ SetGraphicsMode (hdc, old_mode);
+ SelectObject (hdc, old_hfont);
+ return status;
+ }
+
+ SetMapMode (hdc, MM_TEXT);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_win32_scaled_font_done_font:
+ * @scaled_font: A scaled font from the Win32 font backend.
+ *
+ * Releases any resources allocated by cairo_win32_scaled_font_select_font()
+ *
+ * Since: 1.0
+ **/
+void
+cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font)
+{
+ if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+ _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+ }
+}
+
+/**
+ * cairo_win32_scaled_font_get_metrics_factor:
+ * @scaled_font: a scaled font from the Win32 font backend
+ *
+ * Gets a scale factor between logical coordinates in the coordinate
+ * space used by cairo_win32_scaled_font_select_font() (that is, the
+ * coordinate system used by the Windows functions to return metrics) and
+ * font space coordinates.
+ *
+ * Return value: factor to multiply logical units by to get font space
+ * coordinates.
+ *
+ * Since: 1.0
+ **/
+double
+cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font)
+{
+ if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+ _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+ return 1.;
+ }
+ return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale;
+}
+
+/**
+ * cairo_win32_scaled_font_get_logical_to_device:
+ * @scaled_font: a scaled font from the Win32 font backend
+ * @logical_to_device: matrix to return
+ *
+ * Gets the transformation mapping the logical space used by @scaled_font
+ * to device space.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font,
+ cairo_matrix_t *logical_to_device)
+{
+ cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font;
+ if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+ _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+ cairo_matrix_init_identity (logical_to_device);
+ return;
+ }
+ *logical_to_device = win_font->logical_to_device;
+}
+
+/**
+ * cairo_win32_scaled_font_get_device_to_logical:
+ * @scaled_font: a scaled font from the Win32 font backend
+ * @device_to_logical: matrix to return
+ *
+ * Gets the transformation mapping device space to the logical space
+ * used by @scaled_font.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font,
+ cairo_matrix_t *device_to_logical)
+{
+ cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font;
+ if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+ _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+ cairo_matrix_init_identity (device_to_logical);
+ return;
+ }
+ *device_to_logical = win_font->device_to_logical;
+}
+
+void
+_cairo_win32_font_reset_static_data (void)
+{
+ _cairo_win32_font_face_hash_table_destroy ();
+}
diff --git a/src/win32/cairo-win32-gdi-compositor.c b/src/win32/cairo-win32-gdi-compositor.c
new file mode 100755
index 000000000..c70b0f90a
--- /dev/null
+++ b/src/win32/cairo-win32-gdi-compositor.c
@@ -0,0 +1,649 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Behdad Esfahbod <behdad@behdad.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+/* The original X drawing API was very restrictive in what it could handle,
+ * pixel-aligned fill/blits are all that map into Cairo's drawing model.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-win32-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-inline.h"
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-inline.h"
+#include "cairo-surface-offset-private.h"
+
+#if !defined(AC_SRC_OVER)
+#define AC_SRC_OVER 0x00
+#pragma pack(1)
+typedef struct {
+ BYTE BlendOp;
+ BYTE BlendFlags;
+ BYTE SourceConstantAlpha;
+ BYTE AlphaFormat;
+}BLENDFUNCTION;
+#pragma pack()
+#endif
+
+/* for compatibility with VC++ 6 */
+#ifndef AC_SRC_ALPHA
+#define AC_SRC_ALPHA 0x01
+#endif
+
+#define PELS_72DPI ((LONG)(72. / 0.0254))
+
+/* the low-level interface */
+
+struct fill_box {
+ HDC dc;
+ HBRUSH brush;
+};
+
+static cairo_bool_t fill_box (cairo_box_t *box, void *closure)
+{
+ struct fill_box *fb = closure;
+ RECT rect;
+
+ rect.left = _cairo_fixed_integer_part (box->p1.x);
+ rect.top = _cairo_fixed_integer_part (box->p1.y);
+ rect.right = _cairo_fixed_integer_part (box->p2.x);
+ rect.bottom = _cairo_fixed_integer_part (box->p2.y);
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return FillRect (fb->dc, &rect, fb->brush);
+}
+
+struct check_box {
+ cairo_rectangle_int_t limit;
+ int tx, ty;
+};
+
+struct copy_box {
+ cairo_rectangle_int_t limit;
+ int tx, ty;
+ HDC dst, src;
+ BLENDFUNCTION bf;
+ cairo_win32_alpha_blend_func_t alpha_blend;
+};
+
+static cairo_bool_t copy_box (cairo_box_t *box, void *closure)
+{
+ const struct copy_box *cb = closure;
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return BitBlt (cb->dst, x, y, width, height,
+ cb->src, x + cb->tx, y + cb->ty,
+ SRCCOPY);
+}
+
+static cairo_bool_t alpha_box (cairo_box_t *box, void *closure)
+{
+ const struct copy_box *cb = closure;
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return cb->alpha_blend (cb->dst, x, y, width, height,
+ cb->src, x + cb->tx, y + cb->ty, width, height,
+ cb->bf);
+}
+
+struct upload_box {
+ cairo_rectangle_int_t limit;
+ int tx, ty;
+ HDC dst;
+ BITMAPINFO bi;
+ void *data;
+};
+
+static cairo_bool_t upload_box (cairo_box_t *box, void *closure)
+{
+ const struct upload_box *cb = closure;
+ int x = _cairo_fixed_integer_part (box->p1.x);
+ int y = _cairo_fixed_integer_part (box->p1.y);
+ int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+ int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return StretchDIBits (cb->dst, x, y + height - 1, width, -height,
+ x + cb->tx, height - (y + cb->ty - 1),
+ width, -height,
+ cb->data, &cb->bi,
+ DIB_RGB_COLORS, SRCCOPY);
+}
+
+/* the mid-level: converts boxes into drawing operations */
+
+static COLORREF color_to_rgb(const cairo_color_t *c)
+{
+ return RGB (c->red_short >> 8, c->green_short >> 8, c->blue_short >> 8);
+}
+
+static cairo_int_status_t
+fill_boxes (cairo_win32_display_surface_t *dst,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes)
+{
+ const cairo_color_t *color = &((cairo_solid_pattern_t *) src)->color;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ struct fill_box fb;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ fb.dc = dst->win32.dc;
+ fb.brush = CreateSolidBrush (color_to_rgb(color));
+ if (!fb.brush)
+ return _cairo_win32_print_gdi_error (__FUNCTION__);
+
+ if (! _cairo_boxes_for_each_box (boxes, fill_box, &fb))
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ DeleteObject (fb.brush);
+
+ return status;
+}
+
+static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure)
+{
+ struct check_box *data = closure;
+
+ /* The box is pixel-aligned so the truncation is safe. */
+ return
+ _cairo_fixed_integer_part (box->p1.x) + data->tx >= data->limit.x &&
+ _cairo_fixed_integer_part (box->p1.y) + data->ty >= data->limit.y &&
+ _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->limit.x + data->limit.width &&
+ _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->limit.y + data->limit.height;
+}
+
+static cairo_status_t
+copy_boxes (cairo_win32_display_surface_t *dst,
+ const cairo_pattern_t *source,
+ cairo_boxes_t *boxes)
+{
+ const cairo_surface_pattern_t *pattern;
+ struct copy_box cb;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ surface = _cairo_surface_get_source (pattern->surface, &cb.limit);
+ if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ surface = to_image_surface(surface)->parent;
+ if (surface == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (surface->type != CAIRO_SURFACE_TYPE_WIN32)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix,
+ &cb.tx, &cb.ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cb.dst = dst->win32.dc;
+ cb.src = to_win32_surface(surface)->dc;
+
+ /* First check that the data is entirely within the image */
+ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = __cairo_surface_flush (surface, 0);
+ if (status)
+ return status;
+
+ cb.tx += cb.limit.x;
+ cb.ty += cb.limit.y;
+ status = CAIRO_STATUS_SUCCESS;
+ if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb))
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_win32_display_surface_discard_fallback (dst);
+ return status;
+}
+
+static cairo_status_t
+upload_boxes (cairo_win32_display_surface_t *dst,
+ const cairo_pattern_t *source,
+ cairo_boxes_t *boxes)
+{
+ const cairo_surface_pattern_t *pattern;
+ struct upload_box cb;
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix,
+ &cb.tx, &cb.ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ surface = _cairo_surface_get_source (pattern->surface, &cb.limit);
+
+ /* First check that the data is entirely within the image */
+ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->type != CAIRO_SURFACE_TYPE_IMAGE) {
+ status = _cairo_surface_acquire_source_image (surface,
+ &image, &image_extra);
+ if (status)
+ return status;
+ } else
+ image = to_image_surface(surface);
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (!(image->format == CAIRO_FORMAT_ARGB32 ||
+ image->format == CAIRO_FORMAT_RGB24))
+ goto err;
+ if (image->stride != 4*image->width)
+ goto err;
+
+ cb.dst = dst->win32.dc;
+ cb.data = image->data;
+
+ cb.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
+ cb.bi.bmiHeader.biWidth = image->width;
+ cb.bi.bmiHeader.biHeight = -image->height;
+ cb.bi.bmiHeader.biSizeImage = 0;
+ cb.bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+ cb.bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+ cb.bi.bmiHeader.biPlanes = 1;
+ cb.bi.bmiHeader.biBitCount = 32;
+ cb.bi.bmiHeader.biCompression = BI_RGB;
+ cb.bi.bmiHeader.biClrUsed = 0;
+ cb.bi.bmiHeader.biClrImportant = 0;
+
+ cb.tx += cb.limit.x;
+ cb.ty += cb.limit.y;
+ status = CAIRO_STATUS_SUCCESS;
+ if (! _cairo_boxes_for_each_box (boxes, upload_box, &cb))
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_win32_display_surface_discard_fallback (dst);
+err:
+ if (&image->base != surface)
+ _cairo_surface_release_source_image (surface, image, image_extra);
+
+ return status;
+}
+
+static cairo_status_t
+alpha_blend_boxes (cairo_win32_display_surface_t *dst,
+ const cairo_pattern_t *source,
+ cairo_boxes_t *boxes,
+ uint8_t alpha)
+{
+ const cairo_surface_pattern_t *pattern;
+ struct copy_box cb;
+ cairo_surface_t *surface;
+ cairo_win32_display_surface_t *src;
+ cairo_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ pattern = (const cairo_surface_pattern_t *) source;
+ surface = _cairo_surface_get_source (pattern->surface, &cb.limit);
+ if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ surface = to_image_surface(surface)->parent;
+ if (surface == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (surface->type != CAIRO_SURFACE_TYPE_WIN32)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&source->matrix,
+ &cb.tx, &cb.ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ src = to_win32_display_surface (surface);
+ cb.dst = dst->win32.dc;
+ cb.src = src->win32.dc;
+
+ /* First check that the data is entirely within the image */
+ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = __cairo_surface_flush (&src->win32.base, 0);
+ if (status)
+ return status;
+
+ cb.bf.BlendOp = AC_SRC_OVER;
+ cb.bf.BlendFlags = 0;
+ cb.bf.SourceConstantAlpha = alpha;
+ cb.bf.AlphaFormat = (src->win32.format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0;
+ cb.alpha_blend = to_win32_device(dst->win32.base.device)->alpha_blend;
+
+ cb.tx += cb.limit.x;
+ cb.ty += cb.limit.y;
+ status = CAIRO_STATUS_SUCCESS;
+ if (! _cairo_boxes_for_each_box (boxes, alpha_box, &cb))
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_win32_display_surface_discard_fallback (dst);
+ return status;
+}
+
+static cairo_bool_t
+can_alpha_blend (cairo_win32_display_surface_t *dst)
+{
+ if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND) == 0)
+ return FALSE;
+
+ return to_win32_device(dst->win32.base.device)->alpha_blend != NULL;
+}
+
+static cairo_status_t
+draw_boxes (cairo_composite_rectangles_t *composite,
+ cairo_boxes_t *boxes)
+{
+ cairo_win32_display_surface_t *dst = to_win32_display_surface(composite->surface);
+ cairo_operator_t op = composite->op;
+ const cairo_pattern_t *src = &composite->source_pattern.base;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (boxes->num_boxes == 0 && composite->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (!boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_opaque (src, &composite->bounded))
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (dst->win32.base.is_clear &&
+ (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))
+ op = CAIRO_OPERATOR_SOURCE;
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (src->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ status = copy_boxes (dst, src, boxes);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ status = upload_boxes (dst, src, boxes);
+ } else if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
+ status = fill_boxes (dst, src, boxes);
+ }
+ return status;
+ }
+
+ if (op == CAIRO_OPERATOR_OVER && can_alpha_blend (dst))
+ return alpha_blend_boxes (dst, src, boxes, 255);
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+opacity_boxes (cairo_composite_rectangles_t *composite,
+ cairo_boxes_t *boxes)
+{
+ cairo_win32_display_surface_t *dst = to_win32_display_surface(composite->surface);
+ cairo_operator_t op = composite->op;
+ const cairo_pattern_t *src = &composite->source_pattern.base;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (boxes->num_boxes == 0 && composite->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (!boxes->is_pixel_aligned)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (op != CAIRO_OPERATOR_OVER)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (!can_alpha_blend (dst))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return alpha_blend_boxes (dst, src, boxes,
+ composite->mask_pattern.solid.color.alpha_short >> 8);
+}
+
+/* high-level compositor interface */
+
+static cairo_bool_t check_blit (cairo_composite_rectangles_t *composite)
+{
+ cairo_win32_display_surface_t *dst;
+
+ if (composite->clip->path)
+ return FALSE;
+
+ dst = to_win32_display_surface (composite->surface);
+ if (dst->fallback)
+ return FALSE;
+
+ if (dst->win32.format != CAIRO_FORMAT_RGB24)
+ return FALSE;
+
+ if (dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)
+ return TRUE;
+
+ return dst->image == NULL;
+}
+
+static cairo_int_status_t
+_cairo_win32_gdi_compositor_paint (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (check_blit (composite)) {
+ cairo_boxes_t boxes;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ _cairo_clip_steal_boxes (composite->clip, &boxes);
+ status = draw_boxes (composite, &boxes);
+ _cairo_clip_unsteal_boxes (composite->clip, &boxes);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_gdi_compositor_mask (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (check_blit (composite)) {
+ cairo_boxes_t boxes;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ _cairo_clip_steal_boxes (composite->clip, &boxes);
+ status = opacity_boxes (composite, &boxes);
+ _cairo_clip_unsteal_boxes (composite->clip, &boxes);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_gdi_compositor_stroke (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (check_blit (composite) &&
+ _cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ _cairo_boxes_init_with_clip (&boxes, composite->clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = draw_boxes (composite, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_gdi_compositor_fill (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t *composite,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (check_blit (composite) &&
+ _cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ _cairo_boxes_init_with_clip (&boxes, composite->clip);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = draw_boxes (composite, &boxes);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ return status;
+}
+
+static cairo_bool_t check_glyphs (cairo_composite_rectangles_t *composite,
+ cairo_scaled_font_t *scaled_font)
+{
+ if (! _cairo_clip_is_region (composite->clip))
+ return FALSE;
+
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32)
+ return FALSE;
+
+ if (! _cairo_pattern_is_opaque_solid (&composite->source_pattern.base))
+ return FALSE;
+
+ return (composite->op == CAIRO_OPERATOR_CLEAR ||
+ composite->op == CAIRO_OPERATOR_SOURCE ||
+ composite->op == CAIRO_OPERATOR_OVER);
+}
+
+static cairo_int_status_t
+_cairo_win32_gdi_compositor_glyphs (const cairo_compositor_t *compositor,
+ cairo_composite_rectangles_t*composite,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_bool_t overlap)
+{
+ cairo_int_status_t status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (check_blit (composite) && check_glyphs (composite, scaled_font)) {
+ cairo_win32_display_surface_t *dst = to_win32_display_surface (composite->surface);
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ status = _cairo_win32_display_surface_set_clip(dst, composite->clip);
+ if (status)
+ return status;
+
+ status = _cairo_win32_surface_emit_glyphs (&dst->win32,
+ &composite->source_pattern.base,
+ glyphs,
+ num_glyphs,
+ scaled_font,
+ TRUE);
+
+ _cairo_win32_display_surface_unset_clip (dst);
+ }
+
+ return status;
+}
+
+const cairo_compositor_t *
+_cairo_win32_gdi_compositor_get (void)
+{
+ static cairo_compositor_t compositor;
+
+ if (compositor.delegate == NULL) {
+ compositor.delegate = &_cairo_fallback_compositor;
+
+ compositor.paint = _cairo_win32_gdi_compositor_paint;
+ compositor.mask = _cairo_win32_gdi_compositor_mask;
+ compositor.fill = _cairo_win32_gdi_compositor_fill;
+ compositor.stroke = _cairo_win32_gdi_compositor_stroke;
+ compositor.glyphs = _cairo_win32_gdi_compositor_glyphs;
+ }
+
+ return &compositor;
+}
diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c
new file mode 100755
index 000000000..be91445cf
--- /dev/null
+++ b/src/win32/cairo-win32-printing-surface.c
@@ -0,0 +1,1917 @@
+/* -*- 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-default-context-private.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-inline.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-image-info-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-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 char *_cairo_win32_printing_supported_mime_types[] =
+{
+ CAIRO_MIME_TYPE_JPEG,
+ CAIRO_MIME_TYPE_PNG,
+ NULL
+};
+
+static const cairo_surface_backend_t cairo_win32_printing_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend;
+
+static void
+_cairo_win32_printing_surface_init_ps_mode (cairo_win32_printing_surface_t *surface)
+{
+ DWORD word;
+ INT ps_feature, ps_level;
+
+ word = PSIDENT_GDICENTRIC;
+ if (ExtEscape (surface->win32.dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0)
+ return;
+
+ ps_feature = FEATURESETTING_PSLEVEL;
+ if (ExtEscape (surface->win32.dc, GET_PS_FEATURESETTING, sizeof(INT),
+ (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0)
+ return;
+
+ if (ps_level >= 3)
+ surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT;
+}
+
+static void
+_cairo_win32_printing_surface_init_image_support (cairo_win32_printing_surface_t *surface)
+{
+ DWORD word;
+
+ word = CHECKJPEGFORMAT;
+ if (ExtEscape(surface->win32.dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0)
+ surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG;
+
+ word = CHECKPNGFORMAT;
+ if (ExtEscape(surface->win32.dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0)
+ surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG;
+}
+
+/* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not
+ * work unless the GDI function GdiInitializeLanguagePack() has been
+ * called.
+ *
+ * http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html
+ *
+ * The only information I could find on the how to use this
+ * undocumented function is the use in:
+ *
+ * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup
+ *
+ * to solve the same problem. The above code first checks if LPK.DLL
+ * is already loaded. If it is not it calls
+ * GdiInitializeLanguagePack() using the prototype
+ * BOOL GdiInitializeLanguagePack (int)
+ * and argument 0.
+ */
+static void
+_cairo_win32_printing_surface_init_language_pack (cairo_win32_printing_surface_t *surface)
+{
+ typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int);
+ gdi_init_lang_pack_func_t gdi_init_lang_pack;
+ HMODULE module;
+
+ if (GetModuleHandleW (L"LPK.DLL"))
+ return;
+
+ module = GetModuleHandleW (L"GDI32.DLL");
+ if (module) {
+ gdi_init_lang_pack = (gdi_init_lang_pack_func_t)
+ GetProcAddress (module, "GdiInitializeLanguagePack");
+ if (gdi_init_lang_pack)
+ gdi_init_lang_pack (0);
+ }
+}
+
+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_printing_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->win32.flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT;
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_analyze_operation (cairo_win32_printing_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ 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_printing_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_printing_surface_t *surface,
+ cairo_solid_pattern_t *color)
+{
+ if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+ _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE);
+ else
+ _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK);
+}
+
+static COLORREF
+_cairo_win32_printing_surface_flatten_transparency (cairo_win32_printing_surface_t *surface,
+ const cairo_color_t *color)
+{
+ COLORREF c;
+ BYTE red, green, blue;
+
+ red = color->red_short >> 8;
+ green = color->green_short >> 8;
+ blue = color->blue_short >> 8;
+
+ if (!CAIRO_COLOR_IS_OPAQUE(color)) {
+ if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
+ /* Blend into white */
+ uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8);
+
+ red = (color->red_short >> 8) + one_minus_alpha;
+ green = (color->green_short >> 8) + one_minus_alpha;
+ blue = (color->blue_short >> 8) + one_minus_alpha;
+ } else {
+ /* Blend into black */
+ red = (color->red_short >> 8);
+ green = (color->green_short >> 8);
+ blue = (color->blue_short >> 8);
+ }
+ }
+ c = RGB (red, green, blue);
+
+ return c;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_select_solid_brush (cairo_win32_printing_surface_t *surface,
+ const cairo_pattern_t *source)
+{
+ cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source;
+ COLORREF color;
+
+ color = _cairo_win32_printing_surface_flatten_transparency (surface,
+ &pattern->color);
+ surface->brush = CreateSolidBrush (color);
+ if (!surface->brush)
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)");
+ surface->old_brush = SelectObject (surface->win32.dc, surface->brush);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_printing_surface_done_solid_brush (cairo_win32_printing_surface_t *surface)
+{
+ if (surface->old_brush) {
+ SelectObject (surface->win32.dc, surface->old_brush);
+ DeleteObject (surface->brush);
+ surface->old_brush = NULL;
+ }
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_printing_surface_t *surface,
+ RECT *clip)
+{
+ XFORM xform;
+
+ _cairo_matrix_to_win32_xform (&surface->ctm, &xform);
+ if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform");
+ GetClipBox (surface->win32.dc, clip);
+
+ _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform);
+ if (!SetWorldTransform (surface->win32.dc, &xform))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_printing_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ RECT clip;
+ cairo_status_t status;
+
+ GetClipBox (surface->win32.dc, &clip);
+ status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern);
+ if (status)
+ return status;
+
+ FillRect (surface->win32.dc, &clip, surface->brush);
+ _cairo_win32_printing_surface_done_solid_brush (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_printing_surface_t *surface,
+ cairo_surface_pattern_t *pattern)
+{
+ cairo_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->win32.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->win32.dc);
+ m = p2d;
+ cairo_matrix_translate (&m,
+ x_tile*recording_extents.width,
+ y_tile*recording_extents.height);
+ if (extend == CAIRO_EXTEND_REFLECT) {
+ if (x_tile % 2) {
+ cairo_matrix_translate (&m, recording_extents.width, 0);
+ cairo_matrix_scale (&m, -1, 1);
+ }
+ if (y_tile % 2) {
+ cairo_matrix_translate (&m, 0, recording_extents.height);
+ cairo_matrix_scale (&m, 1, -1);
+ }
+ }
+ surface->ctm = m;
+ surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
+
+ /* Set clip path around bbox of the pattern. */
+ BeginPath (surface->win32.dc);
+
+ x = 0;
+ y = 0;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ MoveToEx (surface->win32.dc, (int) x, (int) y, NULL);
+
+ x = recording_extents.width;
+ y = 0;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ LineTo (surface->win32.dc, (int) x, (int) y);
+
+ x = recording_extents.width;
+ y = recording_extents.height;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ LineTo (surface->win32.dc, (int) x, (int) y);
+
+ x = 0;
+ y = recording_extents.height;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ LineTo (surface->win32.dc, (int) x, (int) y);
+
+ CloseFigure (surface->win32.dc);
+ EndPath (surface->win32.dc);
+ SelectClipPath (surface->win32.dc, RGN_AND);
+
+ SaveDC (surface->win32.dc); /* Allow clip path to be reset during replay */
+ status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL,
+ &surface->win32.base,
+ CAIRO_RECORDING_REGION_NATIVE);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ /* Restore both the clip save and our earlier path SaveDC */
+ RestoreDC (surface->win32.dc, -2);
+
+ if (status)
+ return status;
+ }
+ }
+
+ surface->content = old_content;
+ surface->ctm = old_ctm;
+ surface->has_ctm = old_has_ctm;
+ RestoreDC (surface->win32.dc, -1);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_check_jpeg (cairo_win32_printing_surface_t *surface,
+ cairo_surface_t *source,
+ const unsigned char **data,
+ unsigned long *length,
+ cairo_image_info_t *info)
+{
+ const unsigned char *mime_data;
+ unsigned long mime_data_length;
+ cairo_int_status_t status;
+ DWORD result;
+
+ if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+ &mime_data, &mime_data_length);
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length);
+ if (status)
+ return status;
+
+ result = 0;
+ if (ExtEscape(surface->win32.dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data,
+ sizeof(result), (char *) &result) <= 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (result != 1)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ *data = mime_data;
+ *length = mime_data_length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_check_png (cairo_win32_printing_surface_t *surface,
+ cairo_surface_t *source,
+ const unsigned char **data,
+ unsigned long *length,
+ cairo_image_info_t *info)
+{
+ const unsigned char *mime_data;
+ unsigned long mime_data_length;
+
+ cairo_int_status_t status;
+ DWORD result;
+
+ if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG,
+ &mime_data, &mime_data_length);
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length);
+ if (status)
+ return status;
+
+ result = 0;
+ if (ExtEscape(surface->win32.dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data,
+ sizeof(result), (char *) &result) <= 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (result != 1)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ *data = mime_data;
+ *length = mime_data_length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_printing_surface_t *surface,
+ 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;
+
+ /* If we can't use StretchDIBits with this surface, we can't do anything
+ * here.
+ */
+ if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+ background_color = CAIRO_COLOR_WHITE;
+ else
+ background_color = CAIRO_COLOR_BLACK;
+
+ 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);
+
+ if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
+ cairo_surface_t *opaque_surface;
+ cairo_surface_pattern_t image_pattern;
+ cairo_solid_pattern_t background_pattern;
+
+ opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ image->width,
+ image->height);
+ if (opaque_surface->status) {
+ status = opaque_surface->status;
+ goto CLEANUP_OPAQUE_IMAGE;
+ }
+
+ _cairo_pattern_init_solid (&background_pattern,
+ background_color);
+ status = _cairo_surface_paint (opaque_surface,
+ CAIRO_OPERATOR_SOURCE,
+ &background_pattern.base,
+ NULL);
+ if (status)
+ goto CLEANUP_OPAQUE_IMAGE;
+
+ _cairo_pattern_init_for_surface (&image_pattern, &image->base);
+ status = _cairo_surface_paint (opaque_surface,
+ CAIRO_OPERATOR_OVER,
+ &image_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&image_pattern.base);
+ if (status)
+ goto CLEANUP_OPAQUE_IMAGE;
+
+ opaque_image = (cairo_image_surface_t *) opaque_surface;
+ } else {
+ opaque_image = image;
+ }
+
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width;
+ bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height;
+ bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0;
+ bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+ bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biBitCount = 32;
+ bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB;
+ bi.bmiHeader.biClrUsed = 0;
+ bi.bmiHeader.biClrImportant = 0;
+
+ m = 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);
+ SaveDC (surface->win32.dc);
+ _cairo_matrix_to_win32_xform (&m, &xform);
+
+ if (! SetWorldTransform (surface->win32.dc, &xform)) {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
+ goto CLEANUP_OPAQUE_IMAGE;
+ }
+
+ oldmode = SetStretchBltMode(surface->win32.dc, HALFTONE);
+
+ GetClipBox (surface->win32.dc, &clip);
+ if (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->win32.dc,
+ x_tile*opaque_image->width,
+ y_tile*opaque_image->height,
+ opaque_image->width,
+ opaque_image->height,
+ 0,
+ 0,
+ use_mime ? mime_info.width : opaque_image->width,
+ use_mime ? mime_info.height : opaque_image->height,
+ use_mime ? mime_data : opaque_image->data,
+ &bi,
+ DIB_RGB_COLORS,
+ SRCCOPY))
+ {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)");
+ goto CLEANUP_OPAQUE_IMAGE;
+ }
+ }
+ }
+ SetStretchBltMode(surface->win32.dc, oldmode);
+ RestoreDC (surface->win32.dc, -1);
+
+CLEANUP_OPAQUE_IMAGE:
+ if (opaque_image != image)
+ cairo_surface_destroy (&opaque_image->base);
+CLEANUP_IMAGE:
+ _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_printing_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_printing_surface_t *surface,
+ cairo_linear_pattern_t *pattern)
+{
+ TRIVERTEX *vert;
+ GRADIENT_RECT *rect;
+ RECT clip;
+ XFORM xform;
+ int i, num_stops;
+ cairo_matrix_t mat, rot;
+ double p1x, p1y, p2x, p2y, xd, yd, d, sn, cs;
+ cairo_extend_t extend;
+ int range_start, range_stop, num_ranges, num_rects, stop;
+ int total_verts, total_rects;
+ cairo_status_t status;
+
+ extend = cairo_pattern_get_extend (&pattern->base.base);
+ SaveDC (surface->win32.dc);
+
+ mat = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&mat);
+ /* _cairo_pattern_set_matrix guarantees invertibility */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&mat, &surface->ctm, &mat);
+
+ p1x = pattern->pd1.x;
+ p1y = pattern->pd1.y;
+ p2x = pattern->pd2.x;
+ p2y = pattern->pd2.y;
+ cairo_matrix_translate (&mat, p1x, p1y);
+
+ xd = p2x - p1x;
+ yd = p2y - p1y;
+ d = sqrt (xd*xd + yd*yd);
+ sn = yd/d;
+ cs = xd/d;
+ cairo_matrix_init (&rot,
+ cs, sn,
+ -sn, cs,
+ 0, 0);
+ cairo_matrix_multiply (&mat, &rot, &mat);
+
+ _cairo_matrix_to_win32_xform (&mat, &xform);
+
+ if (!SetWorldTransform (surface->win32.dc, &xform))
+ return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2");
+
+ GetClipBox (surface->win32.dc, &clip);
+
+ if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
+ range_start = floor (clip.left / d);
+ range_stop = ceil (clip.right / d);
+ } else {
+ range_start = 0;
+ range_stop = 1;
+ }
+ num_ranges = range_stop - range_start;
+ num_stops = pattern->base.n_stops;
+ num_rects = num_stops - 1;
+
+ /* Add an extra four points and two rectangles for EXTEND_PAD */
+ vert = 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->win32.dc,
+ vert, total_verts,
+ rect, total_rects,
+ GRADIENT_FILL_RECT_H))
+ return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill");
+
+ free (rect);
+ free (vert);
+ RestoreDC (surface->win32.dc, -1);
+
+ return 0;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_paint_pattern (cairo_win32_printing_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ 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;
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ ASSERT_NOT_REACHED;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _win32_print_path_info {
+ cairo_win32_printing_surface_t *surface;
+} win32_path_info_t;
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ win32_path_info_t *path_info = closure;
+
+ if (path_info->surface->has_ctm) {
+ double x, y;
+
+ x = _cairo_fixed_to_double (point->x);
+ y = _cairo_fixed_to_double (point->y);
+ cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+ MoveToEx (path_info->surface->win32.dc, (int) x, (int) y, NULL);
+ } else {
+ MoveToEx (path_info->surface->win32.dc,
+ _cairo_fixed_integer_part (point->x),
+ _cairo_fixed_integer_part (point->y),
+ NULL);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ win32_path_info_t *path_info = closure;
+
+ path_info->surface->path_empty = FALSE;
+ if (path_info->surface->has_ctm) {
+ double x, y;
+
+ x = _cairo_fixed_to_double (point->x);
+ y = _cairo_fixed_to_double (point->y);
+ cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+ LineTo (path_info->surface->win32.dc, (int) x, (int) y);
+ } else {
+ LineTo (path_info->surface->win32.dc,
+ _cairo_fixed_integer_part (point->x),
+ _cairo_fixed_integer_part (point->y));
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ win32_path_info_t *path_info = closure;
+ POINT points[3];
+
+ path_info->surface->path_empty = FALSE;
+ if (path_info->surface->has_ctm) {
+ double x, y;
+
+ x = _cairo_fixed_to_double (b->x);
+ y = _cairo_fixed_to_double (b->y);
+ cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+ points[0].x = (LONG) x;
+ points[0].y = (LONG) y;
+
+ x = _cairo_fixed_to_double (c->x);
+ y = _cairo_fixed_to_double (c->y);
+ cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+ points[1].x = (LONG) x;
+ points[1].y = (LONG) y;
+
+ x = _cairo_fixed_to_double (d->x);
+ y = _cairo_fixed_to_double (d->y);
+ cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+ points[2].x = (LONG) x;
+ points[2].y = (LONG) y;
+ } else {
+ points[0].x = _cairo_fixed_integer_part (b->x);
+ points[0].y = _cairo_fixed_integer_part (b->y);
+ points[1].x = _cairo_fixed_integer_part (c->x);
+ points[1].y = _cairo_fixed_integer_part (c->y);
+ points[2].x = _cairo_fixed_integer_part (d->x);
+ points[2].y = _cairo_fixed_integer_part (d->y);
+ }
+ PolyBezierTo (path_info->surface->win32.dc, points, 3);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_close_path (void *closure)
+{
+ win32_path_info_t *path_info = closure;
+
+ CloseFigure (path_info->surface->win32.dc);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_emit_path (cairo_win32_printing_surface_t *surface,
+ const cairo_path_fixed_t *path)
+{
+ win32_path_info_t path_info;
+
+ path_info.surface = surface;
+ return _cairo_path_fixed_interpret (path,
+ _cairo_win32_printing_surface_path_move_to,
+ _cairo_win32_printing_surface_path_line_to,
+ _cairo_win32_printing_surface_path_curve_to,
+ _cairo_win32_printing_surface_path_close_path,
+ &path_info);
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_show_page (void *abstract_surface)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+
+ /* Undo both SaveDC's that we did in start_page */
+ RestoreDC (surface->win32.dc, -2);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_win32_printing_surface_t *surface = cairo_container_of (clipper,
+ cairo_win32_printing_surface_t,
+ clipper);
+ cairo_status_t status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (path == NULL) {
+ RestoreDC (surface->win32.dc, -1);
+ SaveDC (surface->win32.dc);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ BeginPath (surface->win32.dc);
+ status = _cairo_win32_printing_surface_emit_path (surface, path);
+ EndPath (surface->win32.dc);
+
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ SetPolyFillMode (surface->win32.dc, WINDING);
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ SetPolyFillMode (surface->win32.dc, ALTERNATE);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ SelectClipPath (surface->win32.dc, RGN_AND);
+
+ return status;
+}
+
+static void
+_cairo_win32_printing_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
+ cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+ cairo_solid_pattern_t clear;
+ cairo_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,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ HPEN pen;
+ LOGBRUSH brush;
+ COLORREF color;
+ XFORM xform;
+ DWORD pen_style;
+ DWORD *dash_array;
+ HGDIOBJ obj;
+ unsigned int i;
+ cairo_solid_pattern_t clear;
+ cairo_matrix_t mat;
+ double scale;
+
+ 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++) {
+ dash_array[i] = (DWORD) (scale * style->dash[i]);
+ }
+ } else {
+ pen_style |= PS_SOLID;
+ }
+
+ SetMiterLimit (surface->win32.dc, (FLOAT) (style->miter_limit), NULL);
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+
+ color = _cairo_win32_printing_surface_flatten_transparency (surface,
+ &solid->color);
+ } else {
+ /* Color not used as the pen will only be used by WidenPath() */
+ color = RGB (0,0,0);
+ }
+ brush.lbStyle = BS_SOLID;
+ brush.lbColor = color;
+ brush.lbHatch = 0;
+ pen_style |= _cairo_win32_line_cap (style->line_cap);
+ pen_style |= _cairo_win32_line_join (style->line_join);
+ pen = ExtCreatePen(pen_style,
+ scale * style->line_width,
+ &brush,
+ style->num_dashes,
+ dash_array);
+ if (pen == NULL)
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen");
+ obj = SelectObject (surface->win32.dc, pen);
+ if (obj == NULL)
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject");
+
+ BeginPath (surface->win32.dc);
+ status = _cairo_win32_printing_surface_emit_path (surface, path);
+ EndPath (surface->win32.dc);
+ if (status)
+ return status;
+
+ /*
+ * Switch to user space to set line parameters
+ */
+ SaveDC (surface->win32.dc);
+
+ _cairo_matrix_to_win32_xform (&mat, &xform);
+ xform.eDx = 0.0f;
+ xform.eDy = 0.0f;
+
+ if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ StrokePath (surface->win32.dc);
+ } else {
+ if (!WidenPath (surface->win32.dc))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
+ if (!SelectClipPath (surface->win32.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->win32.dc, &xform))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform");
+ status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+ }
+ RestoreDC (surface->win32.dc, -1);
+ DeleteObject (pen);
+ 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,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ cairo_solid_pattern_t clear;
+
+ 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->win32.dc);
+ status = _cairo_win32_printing_surface_emit_path (surface, path);
+ EndPath (surface->win32.dc);
+
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ SetPolyFillMode (surface->win32.dc, WINDING);
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ SetPolyFillMode (surface->win32.dc, ALTERNATE);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ status = _cairo_win32_printing_surface_select_solid_brush (surface, source);
+ if (status)
+ return status;
+
+ FillPath (surface->win32.dc);
+ _cairo_win32_printing_surface_done_solid_brush (surface);
+ } else if (surface->path_empty == FALSE) {
+ SaveDC (surface->win32.dc);
+ SelectClipPath (surface->win32.dc, RGN_AND);
+ status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+ RestoreDC (surface->win32.dc, -1);
+ }
+
+ fflush(stderr);
+
+ return status;
+}
+
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_printing_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_matrix_t ctm;
+ cairo_glyph_t *unicode_glyphs;
+ cairo_scaled_font_subsets_glyph_t subset_glyph;
+ int i, first;
+ cairo_bool_t sequence_is_unicode;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ /* Where possible reverse the glyph indices back to unicode
+ * characters. Strings of glyphs that could not be reversed to
+ * unicode will be printed with ETO_GLYPH_INDEX.
+ *
+ * As _cairo_win32_scaled_font_index_to_ucs4() is a slow
+ * operation, the font subsetting function
+ * _cairo_scaled_font_subsets_map_glyph() is used to obtain
+ * the unicode value because it caches the reverse mapping in
+ * the subsets.
+ */
+
+ if (surface->has_ctm) {
+ for (i = 0; i < num_glyphs; i++)
+ cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y);
+ cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm);
+ scaled_font = cairo_scaled_font_create (scaled_font->font_face,
+ &scaled_font->font_matrix,
+ &ctm,
+ &scaled_font->options);
+ }
+
+ unicode_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unicode_glyphs == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (unicode_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
+ for (i = 0; i < num_glyphs; i++) {
+ status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets,
+ scaled_font,
+ glyphs[i].index,
+ NULL, 0,
+ &subset_glyph);
+ if (status)
+ goto fail;
+
+ unicode_glyphs[i].index = subset_glyph.unicode;
+ }
+
+ i = 0;
+ first = 0;
+ sequence_is_unicode = unicode_glyphs[0].index <= 0xffff;
+ while (i < num_glyphs) {
+ if (i == num_glyphs - 1 ||
+ ((unicode_glyphs[i + 1].index < 0xffff) != sequence_is_unicode))
+ {
+ status = _cairo_win32_surface_emit_glyphs (&surface->win32,
+ source,
+ sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first],
+ i - first + 1,
+ scaled_font,
+ ! sequence_is_unicode);
+ first = i + 1;
+ if (i < num_glyphs - 1)
+ sequence_is_unicode = unicode_glyphs[i + 1].index <= 0xffff;
+ }
+ i++;
+ }
+
+fail:
+ if (surface->has_ctm)
+ cairo_scaled_font_destroy (scaled_font);
+
+ free (unicode_glyphs);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_pattern_t *opaque = NULL;
+ int i;
+ cairo_matrix_t old_ctm;
+ cairo_bool_t old_has_ctm;
+ cairo_solid_pattern_t clear;
+
+ 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
+
+ /* 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_WIN32_FONT
+ if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 &&
+ source->type == CAIRO_PATTERN_TYPE_SOLID)
+ {
+ return _cairo_win32_printing_surface_emit_win32_glyphs (surface,
+ op,
+ source,
+ glyphs,
+ num_glyphs,
+ scaled_font,
+ clip);
+ }
+#endif
+
+ SaveDC (surface->win32.dc);
+ old_ctm = surface->ctm;
+ old_has_ctm = surface->has_ctm;
+ surface->has_ctm = TRUE;
+ surface->path_empty = TRUE;
+ BeginPath (surface->win32.dc);
+ for (i = 0; i < num_glyphs; i++) {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_PATH,
+ &scaled_glyph);
+ if (status)
+ break;
+ surface->ctm = old_ctm;
+ cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y);
+ status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path);
+ }
+ EndPath (surface->win32.dc);
+ 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)
+ return status;
+
+ SetPolyFillMode (surface->win32.dc, WINDING);
+ FillPath (surface->win32.dc);
+ _cairo_win32_printing_surface_done_solid_brush (surface);
+ } else {
+ SelectClipPath (surface->win32.dc, RGN_AND);
+ status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+ }
+ }
+ RestoreDC (surface->win32.dc, -1);
+
+ if (opaque)
+ cairo_pattern_destroy (opaque);
+
+ return status;
+}
+
+static const char **
+_cairo_win32_printing_surface_get_supported_mime_types (void *abstract_surface)
+{
+ return _cairo_win32_printing_supported_mime_types;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_finish (void *abstract_surface)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+
+ if (surface->font_subsets != NULL)
+ _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_win32_printing_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_rectangle_t extents;
+
+ extents.x = extents.y = 0;
+ extents.width = width;
+ extents.height = height;
+ return cairo_recording_surface_create (content, &extents);
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_start_page (void *abstract_surface)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+ XFORM xform;
+ double x_res, y_res;
+ cairo_matrix_t inverse_ctm;
+ cairo_status_t status;
+
+ SaveDC (surface->win32.dc); /* Save application context first, before doing MWT */
+
+ /* As the logical coordinates used by GDI functions (eg LineTo)
+ * are integers we need to do some additional work to prevent
+ * rounding errors. For example the obvious way to paint a recording
+ * pattern is to:
+ *
+ * SaveDC()
+ * transform the device context DC by the pattern to device matrix
+ * replay the recording surface
+ * RestoreDC()
+ *
+ * The problem here is that if the pattern to device matrix is
+ * [100 0 0 100 0 0], coordinates in the recording pattern such as
+ * (1.56, 2.23) which correspond to (156, 223) in device space
+ * will be rounded to (100, 200) due to (1.56, 2.23) being
+ * truncated to integers.
+ *
+ * This is solved by saving the current GDI CTM in surface->ctm,
+ * switch the GDI CTM to identity, and transforming all
+ * coordinates by surface->ctm before passing them to GDI. When
+ * painting a recording pattern, surface->ctm is transformed by the
+ * pattern to device matrix.
+ *
+ * For printing device contexts where 1 unit is 1 dpi, switching
+ * the GDI CTM to identity maximises the possible resolution of
+ * coordinates.
+ *
+ * If the device context is an EMF file, using an identity
+ * transform often provides 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->win32.dc, GM_ADVANCED);
+ GetWorldTransform(surface->win32.dc, &xform);
+ if (xform.eM11 < 1 && xform.eM22 < 1) {
+ cairo_matrix_init_identity (&surface->ctm);
+ surface->gdi_ctm.xx = xform.eM11;
+ surface->gdi_ctm.xy = xform.eM21;
+ surface->gdi_ctm.yx = xform.eM12;
+ surface->gdi_ctm.yy = xform.eM22;
+ surface->gdi_ctm.x0 = xform.eDx;
+ surface->gdi_ctm.y0 = xform.eDy;
+ } else {
+ surface->ctm.xx = xform.eM11;
+ surface->ctm.xy = xform.eM21;
+ surface->ctm.yx = xform.eM12;
+ surface->ctm.yy = xform.eM22;
+ surface->ctm.x0 = xform.eDx;
+ surface->ctm.y0 = xform.eDy;
+ cairo_matrix_init_identity (&surface->gdi_ctm);
+ if (!ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform");
+ }
+
+ surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
+ surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm);
+ inverse_ctm = surface->ctm;
+ status = cairo_matrix_invert (&inverse_ctm);
+ if (status)
+ return status;
+
+ x_res = GetDeviceCaps (surface->win32.dc, LOGPIXELSX);
+ y_res = GetDeviceCaps (surface->win32.dc, LOGPIXELSY);
+ cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res);
+ _cairo_surface_set_resolution (&surface->win32.base, x_res, y_res);
+
+ SaveDC (surface->win32.dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface,
+ cairo_paginated_mode_t paginated_mode)
+{
+ cairo_win32_printing_surface_t *surface = abstract_surface;
+
+ surface->paginated_mode = paginated_mode;
+}
+
+static cairo_bool_t
+_cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_surface)
+{
+ return TRUE;
+}
+
+/**
+ * cairo_win32_printing_surface_create:
+ * @hdc: the DC to create a surface for
+ *
+ * Creates a cairo surface that targets the given DC. The DC will be
+ * queried for its initial clip extents, and this will be used as the
+ * size of the cairo surface. The DC should be a printing DC;
+ * antialiasing will be ignored, and GDI will be used as much as
+ * possible to draw to the surface.
+ *
+ * The returned surface will be wrapped using the paginated surface to
+ * provide correct complex rendering behaviour; cairo_surface_show_page() and
+ * associated methods must be used for correct output.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.6
+ **/
+cairo_surface_t *
+cairo_win32_printing_surface_create (HDC hdc)
+{
+ cairo_win32_printing_surface_t *surface;
+ cairo_surface_t *paginated;
+ RECT rect;
+
+ surface = malloc (sizeof (cairo_win32_printing_surface_t));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+#if 0
+ if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+#endif
+
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_win32_printing_surface_clipper_intersect_clip_path);
+
+ surface->win32.format = CAIRO_FORMAT_RGB24;
+ surface->win32.base.content = CAIRO_CONTENT_COLOR_ALPHA;
+
+ surface->win32.dc = hdc;
+
+ 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->win32.extents.x = rect.left;
+ surface->win32.extents.y = rect.top;
+ surface->win32.extents.width = rect.right - rect.left;
+ surface->win32.extents.height = rect.bottom - rect.top;
+
+ surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc);
+ surface->win32.flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING;
+
+ _cairo_win32_printing_surface_init_ps_mode (surface);
+ _cairo_win32_printing_surface_init_image_support (surface);
+ _cairo_win32_printing_surface_init_language_pack (surface);
+ _cairo_surface_init (&surface->win32.base,
+ &cairo_win32_printing_surface_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ paginated = _cairo_paginated_surface_create (&surface->win32.base,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ &cairo_win32_surface_paginated_backend);
+
+ /* paginated keeps the only reference to surface now, drop ours */
+ cairo_surface_destroy (&surface->win32.base);
+
+ return paginated;
+}
+
+static const cairo_surface_backend_t cairo_win32_printing_surface_backend = {
+ CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+ _cairo_win32_printing_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_win32_printing_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* snapshot */
+
+ NULL, /* copy_page */
+ _cairo_win32_printing_surface_show_page,
+
+ _cairo_win32_surface_get_extents,
+ _cairo_win32_printing_surface_get_font_options,
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_win32_printing_surface_paint,
+ NULL, /* mask */
+ _cairo_win32_printing_surface_stroke,
+ _cairo_win32_printing_surface_fill,
+ NULL, /* fill/stroke */
+ _cairo_win32_printing_surface_show_glyphs,
+ NULL, /* has_show_text_glyphs */
+ NULL, /* show_text_glyphs */
+ _cairo_win32_printing_surface_get_supported_mime_types,
+};
+
+static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = {
+ _cairo_win32_printing_surface_start_page,
+ _cairo_win32_printing_surface_set_paginated_mode,
+ NULL, /* set_bounding_box */
+ NULL, /* _cairo_win32_printing_surface_has_fallback_images, */
+ _cairo_win32_printing_surface_supports_fine_grained_fallbacks,
+};
diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h
new file mode 100755
index 000000000..b6c24311a
--- /dev/null
+++ b/src/win32/cairo-win32-private.h
@@ -0,0 +1,226 @@
+/* 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-device-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-private.h"
+
+#ifndef SHADEBLENDCAPS
+#define SHADEBLENDCAPS 120
+#endif
+#ifndef SB_NONE
+#define SB_NONE 0
+#endif
+
+#define WIN32_FONT_LOGICAL_SCALE 32
+
+/* 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),
+};
+
+typedef struct _cairo_win32_surface {
+ cairo_surface_t base;
+
+ cairo_format_t format;
+ HDC dc;
+
+ /* Surface DC flags */
+ unsigned flags;
+
+ /* We use the x and y parts of extents for situations where
+ * we're not supposed to draw to the entire surface.
+ * For example, during a paint event a program will get
+ * a DC that has been clipped to the dirty region.
+ * A cairo surface constructed for that DC will have extents
+ * that match bounds of the clipped region.
+ */
+ cairo_rectangle_int_t extents;
+} cairo_win32_surface_t;
+#define to_win32_surface(S) ((cairo_win32_surface_t *)(S))
+
+typedef struct _cairo_win32_display_surface {
+ cairo_win32_surface_t win32;
+
+ /* We create off-screen surfaces as DIBs or DDBs, based on what we created
+ * originally*/
+ HBITMAP bitmap;
+ cairo_bool_t is_dib;
+
+ /* Used to save the initial 1x1 monochrome bitmap for the DC to
+ * select back into the DC before deleting the DC and our
+ * bitmap. For Windows XP, this doesn't seem to be necessary
+ * ... we can just delete the DC and that automatically unselects
+ * 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_surface_t *fallback;
+
+ HRGN initial_clip_rgn;
+ cairo_bool_t had_simple_clip;
+} cairo_win32_display_surface_t;
+#define to_win32_display_surface(S) ((cairo_win32_display_surface_t *)(S))
+
+typedef struct _cairo_win32_printing_surface {
+ cairo_win32_surface_t win32;
+
+ cairo_surface_clipper_t clipper;
+
+ cairo_paginated_mode_t paginated_mode;
+ cairo_content_t content;
+ cairo_bool_t path_empty;
+ cairo_bool_t has_ctm;
+ cairo_matrix_t ctm;
+ cairo_bool_t has_gdi_ctm;
+ cairo_matrix_t gdi_ctm;
+ HBRUSH brush, old_brush;
+ cairo_scaled_font_subsets_t *font_subsets;
+} cairo_win32_printing_surface_t;
+#define to_win32_printing_surface(S) ((cairo_win32_printing_surface_t *)(S))
+
+typedef BOOL (WINAPI *cairo_win32_alpha_blend_func_t) (HDC hdcDest,
+ int nXOriginDest,
+ int nYOriginDest,
+ int nWidthDest,
+ int hHeightDest,
+ HDC hdcSrc,
+ int nXOriginSrc,
+ int nYOriginSrc,
+ int nWidthSrc,
+ int nHeightSrc,
+ BLENDFUNCTION blendFunction);
+
+typedef struct _cairo_win32_device {
+ cairo_device_t base;
+
+ HMODULE msimg32_dll;
+
+ const cairo_compositor_t *compositor;
+
+ cairo_win32_alpha_blend_func_t alpha_blend;
+} cairo_win32_device_t;
+#define to_win32_device(D) ((cairo_win32_device_t *)(D))
+#define to_win32_device_from_surface(S) to_win32_device(((cairo_surface_t *)(S))->device)
+
+cairo_private cairo_device_t *
+_cairo_win32_device_get (void);
+
+const cairo_compositor_t *
+_cairo_win32_gdi_compositor_get (void);
+
+cairo_status_t
+_cairo_win32_print_gdi_error (const char *context);
+
+cairo_private void
+_cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface);
+
+cairo_bool_t
+_cairo_win32_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle);
+
+uint32_t
+_cairo_win32_flags_for_dc (HDC dc);
+
+cairo_int_status_t
+_cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_bool_t glyph_indexing);
+
+static inline void
+_cairo_matrix_to_win32_xform (const cairo_matrix_t *m,
+ XFORM *xform)
+{
+ xform->eM11 = (FLOAT) m->xx;
+ xform->eM21 = (FLOAT) m->xy;
+ xform->eM12 = (FLOAT) m->yx;
+ xform->eM22 = (FLOAT) m->yy;
+ xform->eDx = (FLOAT) m->x0;
+ xform->eDy = (FLOAT) m->y0;
+}
+
+cairo_status_t
+_cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface,
+ cairo_clip_t *clip);
+
+void
+_cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface);
+
+void
+_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header);
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font);
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font);
+
+#endif /* CAIRO_WIN32_PRIVATE_H */
diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c
new file mode 100755
index 000000000..7cd46fc60
--- /dev/null
+++ b/src/win32/cairo-win32-surface.c
@@ -0,0 +1,321 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Owen Taylor <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-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <wchar.h>
+#include <windows.h>
+
+#if defined(__MINGW32__) && !defined(ETO_PDY)
+# define ETO_PDY 0x2000
+#endif
+
+/**
+ * SECTION:cairo-win32
+ * @Title: Win32 Surfaces
+ * @Short_Description: Microsoft Windows surface support
+ * @See_Also: #cairo_surface_t
+ *
+ * The Microsoft Windows surface is used to render cairo graphics to
+ * Microsoft Windows windows, bitmaps, and printing device contexts.
+ *
+ * The surface returned by cairo_win32_printing_surface_create() is of surface
+ * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface
+ * type.
+ *
+ * The surface returned by the other win32 constructors is of surface type
+ * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type.
+ **/
+
+/**
+ * CAIRO_HAS_WIN32_SURFACE:
+ *
+ * Defined if the Microsoft Windows surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * Since: 1.0
+ **/
+
+/**
+ * _cairo_win32_print_gdi_error:
+ * @context: context string to display along with the error
+ *
+ * Helper function to dump out a human readable form of the
+ * current error code.
+ *
+ * Return value: A cairo status code for the error code
+ **/
+cairo_status_t
+_cairo_win32_print_gdi_error (const char *context)
+{
+ void *lpMsgBuf;
+ DWORD last_error = GetLastError ();
+
+ if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ last_error,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR) &lpMsgBuf,
+ 0, NULL)) {
+ fprintf (stderr, "%s: Unknown GDI error", context);
+ } else {
+ fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf);
+
+ LocalFree (lpMsgBuf);
+ }
+
+ fflush (stderr);
+
+ /* 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);
+}
+
+cairo_bool_t
+_cairo_win32_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ *rectangle = surface->extents;
+ return TRUE;
+}
+
+/**
+ * cairo_win32_surface_get_dc:
+ * @surface: a #cairo_surface_t
+ *
+ * Returns the HDC associated with this surface, or %NULL if none.
+ * Also returns %NULL if the surface is not a win32 surface.
+ *
+ * A call to cairo_surface_flush() is required before using the HDC to
+ * ensure that all pending drawing operations are finished and to
+ * restore any temporary modification cairo has made to its state. A
+ * call to cairo_surface_mark_dirty() is required after the state or
+ * the content of the HDC has been modified.
+ *
+ * Return value: HDC or %NULL if no HDC available.
+ *
+ * Since: 1.2
+ **/
+HDC
+cairo_win32_surface_get_dc (cairo_surface_t *surface)
+{
+ if (surface->backend->type == CAIRO_SURFACE_TYPE_WIN32)
+ return to_win32_surface(surface)->dc;
+
+ if (_cairo_surface_is_paginated (surface)) {
+ cairo_surface_t *target = _cairo_paginated_surface_get_target (surface);
+ if (target->backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING)
+ return to_win32_surface(target)->dc;
+ }
+
+ return NULL;
+}
+
+/**
+ * cairo_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 (surface->backend->type != CAIRO_SURFACE_TYPE_WIN32)
+ return NULL;
+
+ GdiFlush();
+ return to_win32_display_surface(surface)->image;
+}
+
+#define STACK_GLYPH_SIZE 256
+cairo_int_status_t
+_cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_bool_t glyph_indexing)
+{
+#if CAIRO_HAS_WIN32_FONT
+ WORD glyph_buf_stack[STACK_GLYPH_SIZE];
+ WORD *glyph_buf = glyph_buf_stack;
+ int dxy_buf_stack[2 * STACK_GLYPH_SIZE];
+ int *dxy_buf = dxy_buf_stack;
+
+ BOOL win_result = 0;
+ int i, j;
+
+ cairo_solid_pattern_t *solid_pattern;
+ COLORREF color;
+
+ cairo_matrix_t device_to_logical;
+
+ int start_x, start_y;
+ double user_x, user_y;
+ int logical_x, logical_y;
+ unsigned int glyph_index_option;
+
+ /* We can only handle win32 fonts */
+ assert (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32);
+
+ /* We can only handle opaque solid color sources and destinations */
+ assert (_cairo_pattern_is_opaque_solid(source));
+ assert (dst->format == CAIRO_FORMAT_RGB24);
+
+ solid_pattern = (cairo_solid_pattern_t *)source;
+ color = RGB(((int)solid_pattern->color.red_short) >> 8,
+ ((int)solid_pattern->color.green_short) >> 8,
+ ((int)solid_pattern->color.blue_short) >> 8);
+
+ cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical);
+
+ SaveDC(dst->dc);
+
+ cairo_win32_scaled_font_select_font(scaled_font, dst->dc);
+ SetTextColor(dst->dc, color);
+ SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT);
+ SetBkMode(dst->dc, TRANSPARENT);
+
+ if (num_glyphs > STACK_GLYPH_SIZE) {
+ glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD));
+ dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2);
+ }
+
+ /* It is vital that dx values for dxy_buf are calculated from the delta of
+ * _logical_ x coordinates (not user x coordinates) or else the sum of all
+ * previous dx values may start to diverge from the current glyph's x
+ * coordinate due to accumulated rounding error. As a result strings could
+ * be painted shorter or longer than expected. */
+
+ user_x = glyphs[0].x;
+ user_y = glyphs[0].y;
+
+ cairo_matrix_transform_point(&device_to_logical,
+ &user_x, &user_y);
+
+ logical_x = _cairo_lround (user_x);
+ logical_y = _cairo_lround (user_y);
+
+ start_x = logical_x;
+ start_y = logical_y;
+
+ for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) {
+ glyph_buf[i] = (WORD) glyphs[i].index;
+ if (i == num_glyphs - 1) {
+ dxy_buf[j] = 0;
+ dxy_buf[j+1] = 0;
+ } else {
+ double next_user_x = glyphs[i+1].x;
+ double next_user_y = glyphs[i+1].y;
+ int next_logical_x, next_logical_y;
+
+ cairo_matrix_transform_point(&device_to_logical,
+ &next_user_x, &next_user_y);
+
+ next_logical_x = _cairo_lround (next_user_x);
+ next_logical_y = _cairo_lround (next_user_y);
+
+ dxy_buf[j] = _cairo_lround (next_logical_x - logical_x);
+ dxy_buf[j+1] = _cairo_lround (next_logical_y - logical_y);
+
+ logical_x = next_logical_x;
+ logical_y = next_logical_y;
+ }
+ }
+
+ if (glyph_indexing)
+ glyph_index_option = ETO_GLYPH_INDEX;
+ else
+ glyph_index_option = 0;
+
+ win_result = ExtTextOutW(dst->dc,
+ start_x,
+ start_y,
+ glyph_index_option | ETO_PDY,
+ NULL,
+ glyph_buf,
+ num_glyphs,
+ dxy_buf);
+ if (!win_result) {
+ _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)");
+ }
+
+ RestoreDC(dst->dc, -1);
+
+ if (glyph_buf != glyph_buf_stack) {
+ free(glyph_buf);
+ free(dxy_buf);
+ }
+ return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED;
+#else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+#undef STACK_GLYPH_SIZE
diff --git a/src/win32/cairo-win32-system.c b/src/win32/cairo-win32-system.c
new file mode 100755
index 000000000..878553009
--- /dev/null
+++ b/src/win32/cairo-win32-system.c
@@ -0,0 +1,89 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Owen Taylor <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 <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